~james/core

core/web/src/lib/components/landing/Landing.svelte -rw-r--r-- 2.8 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 16 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<script lang="ts">
	import "#lib/components/landing/landing.css";
	import "#lib/components/landing/material.css";
	import { onMount } from "svelte";
	import Cta from "#lib/components/landing/cta/Cta.svelte";
	import EmbossFilter from "#lib/components/landing/EmbossFilter.svelte";
	import Features from "#lib/components/landing/features/Features.svelte";
	import { Guest, setGuest } from "#lib/components/landing/guest.svelte.ts";
	import Hackable from "#lib/components/landing/hackable/Hackable.svelte";
	import Hero from "#lib/components/landing/hero/Hero.svelte";
	import Footer from "#lib/components/landing/shell/Footer.svelte";
	import SelfHost from "#lib/components/landing/selfhost/SelfHost.svelte";
	import Navbar from "#lib/components/landing/shell/Navbar.svelte";
	import { setThread, Thread } from "#lib/components/landing/thread/thread.svelte.ts";
	import ThreadRail from "#lib/components/landing/thread/ThreadRail.svelte";
	import { loadSounds } from "#lib/components/landing/sound.ts";
	import type { Notification } from "#lib/components/landing/hero/notifications.ts";
	import RecentUpdates from "#lib/components/landing/updates/RecentUpdates.svelte";
	import type { Update } from "#lib/components/landing/updates/updates.ts";

	/*
	 * The landing page for signed-out visitors. One thread runs through it, from the hero's
	 * notification stack down the rail beside every section into the CTA's knot (thread/).
	 * Sections sit in rail containers, so their content keeps clear of the line.
	 */

	interface Props {
		/** The hero stack's live activity, from the public timeline. */
		activity?: Notification[] | null;
		updates?: Update[] | null;
	}

	// Each is null when its source couldn't be reached: the hero's stack shows a recorded
	// snapshot instead (hero/mock.ts) and Recent updates is left out.
	let { activity, updates }: Props = $props();

	const thread = setThread(new Thread());
	const guest = setGuest(new Guest());

	onMount(() => {
		void loadSounds();
		return () => guest.destroy();
	});

	const startAtTop = `<script>
		if (performance.getEntriesByType("navigation")[0]?.type === "reload") {
			history.scrollRestoration = "manual";
			history.replaceState(null, "");
		}
	</${"script"}>`;
</script>

<svelte:head>
	<!-- eslint-disable-next-line svelte/no-at-html-tags -- static -->
	{@html startAtTop}
</svelte:head>

<div class="landing" {@attach thread.mark("root")}>
	<!-- the hero's side grids, the full height of the page -->
	<div class="page-grid l" aria-hidden="true"></div>
	<div class="page-grid r" aria-hidden="true"></div>
	<EmbossFilter />
	<Navbar />
	<main>
		<Hero {activity} />
		<div class="body" {@attach thread.mark("body")}>
			<Features />
			<Hackable />
			<SelfHost />
			{#if updates?.length}
				<RecentUpdates {updates} />
			{/if}
		</div>
		<Cta />
	</main>
	<Footer />
	<ThreadRail />
</div>