From bce557cc2dc767628bed6aac87301a1be7c5431b Mon Sep 17 00:00:00 2001
From: rxliuli <rxliuli@gmail.com>
Date: Tue, 4 Nov 2025 05:03:50 +0800
Subject: init commit

---
 src/App.svelte | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 161 insertions(+)
 create mode 100644 src/App.svelte

(limited to 'src/App.svelte')

diff --git a/src/App.svelte b/src/App.svelte
new file mode 100644
index 0000000..846f1df
--- /dev/null
+++ b/src/App.svelte
@@ -0,0 +1,161 @@
+<script lang="ts">
+    import { onMount } from 'svelte';
+
+    import { BUILD } from '~/config/build';
+    import { getJet } from '~/jet';
+    import { makeErrorPageIntent } from '~/jet/intents/error-page-intent-controller';
+    import { getLocale } from '~/utils/locale';
+
+    // Types
+    import type { Page } from './jet/models/page';
+
+    // Components
+    import Fonts from '~/components/structure/Fonts.svelte';
+    import Footer from '~/components/structure/Footer.svelte';
+    import Navigation from '~/components/navigation/Navigation.svelte';
+    import NavigationSkeleton from '~/components/navigation/Skeleton.svelte';
+    import PageResolver from '~/components/PageResolver.svelte';
+
+    const locale = getLocale();
+    const jet = getJet();
+
+    $: language = locale.language;
+
+    export let page: Promise<Page> | Page = new Promise(() => {});
+    export let isFirstPage: boolean = true;
+
+    $: pageWithRejectionErrorPage = transformRejectionIntoErrorPage(page);
+
+    // Critically, this function is not async. We want to preserve the behavior
+    // where if page is not a promise than neither is
+    // pageWithRejectionErrorPage.
+    function transformRejectionIntoErrorPage(
+        page: Promise<Page> | Page,
+    ): Promise<Page> | Page {
+        if (!(page instanceof Promise)) {
+            return page;
+        }
+
+        // The async IIFE allows this function to return synchronously.
+        return (async (): Promise<Page> => {
+            try {
+                return await page;
+            } catch (error) {
+                return jet.dispatch(
+                    makeErrorPageIntent({
+                        // This allows the error page to pick the right platform
+                        // and display the correct mesage (ex. "Page not found" for
+                        // a 404)
+                        error: error instanceof Error ? error : null,
+                    }),
+                );
+            }
+        })();
+    }
+
+    // NOTE: The use of page instead of pageWithRejectionErrorPage here is very
+    //       intentional. Since pageWithRejectionErrorPage is reactive, it will
+    //       be undefined in this initializer. This is intentionally not
+    //       not derived (eg. defined as $: webNavigation = ...), since we only
+    //       want to update it _after_ the page promise resolves (so the nav
+    //       doesn't disappear on navigation). But then for SSR, there are no
+    //       promises, so we need a sync value here so the nav renders, which
+    //       is why we have the initializer.
+    let webNavigation = page instanceof Promise ? null : page.webNavigation;
+    $: {
+        if (pageWithRejectionErrorPage instanceof Promise) {
+            // Clientside once the new page resolves, update the navigation
+            // (in case it changed)
+            pageWithRejectionErrorPage.then((page: Page) => {
+                webNavigation = page.webNavigation;
+            });
+        } else {
+            // Sometimes clientside a promise is not passed to updateApp, so
+            // we need to handle a WebRenderablePage (possible with a
+            // different webNavigation).
+            webNavigation = pageWithRejectionErrorPage.webNavigation;
+        }
+    }
+
+    onMount(() => {
+        //@ts-ignore
+        window.__ASOTW = {
+            version: BUILD,
+        };
+    });
+</script>
+
+<svelte:head>
+    <meta name="version" content={BUILD} />
+</svelte:head>
+
+<Fonts {language} />
+
+{#if import.meta.env.DEV}
+    {#await import('~/components/ArtworkBreakpointLogger.svelte') then { default: ArtworkBreakpointLogger }}
+        <ArtworkBreakpointLogger />
+    {/await}
+{/if}
+
+<div class="app-container" data-testid="app-container">
+    <div class="navigation-container">
+        {#if webNavigation}
+            <Navigation {webNavigation} />
+        {:else}
+            <NavigationSkeleton />
+        {/if}
+    </div>
+
+    <div
+        style="display: flex;
+    position: relative;
+    flex-direction: column;
+        min-height: 100vh;
+    "
+    >
+        <main class="page-container">
+            <PageResolver page={pageWithRejectionErrorPage} {isFirstPage} />
+        </main>
+
+        <Footer />
+    </div>
+</div>
+
+<style lang="scss">
+    @use '@amp/web-shared-styles/sasskit-stylekit/ac-sasskit-config';
+    @use '@amp/web-shared-styles/app/core/viewports' as *;
+    @use '@amp/web-shared-styles/app/core/globalvars' as *;
+
+    .app-container {
+        min-height: 100vh;
+        min-height: 100dvh;
+        display: grid;
+        grid-template-areas:
+            'structure-header'
+            'structure-main-section';
+        grid-template-columns: minmax(0, 1fr);
+        grid-gap: 0;
+        grid-template-rows: 44px auto;
+
+        @media (--sidebar-visible) {
+            grid-template-rows: auto;
+            grid-template-columns: 260px minmax(0, 1fr);
+        }
+
+        @media (--sidebar-large-visible) {
+            grid-template-columns: $global-sidebar-width-large minmax(0, 1fr);
+        }
+    }
+
+    .navigation-container {
+        @media (--range-small-up) {
+            height: 100vh;
+            position: sticky;
+            top: 0;
+        }
+    }
+
+    .page-container {
+        flex-grow: 1;
+    }
+</style>
-- 
cgit v1.2.3

