Enhance routing and layout for site-specific pages. Added support for dynamic site routes, updated component imports, and improved layout styles for fullscreen display. Refactored navigation to utilize site data for dynamic button generation.
CI / build (push) Has been skipped
CI / deploy (push) Successful in 1m0s

This commit is contained in:
eewing
2026-02-18 15:33:46 -06:00
parent ec317eb17c
commit 1f14c6d422
19 changed files with 185 additions and 45 deletions
+7 -4
View File
@@ -5,15 +5,18 @@ export const nodes = [
() => import('./nodes/1'),
() => import('./nodes/2'),
() => import('./nodes/3'),
() => import('./nodes/4')
() => import('./nodes/4'),
() => import('./nodes/5'),
() => import('./nodes/6')
];
export const server_loads = [0];
export const dictionary = {
"/": [2],
"/login": [~4],
"/logout": [~3]
"/": [3],
"/login": [~6],
"/logout": [~4],
"/[site]": [~5,[2]]
};
export const hooks = {
+1 -1
View File
@@ -1 +1 @@
export { default as component } from "../../../../src/routes/+page.svelte";
export { default as component } from "../../../../src/routes/[site]/+layout.svelte";
+1 -1
View File
@@ -1 +1 @@
export { default as component } from "../../../../src/routes/logout/+page.svelte";
export { default as component } from "../../../../src/routes/+page.svelte";
+1 -1
View File
@@ -1 +1 @@
export { default as component } from "../../../../src/routes/login/+page.svelte";
export { default as component } from "../../../../src/routes/logout/+page.svelte";
+1
View File
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/[site]/+page.svelte";
+1
View File
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/login/+page.svelte";
+17 -5
View File
@@ -5,7 +5,7 @@
import { browser } from '$app/environment';
// stores
let { stores, page, constructors, components = [], form, data_0 = null, data_1 = null } = $props();
let { stores, page, constructors, components = [], form, data_0 = null, data_1 = null, data_2 = null } = $props();
if (!browser) {
// svelte-ignore state_referenced_locally
@@ -19,7 +19,7 @@
stores.page.set(page);
}
$effect(() => {
stores;page;constructors;components;form;data_0;data_1;
stores;page;constructors;components;form;data_0;data_1;data_2;
stores.page.notify();
});
@@ -41,15 +41,27 @@
return unsubscribe;
});
const Pyramid_1=$derived(constructors[1])
const Pyramid_2=$derived(constructors[2])
</script>
{#if constructors[1]}
{@const Pyramid_0 = constructors[0]}
<!-- svelte-ignore binding_property_non_reactive -->
<Pyramid_0 bind:this={components[0]} data={data_0} {form} params={page.params}>
<!-- svelte-ignore binding_property_non_reactive -->
<Pyramid_1 bind:this={components[1]} data={data_1} {form} params={page.params} />
{#if constructors[2]}
{@const Pyramid_1 = constructors[1]}
<!-- svelte-ignore binding_property_non_reactive -->
<Pyramid_1 bind:this={components[1]} data={data_1} {form} params={page.params}>
<!-- svelte-ignore binding_property_non_reactive -->
<Pyramid_2 bind:this={components[2]} data={data_2} {form} params={page.params} />
</Pyramid_1>
{:else}
{@const Pyramid_1 = constructors[1]}
<!-- svelte-ignore binding_property_non_reactive -->
<Pyramid_1 bind:this={components[1]} data={data_1} {form} params={page.params} />
{/if}
</Pyramid_0>
{:else}
+1 -1
View File
@@ -24,7 +24,7 @@ export const options = {
app: ({ head, body, assets, nonce, env }) => "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <link rel=\"icon\" href=\"" + assets + "/favicon.png\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=5, viewport-fit=cover\" />\n " + head + "\n </head>\n <body data-sveltekit-preload-data=\"hover\">\n <div style=\"display: contents\">" + body + "</div>\n </body>\n</html>\n",
error: ({ status, message }) => "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<title>" + message + "</title>\n\n\t\t<style>\n\t\t\tbody {\n\t\t\t\t--bg: white;\n\t\t\t\t--fg: #222;\n\t\t\t\t--divider: #ccc;\n\t\t\t\tbackground: var(--bg);\n\t\t\t\tcolor: var(--fg);\n\t\t\t\tfont-family:\n\t\t\t\t\tsystem-ui,\n\t\t\t\t\t-apple-system,\n\t\t\t\t\tBlinkMacSystemFont,\n\t\t\t\t\t'Segoe UI',\n\t\t\t\t\tRoboto,\n\t\t\t\t\tOxygen,\n\t\t\t\t\tUbuntu,\n\t\t\t\t\tCantarell,\n\t\t\t\t\t'Open Sans',\n\t\t\t\t\t'Helvetica Neue',\n\t\t\t\t\tsans-serif;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\theight: 100vh;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t.error {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tmax-width: 32rem;\n\t\t\t\tmargin: 0 1rem;\n\t\t\t}\n\n\t\t\t.status {\n\t\t\t\tfont-weight: 200;\n\t\t\t\tfont-size: 3rem;\n\t\t\t\tline-height: 1;\n\t\t\t\tposition: relative;\n\t\t\t\ttop: -0.05rem;\n\t\t\t}\n\n\t\t\t.message {\n\t\t\t\tborder-left: 1px solid var(--divider);\n\t\t\t\tpadding: 0 0 0 1rem;\n\t\t\t\tmargin: 0 0 0 1rem;\n\t\t\t\tmin-height: 2.5rem;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t}\n\n\t\t\t.message h1 {\n\t\t\t\tfont-weight: 400;\n\t\t\t\tfont-size: 1em;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t@media (prefers-color-scheme: dark) {\n\t\t\t\tbody {\n\t\t\t\t\t--bg: #222;\n\t\t\t\t\t--fg: #ddd;\n\t\t\t\t\t--divider: #666;\n\t\t\t\t}\n\t\t\t}\n\t\t</style>\n\t</head>\n\t<body>\n\t\t<div class=\"error\">\n\t\t\t<span class=\"status\">" + status + "</span>\n\t\t\t<div class=\"message\">\n\t\t\t\t<h1>" + message + "</h1>\n\t\t\t</div>\n\t\t</div>\n\t</body>\n</html>\n"
},
version_hash: "k1vc61"
version_hash: "1jm6xni"
};
export async function get_hooks() {
+6 -5
View File
@@ -27,18 +27,19 @@ export {};
declare module "$app/types" {
export interface AppTypes {
RouteId(): "/" | "/api" | "/api/darkmode" | "/login" | "/logout";
RouteId(): "/" | "/api" | "/api/darkmode" | "/login" | "/logout" | "/[site]";
RouteParams(): {
"/[site]": { site: string }
};
LayoutParams(): {
"/": Record<string, never>;
"/": { site?: string };
"/api": Record<string, never>;
"/api/darkmode": Record<string, never>;
"/login": Record<string, never>;
"/logout": Record<string, never>
"/logout": Record<string, never>;
"/[site]": { site: string }
};
Pathname(): "/" | "/api/darkmode" | "/login" | "/logout";
Pathname(): "/" | "/api/darkmode" | "/login" | "/logout" | `/${string}` & {};
ResolvedPathname(): `${"" | `/${string}`}${ReturnType<AppTypes['Pathname']>}`;
Asset(): string & {};
}
+5
View File
@@ -13,5 +13,10 @@
"/logout": [
"src/routes/logout/+page.server.ts",
"src/routes/+layout.server.ts"
],
"/[site]": [
"src/routes/[site]/+page.server.ts",
"src/routes/+layout.server.ts",
"src/routes/+layout.server.ts"
]
}
+2 -2
View File
@@ -12,8 +12,8 @@ type EnsureDefined<T> = T extends null | undefined ? {} : T;
type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;
export type Snapshot<T = any> = Kit.Snapshot<T>;
type PageParentData = EnsureDefined<LayoutData>;
type LayoutRouteId = RouteId | "/" | "/logout" | "/login" | null
type LayoutParams = RouteParams & { }
type LayoutRouteId = RouteId | "/" | "/logout" | "/[site]" | "/login" | null
type LayoutParams = RouteParams & { site?: string }
type LayoutServerParentData = EnsureDefined<{}>;
type LayoutParentData = EnsureDefined<{}>;
+32
View File
@@ -0,0 +1,32 @@
import type * as Kit from '@sveltejs/kit';
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
// @ts-ignore
type MatcherParam<M> = M extends (param : string) => param is infer U ? U extends string ? U : string : string;
type RouteParams = { site: string };
type RouteId = '/[site]';
type MaybeWithVoid<T> = {} extends T ? T | void : T;
export type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T];
type OutputDataShape<T> = MaybeWithVoid<Omit<App.PageData, RequiredKeys<T>> & Partial<Pick<App.PageData, keyof T & keyof App.PageData>> & Record<string, any>>
type EnsureDefined<T> = T extends null | undefined ? {} : T;
type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;
export type Snapshot<T = any> = Kit.Snapshot<T>;
type PageServerParentData = Omit<EnsureDefined<import('../$types.js').LayoutServerData>, keyof LayoutServerData> & EnsureDefined<LayoutServerData>;
type PageParentData = Omit<EnsureDefined<import('../$types.js').LayoutData>, keyof LayoutData> & EnsureDefined<LayoutData>;
type LayoutRouteId = RouteId | "/[site]"
type LayoutParams = RouteParams & { site?: string }
type LayoutParentData = EnsureDefined<import('../$types.js').LayoutData>;
export type EntryGenerator = () => Promise<Array<RouteParams>> | Array<RouteParams>;
export type PageServerLoad<OutputData extends OutputDataShape<PageServerParentData> = OutputDataShape<PageServerParentData>> = Kit.ServerLoad<RouteParams, PageServerParentData, OutputData, RouteId>;
export type PageServerLoadEvent = Parameters<PageServerLoad>[0];
export type ActionData = unknown;
export type PageServerData = Expand<OptionalUnion<EnsureDefined<Kit.LoadProperties<Awaited<ReturnType<typeof import('../../../../../src/routes/[site]/+page.server.js').load>>>>>>;
export type PageData = Expand<Omit<PageParentData, keyof PageServerData> & EnsureDefined<PageServerData>>;
export type Action<OutputData extends Record<string, any> | void = Record<string, any> | void> = Kit.Action<RouteParams, OutputData, RouteId>
export type Actions<OutputData extends Record<string, any> | void = Record<string, any> | void> = Kit.Actions<RouteParams, OutputData, RouteId>
export type PageProps = { params: RouteParams; data: PageData; form: ActionData }
export type LayoutServerData = null;
export type LayoutData = Expand<LayoutParentData>;
export type LayoutProps = { params: LayoutParams; data: LayoutData; children: import("svelte").Snippet }
export type RequestEvent = Kit.RequestEvent<RouteParams, RouteId>;
+26
View File
@@ -0,0 +1,26 @@
/**
* Config for each linked site: route slug → URL and optional per-site options.
* Add site-specific options here (e.g. title, sandbox, allow) as needed.
*/
export const sites = {
prism: {
url: 'https://prism.ccllc.pro',
title: 'Prism'
},
tasgrid: {
url: 'https://tasgrid.ccllc.pro',
title: 'TasGrid'
},
stackq: {
url: 'https://stackq.ccllc.pro',
title: 'Stackq'
},
hrm: {
url: 'https://hrm.ccllc.pro',
title: 'HRM'
}
} as const;
export type SiteSlug = keyof typeof sites;
export const siteSlugs = Object.keys(sites) as SiteSlug[];
+7 -12
View File
@@ -1,6 +1,7 @@
<script lang="ts">
import { browser } from '$app/environment';
import { Button } from '$lib/components/ui/button';
import { sites, siteSlugs } from '$lib/sites';
let { data }: { data: Record<string, unknown> } = $props();
</script>
@@ -17,18 +18,12 @@
<main class="flex min-h-0 flex-1 flex-col items-center justify-center gap-4 p-4 sm:gap-6 sm:p-6">
<h1 class="text-2xl font-semibold">Base</h1>
<div class="flex w-full max-w-xs flex-col gap-3">
<a href="https://prism.ccllc.pro" class="block" target="_blank" rel="noopener noreferrer">
<Button class="h-16 w-full min-h-16" variant="outline">Prism</Button>
</a>
<a href="https://tasgrid.ccllc.pro" class="block" target="_blank" rel="noopener noreferrer">
<Button class="h-16 min-h-16 w-full" variant="outline">TasGrid</Button>
</a>
<a href="https://stackq.ccllc.pro" class="block" target="_blank" rel="noopener noreferrer">
<Button class="h-16 min-h-16 w-full" variant="outline">Stackq</Button>
</a>
<a href="https://hrm.ccllc.pro" class="block" target="_blank" rel="noopener noreferrer">
<Button class="h-16 min-h-16 w-full" variant="outline">HRM</Button>
</a>
{#each siteSlugs as slug}
{@const site = sites[slug]}
<a href="/{slug}" class="block">
<Button class="h-16 w-full min-h-16" variant="outline">{site.title}</Button>
</a>
{/each}
</div>
</main>
{/if}
+9 -13
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import { Button } from '$lib/components/ui/button';
import { sites, siteSlugs } from '$lib/sites';
let { data: _data }: { data: Record<string, unknown> } = $props();
// `_data` is accepted to mirror Stackq's pattern (unused for now).
@@ -7,20 +8,15 @@
<main class="flex min-h-0 flex-1 flex-col items-center justify-center gap-4 p-4 sm:gap-6 sm:p-6">
<h1 class="w-full max-w-xs shrink-0 text-center text-2xl font-semibold">Base</h1>
<div class="flex w-full max-w-xs shrink-0 flex-col gap-3">
<a href="https://prism.ccllc.pro" class="block" target="_blank" rel="noopener noreferrer">
<Button class="h-16 min-h-16 w-full" variant="outline">Prism</Button>
</a>
<a href="https://tasgrid.ccllc.pro" class="block" target="_blank" rel="noopener noreferrer">
<Button class="h-16 min-h-16 w-full" variant="outline">TasGrid</Button>
</a>
<a href="https://stackq.ccllc.pro" class="block" target="_blank" rel="noopener noreferrer">
<Button class="h-16 min-h-16 w-full" variant="outline">Stackq</Button>
</a>
<a href="https://hrm.ccllc.pro" class="block" target="_blank" rel="noopener noreferrer">
<Button class="h-16 min-h-16 w-full" variant="outline">HRM</Button>
</a>
{#each siteSlugs as slug}
{@const site = sites[slug]}
<a href="/{slug}" class="block">
<Button class="h-16 min-h-16 w-full" variant="outline">
{site.title}
</Button>
</a>
{/each}
</div>
</main>
+7
View File
@@ -0,0 +1,7 @@
<script lang="ts">
let { children }: { children: import('svelte').Snippet } = $props();
</script>
<div class="site-fullscreen">
{@render children?.()}
</div>
+17
View File
@@ -0,0 +1,17 @@
import { sites, type SiteSlug } from '$lib/sites';
import { error } from '@sveltejs/kit';
export function load({ params }) {
const slug = params.site?.toLowerCase() as SiteSlug | undefined;
const site = slug && slug in sites ? sites[slug as SiteSlug] : null;
if (!site) {
throw error(404, `Unknown site: ${params.site}`);
}
return {
slug: slug as SiteSlug,
url: site.url,
title: site.title
};
}
+32
View File
@@ -0,0 +1,32 @@
<script lang="ts">
import { Button } from '$lib/components/ui/button';
let { data }: {
data: { slug: string; url: string; title: string };
} = $props();
</script>
<svelte:head>
<title>{data.title} — Base</title>
</svelte:head>
<main class="relative flex-1 min-h-0 min-w-0 flex flex-col">
<iframe
id="app-iframe"
title={data.title}
src={data.url}
class="absolute inset-0 w-full h-full border-0 rounded-none"
/>
<div
class="absolute top-2 left-1/2 -translate-x-1/2 z-10 flex items-center justify-center pointer-events-none pt-[env(safe-area-inset-top)]"
aria-hidden="true"
>
<div class="pointer-events-auto">
<a href="/">
<Button type="button" variant="secondary" class="shadow-md">
Base
</Button>
</a>
</div>
</div>
</main>
+12
View File
@@ -142,6 +142,18 @@
min-height: 36px;
}
}
/* [site] iframe route: break out of shell and fill viewport */
.site-fullscreen {
position: fixed;
inset: 0;
width: 100vw;
height: 100dvh;
min-height: 100dvh;
z-index: 40;
display: flex;
flex-direction: column;
overflow: hidden;
}
}
/* Use on icon-only links for 44px touch target */