From 66a12a6f7717e401ade6bc9538f311f399fcc376 Mon Sep 17 00:00:00 2001 From: LucHeart Date: Thu, 19 Mar 2026 22:07:54 +0100 Subject: [PATCH 01/13] breadcrumb cleanup --- src/lib/state/breadcrumbs-state.svelte.ts | 8 +- .../[shockerId=guid]/edit/+page.svelte | 3 + src/routes/(app)/shockers/own/+page.svelte | 2 +- src/routes/Breadcrumb.svelte | 18 ++-- src/routes/Header.svelte | 97 ++++++++++--------- 5 files changed, 65 insertions(+), 63 deletions(-) diff --git a/src/lib/state/breadcrumbs-state.svelte.ts b/src/lib/state/breadcrumbs-state.svelte.ts index a40241c0..a19a8d18 100644 --- a/src/lib/state/breadcrumbs-state.svelte.ts +++ b/src/lib/state/breadcrumbs-state.svelte.ts @@ -2,7 +2,7 @@ import type { Pathname } from '$app/types'; import { onMount } from 'svelte'; export interface BreadCrumbEntry { - text: string; + label: string; href: Pathname | null; } @@ -12,12 +12,12 @@ export const breadcrumbs = { get state() { return _state; }, - push: (text: string, href: Pathname | null = null) => { + push: (label: string, href: Pathname | null = null) => { onMount(() => { - const entry = { text, href }; + const entry = { label, href }; _state = [..._state, entry]; return () => { - _state = _state.filter((e) => e.text !== entry.text || e.href !== entry.href); + _state = _state.filter((e) => e.label !== entry.label); }; }); }, diff --git a/src/routes/(app)/shockers/[shockerId=guid]/edit/+page.svelte b/src/routes/(app)/shockers/[shockerId=guid]/edit/+page.svelte index ad372995..57fba1b0 100644 --- a/src/routes/(app)/shockers/[shockerId=guid]/edit/+page.svelte +++ b/src/routes/(app)/shockers/[shockerId=guid]/edit/+page.svelte @@ -13,6 +13,7 @@ import * as Select from '$lib/components/ui/select'; import PauseToggle from '$lib/components/utils/PauseToggle.svelte'; import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; + import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import { refreshOwnHubs } from '$lib/state/hubs-state.svelte'; import { LoaderCircle } from '@lucide/svelte'; import { onMount } from 'svelte'; @@ -30,6 +31,8 @@ let model = $state(ShockerModelType.CaiXianlin); let saving = $state(false); + breadcrumbs.push('Edit Shocker', null); + onMount(async () => { try { const shockerId = page.params.shockerId!; diff --git a/src/routes/(app)/shockers/own/+page.svelte b/src/routes/(app)/shockers/own/+page.svelte index a54afd08..024c1487 100644 --- a/src/routes/(app)/shockers/own/+page.svelte +++ b/src/routes/(app)/shockers/own/+page.svelte @@ -116,7 +116,7 @@ {#if moduleType === ModuleType.MapControlModule} {:else} -
+
{#each shockers ?? [] as shocker (shocker.id)} {#if moduleType === ModuleType.ClassicControlModule} diff --git a/src/routes/Breadcrumb.svelte b/src/routes/Breadcrumb.svelte index 4298332a..770b0560 100644 --- a/src/routes/Breadcrumb.svelte +++ b/src/routes/Breadcrumb.svelte @@ -4,19 +4,17 @@ {#if breadcrumbs.state.length > 0} - + - {#each breadcrumbs.state as crumb, index (crumb.href)} + {#each breadcrumbs.state as crumb, i (i)} + {#if i > 0} + + {/if} - {#if index < breadcrumbs.state.length - 1} - - {crumb.text} - - + {#if crumb.href && i < breadcrumbs.state.length - 1} + {crumb.label} {:else} - - {crumb.text} - + {crumb.label} {/if} {/each} diff --git a/src/routes/Header.svelte b/src/routes/Header.svelte index efb10880..08e68adf 100644 --- a/src/routes/Header.svelte +++ b/src/routes/Header.svelte @@ -10,7 +10,7 @@ import GithubIcon from '$lib/components/svg/GithubIcon.svelte'; import { Button } from '$lib/components/ui/button'; import * as DropdownMenu from '$lib/components/ui/dropdown-menu'; - import { useSidebar } from '$lib/components/ui/sidebar'; + import { Separator, useSidebar } from '$lib/components/ui/sidebar'; import { userState } from '$lib/state/user-state.svelte'; import { cn } from '$lib/utils'; import Breadcrumb from './Breadcrumb.svelte'; @@ -28,54 +28,55 @@ {/snippet} -
- - -
-
+
+
+ + + +
+
- + - {#if userState.self} - - - {userState.self.name}'s avatar - - - - - {@render dropdownItem('Profile', '/profile')} - {@render dropdownItem('Settings', '/settings/account')} - {@render dropdownItem('Logout', '/logout')} - - - - {:else} - - - - {/if} + {#if userState.self} + + + {userState.self.name}'s avatar + + + + + {@render dropdownItem('Profile', '/profile')} + {@render dropdownItem('Settings', '/settings/account')} + {@render dropdownItem('Logout', '/logout')} + + + + {:else} + + + + {/if} +
From b1473b212e29a3d284d91e1b1b65642ec3f4582d Mon Sep 17 00:00:00 2001 From: LucHeart Date: Thu, 19 Mar 2026 22:15:42 +0100 Subject: [PATCH 02/13] fix format... --- src/routes/(app)/shockers/[shockerId=guid]/edit/+page.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/(app)/shockers/[shockerId=guid]/edit/+page.svelte b/src/routes/(app)/shockers/[shockerId=guid]/edit/+page.svelte index 57fba1b0..d38a17ea 100644 --- a/src/routes/(app)/shockers/[shockerId=guid]/edit/+page.svelte +++ b/src/routes/(app)/shockers/[shockerId=guid]/edit/+page.svelte @@ -32,7 +32,7 @@ let saving = $state(false); breadcrumbs.push('Edit Shocker', null); - + onMount(async () => { try { const shockerId = page.params.shockerId!; From a99aa45664fc8139b8a27fc07dbe57d613ffd27a Mon Sep 17 00:00:00 2001 From: LucHeart Date: Mon, 23 Mar 2026 23:41:09 +0100 Subject: [PATCH 03/13] add breadcrumb stuff, and edit button to public shares --- src/lib/state/breadcrumbs-state.svelte.ts | 16 ++++-- src/routes/(app)/admin/+layout.svelte | 3 + .../(app)/admin/blacklists/+page.svelte | 3 + src/routes/(app)/admin/config/+page.svelte | 3 + .../(app)/admin/online-hubs/+page.svelte | 3 + src/routes/(app)/admin/users/+page.svelte | 3 + .../(app)/admin/users/[userId]/+page.svelte | 8 +++ src/routes/(app)/admin/webhooks/+page.svelte | 3 + .../hubs/[hubId=guid]/update/+page.svelte | 6 +- src/routes/(app)/profile/+page.svelte | 5 ++ .../(app)/settings/account/+page.svelte | 4 ++ .../(app)/settings/api-tokens/+page.svelte | 4 ++ .../settings/api-tokens/new/+page.svelte | 5 ++ .../(app)/settings/connections/+page.svelte | 4 ++ .../(app)/settings/sessions/+page.svelte | 4 ++ src/routes/(app)/shares/public/+page.svelte | 3 + .../public/[shareId=guid]/edit/+page.svelte | 16 +++++- src/routes/(app)/shares/user/+layout.svelte | 3 + .../[shockerId=guid]/edit/+page.svelte | 7 ++- src/routes/(app)/shockers/logs/+page.svelte | 3 + .../logs/[shockerId=guid]/+page.svelte | 4 ++ src/routes/(app)/shockers/own/+page.svelte | 3 + src/routes/(app)/shockers/shared/+page.svelte | 3 + .../shares/public/[shareId=guid]/+page.svelte | 13 ++++- .../public/[shareId=guid]/ControlView.svelte | 56 ++++++++++++------- 25 files changed, 156 insertions(+), 29 deletions(-) diff --git a/src/lib/state/breadcrumbs-state.svelte.ts b/src/lib/state/breadcrumbs-state.svelte.ts index a19a8d18..dbc2ee31 100644 --- a/src/lib/state/breadcrumbs-state.svelte.ts +++ b/src/lib/state/breadcrumbs-state.svelte.ts @@ -1,9 +1,14 @@ import type { Pathname } from '$app/types'; import { onMount } from 'svelte'; -export interface BreadCrumbEntry { - label: string; +export class BreadCrumbEntry { + label = $state(''); href: Pathname | null; + + constructor(label: string, href: Pathname | null) { + this.label = label; + this.href = href; + } } let _state = $state([]); @@ -12,14 +17,15 @@ export const breadcrumbs = { get state() { return _state; }, - push: (label: string, href: Pathname | null = null) => { + push: (label: string, href: Pathname | null = null): BreadCrumbEntry => { + const entry = new BreadCrumbEntry(label, href); onMount(() => { - const entry = { label, href }; _state = [..._state, entry]; return () => { - _state = _state.filter((e) => e.label !== entry.label); + _state = _state.filter((e) => e !== entry); }; }); + return entry; }, clear: () => (_state = []), }; diff --git a/src/routes/(app)/admin/+layout.svelte b/src/routes/(app)/admin/+layout.svelte index a2481f0e..64425961 100644 --- a/src/routes/(app)/admin/+layout.svelte +++ b/src/routes/(app)/admin/+layout.svelte @@ -2,9 +2,12 @@ import { resolve } from '$app/paths'; import { RoleType } from '$lib/api/internal/v1'; import { Button } from '$lib/components/ui/button'; + import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import { userState } from '$lib/state/user-state.svelte'; import type { Snippet } from 'svelte'; + breadcrumbs.push('Admin', '/admin/users'); + interface Props { children?: Snippet; } diff --git a/src/routes/(app)/admin/blacklists/+page.svelte b/src/routes/(app)/admin/blacklists/+page.svelte index 8afcffa7..945a03ea 100644 --- a/src/routes/(app)/admin/blacklists/+page.svelte +++ b/src/routes/(app)/admin/blacklists/+page.svelte @@ -8,6 +8,9 @@ } from '$lib/api/internal/v1'; import TextInput from '$lib/components/input/TextInput.svelte'; import { Button } from '$lib/components/ui/button'; + import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + + breadcrumbs.push('Blacklists'); import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card'; import { ScrollArea } from '$lib/components/ui/scroll-area'; import * as Select from '$lib/components/ui/select'; diff --git a/src/routes/(app)/admin/config/+page.svelte b/src/routes/(app)/admin/config/+page.svelte index 3edbe503..b2d39ab8 100644 --- a/src/routes/(app)/admin/config/+page.svelte +++ b/src/routes/(app)/admin/config/+page.svelte @@ -9,6 +9,7 @@ LocaleDateTimeRenderer, RenderCell, } from '$lib/components/Table/ColumnUtils'; + import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import DataTable from '$lib/components/Table/DataTableTemplate.svelte'; import { Button } from '$lib/components/ui/button'; import { CardHeader, CardTitle } from '$lib/components/ui/card'; @@ -18,6 +19,8 @@ import DataTableActions from './data-table-actions.svelte'; import WebhookAddDialog from './dialog-item-add.svelte'; + breadcrumbs.push('Config'); + const columns: ColumnDef[] = [ CreateSortableColumnDef('name', 'Name', RenderCell), CreateSortableColumnDef('description', 'Description', RenderCell), diff --git a/src/routes/(app)/admin/online-hubs/+page.svelte b/src/routes/(app)/admin/online-hubs/+page.svelte index 7237f579..f6eec16c 100644 --- a/src/routes/(app)/admin/online-hubs/+page.svelte +++ b/src/routes/(app)/admin/online-hubs/+page.svelte @@ -8,8 +8,11 @@ import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; import { SemVer } from 'semver'; import { onMount } from 'svelte'; + import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import { type OnlineHub, columns } from './columns'; + breadcrumbs.push('Online Hubs'); + let data = $state([]); let sorting = $state([]); let filters = $state([]); diff --git a/src/routes/(app)/admin/users/+page.svelte b/src/routes/(app)/admin/users/+page.svelte index 4d748289..351bf731 100644 --- a/src/routes/(app)/admin/users/+page.svelte +++ b/src/routes/(app)/admin/users/+page.svelte @@ -21,8 +21,11 @@ import { Input } from '$lib/components/ui/input'; import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; import type { TimeoutHandle } from '$lib/types/WAPI'; + import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import DataTableActions from './data-table-actions.svelte'; + breadcrumbs.push('Users'); + const PasswordHashTypeRenderer = (passwordHashType: PasswordHashingAlgorithm) => { if (passwordHashType !== PasswordHashingAlgorithm.BCrypt) return RenderOrangeCell(passwordHashType); diff --git a/src/routes/(app)/admin/users/[userId]/+page.svelte b/src/routes/(app)/admin/users/[userId]/+page.svelte index 36d55f25..4003cd04 100644 --- a/src/routes/(app)/admin/users/[userId]/+page.svelte +++ b/src/routes/(app)/admin/users/[userId]/+page.svelte @@ -3,6 +3,10 @@ import { adminApi } from '$lib/api'; import type { AdminUsersView, AdminUsersViewPaginated } from '$lib/api/internal/v1'; import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; + import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + + breadcrumbs.push('Users', '/admin/users'); + const userCrumb = breadcrumbs.push('Loading...'); let user = $state(null); @@ -21,6 +25,10 @@ .then(handleResponse) .catch(handleApiError); }); + + $effect(() => { + if (user) userCrumb.label = user.name; + }); {user?.name ?? 'Loading...'} diff --git a/src/routes/(app)/admin/webhooks/+page.svelte b/src/routes/(app)/admin/webhooks/+page.svelte index 02683508..42b05271 100644 --- a/src/routes/(app)/admin/webhooks/+page.svelte +++ b/src/routes/(app)/admin/webhooks/+page.svelte @@ -17,8 +17,11 @@ import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; import { onMount } from 'svelte'; import DataTableActions from './data-table-actions.svelte'; + import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import WebhookAddDialog from './dialog-webhook-add.svelte'; + breadcrumbs.push('Webhooks'); + const columns: ColumnDef[] = [ CreateSortableColumnDef('name', 'Name', RenderCell), CreateSortableColumnDef('url', 'Url', RenderCell), diff --git a/src/routes/(app)/hubs/[hubId=guid]/update/+page.svelte b/src/routes/(app)/hubs/[hubId=guid]/update/+page.svelte index d8e29fe1..9b833712 100644 --- a/src/routes/(app)/hubs/[hubId=guid]/update/+page.svelte +++ b/src/routes/(app)/hubs/[hubId=guid]/update/+page.svelte @@ -193,7 +193,11 @@ $effect(() => fetchOtaLogs(page.params.hubId)); breadcrumbs.push('Hubs', '/hubs'); - breadcrumbs.push('Update'); + const hubCrumb = breadcrumbs.push('Update'); + + $effect(() => { + hubCrumb.label = hubName; + }); onMount(refreshOwnHubs); diff --git a/src/routes/(app)/profile/+page.svelte b/src/routes/(app)/profile/+page.svelte index e69de29b..027a5e1f 100644 --- a/src/routes/(app)/profile/+page.svelte +++ b/src/routes/(app)/profile/+page.svelte @@ -0,0 +1,5 @@ + diff --git a/src/routes/(app)/settings/account/+page.svelte b/src/routes/(app)/settings/account/+page.svelte index dd7a9798..b789b70e 100644 --- a/src/routes/(app)/settings/account/+page.svelte +++ b/src/routes/(app)/settings/account/+page.svelte @@ -4,8 +4,12 @@ import ChangeEmail from './ChangeEmail.svelte'; import ChangePassword from './ChangePassword.svelte'; import ChangeUsername from './ChangeUsername.svelte'; + import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import DangerZone from './DangerZone.svelte'; + breadcrumbs.push('Settings', '/settings/account'); + breadcrumbs.push('Account'); + let account = $derived(userState.self); diff --git a/src/routes/(app)/settings/api-tokens/+page.svelte b/src/routes/(app)/settings/api-tokens/+page.svelte index a6aab0e3..263282cf 100644 --- a/src/routes/(app)/settings/api-tokens/+page.svelte +++ b/src/routes/(app)/settings/api-tokens/+page.svelte @@ -22,8 +22,12 @@ import { toast } from 'svelte-sonner'; import DataTableActions from './data-table-actions.svelte'; import TokenCreateDialog from './dialog-token-create.svelte'; + import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import TokenCreatedDialog from './dialog-token-created.svelte'; + breadcrumbs.push('Settings', '/settings/account'); + breadcrumbs.push('API Tokens'); + let loading = $state(false); let data = $state([]); let sorting = $state([]); diff --git a/src/routes/(app)/settings/api-tokens/new/+page.svelte b/src/routes/(app)/settings/api-tokens/new/+page.svelte index 02d3fea4..635c51fd 100644 --- a/src/routes/(app)/settings/api-tokens/new/+page.svelte +++ b/src/routes/(app)/settings/api-tokens/new/+page.svelte @@ -10,6 +10,11 @@ import type { QueryParamsType } from './queryParamsType'; import { page } from '$app/state'; import { browser } from '$app/environment'; + import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + + breadcrumbs.push('Settings', '/settings/account'); + breadcrumbs.push('API Tokens', '/settings/api-tokens'); + breadcrumbs.push('New'); let windowQueryParams = $state< | QueryParamsType diff --git a/src/routes/(app)/settings/connections/+page.svelte b/src/routes/(app)/settings/connections/+page.svelte index 8c740c0c..800e5b44 100644 --- a/src/routes/(app)/settings/connections/+page.svelte +++ b/src/routes/(app)/settings/connections/+page.svelte @@ -16,8 +16,12 @@ import { onMount } from 'svelte'; import { toast } from 'svelte-sonner'; import DisconnectDialog from './dialog-oauth-disconnect.svelte'; + import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import { backendMetadata } from '$lib/state/backend-metadata-state.svelte'; + breadcrumbs.push('Settings', '/settings/account'); + breadcrumbs.push('Connections'); + // ---------- state let loading = $state(false); // overall refresh button state let loadingProviders = $state(true); diff --git a/src/routes/(app)/settings/sessions/+page.svelte b/src/routes/(app)/settings/sessions/+page.svelte index 470477c7..5c0e35c0 100644 --- a/src/routes/(app)/settings/sessions/+page.svelte +++ b/src/routes/(app)/settings/sessions/+page.svelte @@ -20,8 +20,12 @@ import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; import { onMount } from 'svelte'; import { toast } from 'svelte-sonner'; + import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import DataTableActions from './data-table-actions.svelte'; + breadcrumbs.push('Settings', '/settings/account'); + breadcrumbs.push('Sessions'); + let data = $state([]); let sorting = $state([]); diff --git a/src/routes/(app)/shares/public/+page.svelte b/src/routes/(app)/shares/public/+page.svelte index 10b3755d..5c46dad9 100644 --- a/src/routes/(app)/shares/public/+page.svelte +++ b/src/routes/(app)/shares/public/+page.svelte @@ -21,8 +21,11 @@ import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; import { onMount } from 'svelte'; import DataTableActions from './data-table-actions.svelte'; + import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import CreatePublicShareDialog from './dialog-publicshare-create.svelte'; + breadcrumbs.push('Public Shares'); + const columns: ColumnDef[] = [ CreateSortableColumnDef('name', 'Name', RenderCell), CreateSortableColumnDef('createdOn', 'Created at', LocaleDateTimeRenderer), diff --git a/src/routes/(app)/shares/public/[shareId=guid]/edit/+page.svelte b/src/routes/(app)/shares/public/[shareId=guid]/edit/+page.svelte index 92a7af5f..87ff50c3 100644 --- a/src/routes/(app)/shares/public/[shareId=guid]/edit/+page.svelte +++ b/src/routes/(app)/shares/public/[shareId=guid]/edit/+page.svelte @@ -1,5 +1,6 @@
@@ -43,26 +51,34 @@

- - - - - - - - {shareLinkRoot.author.name.charAt(0)} - - -

{shareLinkRoot.author.name}

-
-
- -

Shared by

-
-
-
+
+ {#if userState.self} +
+ +
+ {/if} + + + + + + + + + {shareLinkRoot.author.name.charAt(0)} + + +

{shareLinkRoot.author.name}

+
+
+ +

Shared by

+
+
+
+
From 6b1fdf55fbfe8d4c554deafdc0d92cce509003fc Mon Sep 17 00:00:00 2001 From: LucHeart Date: Wed, 1 Apr 2026 01:07:37 +0200 Subject: [PATCH 04/13] add share copy input --- src/lib/components/CopyInput.svelte | 11 +++++++++-- src/routes/(shortcuts)/s/[id=guid]/+server.ts | 15 +++++++++++++++ src/routes/{ => (shortcuts)}/t/+server.ts | 0 .../usc/[code=guid]}/+server.ts | 0 .../public/[shareId=guid]/ControlView.svelte | 16 +++++++++++----- 5 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 src/routes/(shortcuts)/s/[id=guid]/+server.ts rename src/routes/{ => (shortcuts)}/t/+server.ts (100%) rename src/routes/{usc/[code] => (shortcuts)/usc/[code=guid]}/+server.ts (100%) diff --git a/src/lib/components/CopyInput.svelte b/src/lib/components/CopyInput.svelte index dc8167c4..da45dbc8 100644 --- a/src/lib/components/CopyInput.svelte +++ b/src/lib/components/CopyInput.svelte @@ -9,9 +9,10 @@ value: string; class?: string; icon?: Snippet; + displayValue?: string; } - let { value, class: className, icon }: Props = $props(); + let { value, class: className, icon, displayValue }: Props = $props(); let copied = $state(false); @@ -32,7 +33,13 @@ {#if icon} {@render icon()} {/if} - + -
+ {/if} From a5e2ee953c3f193e4d656283ffb8a844e62aa23c Mon Sep 17 00:00:00 2001 From: LucHeart Date: Wed, 1 Apr 2026 01:20:35 +0200 Subject: [PATCH 05/13] format --- src/routes/(shortcuts)/s/[id=guid]/+server.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/(shortcuts)/s/[id=guid]/+server.ts b/src/routes/(shortcuts)/s/[id=guid]/+server.ts index 1f351987..84046b6f 100644 --- a/src/routes/(shortcuts)/s/[id=guid]/+server.ts +++ b/src/routes/(shortcuts)/s/[id=guid]/+server.ts @@ -1,8 +1,8 @@ import { resolve } from '$app/paths'; +import type { Pathname } from '$app/types'; import { getSiteURL, isShortLinkOrigin } from '$lib/utils/url'; import { redirect } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; -import type { Pathname } from '$app/types'; export const GET: RequestHandler = ({ url, params }) => { const target: Pathname = `/shares/public/${params.id}`; @@ -10,6 +10,6 @@ export const GET: RequestHandler = ({ url, params }) => { if (isShortLinkOrigin(url)) { return redirect(303, getSiteURL(target)); } - + return redirect(303, resolve(target)); }; From 0730dddbc3357001dab204f5e6b9585269ace38e Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Wed, 1 Apr 2026 05:57:53 +0200 Subject: [PATCH 06/13] yeet --- .../shares/public/[shareId=guid]/ControlView.svelte | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/routes/shares/public/[shareId=guid]/ControlView.svelte b/src/routes/shares/public/[shareId=guid]/ControlView.svelte index 97c9dd9a..77da0d15 100644 --- a/src/routes/shares/public/[shareId=guid]/ControlView.svelte +++ b/src/routes/shares/public/[shareId=guid]/ControlView.svelte @@ -8,12 +8,12 @@ import type { Control } from '$lib/signalr/models/Control'; import { onMount, untrack } from 'svelte'; import { page } from '$app/state'; - import { resolve } from '$app/paths'; - import { userState } from '$lib/state/user-state.svelte'; - import { Button } from '$lib/components/ui/button'; - import { Pencil } from '@lucide/svelte'; import CopyInput from '$lib/components/CopyInput.svelte'; import { getSiteShortURL } from '$lib/utils/url'; + import { Button } from '$lib/components/ui/button'; + import { Pencil } from '@lucide/svelte'; + import { userState } from '$lib/state/user-state.svelte'; + import { resolve } from '$app/paths'; interface Props { shareLinkRoot: PublicShareResponse; From ec88a7cbcc458efc7929fb689f898ea34f2cd3e5 Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Wed, 1 Apr 2026 07:04:01 +0200 Subject: [PATCH 07/13] Improve breadcrumb store --- src/lib/state/breadcrumbs-state.svelte.ts | 48 +++++++++++-------- src/routes/(app)/admin/+layout.svelte | 4 +- .../(app)/admin/blacklists/+page.svelte | 4 +- src/routes/(app)/admin/config/+page.svelte | 4 +- .../(app)/admin/online-hubs/+page.svelte | 4 +- src/routes/(app)/admin/users/+page.svelte | 4 +- .../(app)/admin/users/[userId]/+page.svelte | 14 +++--- src/routes/(app)/admin/webhooks/+page.svelte | 4 +- src/routes/(app)/home/+page.svelte | 4 +- src/routes/(app)/hubs/+page.svelte | 4 +- .../hubs/[hubId=guid]/update/+page.svelte | 12 ++--- src/routes/(app)/profile/+page.svelte | 4 +- .../(app)/settings/account/+page.svelte | 8 ++-- .../(app)/settings/api-tokens/+page.svelte | 8 ++-- .../settings/api-tokens/new/+page.svelte | 10 ++-- .../(app)/settings/connections/+page.svelte | 8 ++-- .../(app)/settings/sessions/+page.svelte | 8 ++-- src/routes/(app)/shares/public/+page.svelte | 4 +- .../public/[shareId=guid]/edit/+page.svelte | 12 ++--- src/routes/(app)/shares/user/+layout.svelte | 4 +- .../[shockerId=guid]/edit/+page.svelte | 12 ++--- src/routes/(app)/shockers/logs/+page.svelte | 4 +- .../logs/[shockerId=guid]/+page.svelte | 8 ++-- src/routes/(app)/shockers/own/+page.svelte | 4 +- src/routes/(app)/shockers/shared/+page.svelte | 4 +- .../shares/public/[shareId=guid]/+page.svelte | 14 +++--- 26 files changed, 114 insertions(+), 104 deletions(-) diff --git a/src/lib/state/breadcrumbs-state.svelte.ts b/src/lib/state/breadcrumbs-state.svelte.ts index dbc2ee31..183aba85 100644 --- a/src/lib/state/breadcrumbs-state.svelte.ts +++ b/src/lib/state/breadcrumbs-state.svelte.ts @@ -1,31 +1,39 @@ import type { Pathname } from '$app/types'; import { onMount } from 'svelte'; -export class BreadCrumbEntry { - label = $state(''); +export interface BreadcrumbEntry { + label: string; href: Pathname | null; +} - constructor(label: string, href: Pathname | null) { - this.label = label; - this.href = href; - } +interface BreadcrumbSlot { + id: symbol; + entries: BreadcrumbEntry[]; } -let _state = $state([]); +let _slots = $state([]); export const breadcrumbs = { - get state() { - return _state; - }, - push: (label: string, href: Pathname | null = null): BreadCrumbEntry => { - const entry = new BreadCrumbEntry(label, href); - onMount(() => { - _state = [..._state, entry]; - return () => { - _state = _state.filter((e) => e !== entry); - }; - }); - return entry; + get state(): BreadcrumbEntry[] { + return _slots.flatMap((s) => s.entries); }, - clear: () => (_state = []), }; + +export function registerBreadcrumbs( + entriesFn: () => Array<{ label: string; href?: Pathname | null }> +): void { + const id = Symbol(); + _slots.push({ id, entries: [] }); + + $effect(() => { + const slot = _slots.find((s) => s.id === id); + if (slot) { + slot.entries = entriesFn().map((e) => ({ label: e.label, href: e.href ?? null })); + } + }); + + onMount(() => () => { + const idx = _slots.findIndex((s) => s.id === id); + if (idx !== -1) _slots.splice(idx, 1); + }); +} diff --git a/src/routes/(app)/admin/+layout.svelte b/src/routes/(app)/admin/+layout.svelte index 17302afc..67dbd242 100644 --- a/src/routes/(app)/admin/+layout.svelte +++ b/src/routes/(app)/admin/+layout.svelte @@ -7,11 +7,11 @@ {user?.name ?? 'Loading...'} diff --git a/src/routes/(app)/admin/webhooks/+page.svelte b/src/routes/(app)/admin/webhooks/+page.svelte index 42b05271..3ef115ec 100644 --- a/src/routes/(app)/admin/webhooks/+page.svelte +++ b/src/routes/(app)/admin/webhooks/+page.svelte @@ -17,10 +17,10 @@ import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; import { onMount } from 'svelte'; import DataTableActions from './data-table-actions.svelte'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import WebhookAddDialog from './dialog-webhook-add.svelte'; - breadcrumbs.push('Webhooks'); + registerBreadcrumbs(() => [{ label: 'Webhooks' }]); const columns: ColumnDef[] = [ CreateSortableColumnDef('name', 'Name', RenderCell), diff --git a/src/routes/(app)/home/+page.svelte b/src/routes/(app)/home/+page.svelte index d0b4f51f..18e60d82 100644 --- a/src/routes/(app)/home/+page.svelte +++ b/src/routes/(app)/home/+page.svelte @@ -4,12 +4,12 @@ import Container from '$lib/components/Container.svelte'; import * as Card from '$lib/components/ui/card'; import { Button } from '$lib/components/ui/button'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import { userState } from '$lib/state/user-state.svelte'; import { onlineHubs, ownHubs, refreshOwnHubs } from '$lib/state/hubs-state.svelte'; import { onMount } from 'svelte'; - breadcrumbs.push('Home', '/home'); + registerBreadcrumbs(() => [{ label: 'Home', href: '/home' }]); let shockerCount = $derived( Array.from(ownHubs).reduce((sum, [, hub]) => sum + hub.shockers.length, 0) diff --git a/src/routes/(app)/hubs/+page.svelte b/src/routes/(app)/hubs/+page.svelte index b731c805..07053729 100644 --- a/src/routes/(app)/hubs/+page.svelte +++ b/src/routes/(app)/hubs/+page.svelte @@ -6,7 +6,7 @@ import * as Card from '$lib/components/ui/card'; import * as Table from '$lib/components/ui/table'; import { IsMobile } from '$lib/hooks/is-mobile.svelte'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import { onlineHubs, ownHubs, refreshOwnHubs } from '$lib/state/hubs-state.svelte'; import { onMount } from 'svelte'; import type { Hub } from './columns'; @@ -57,7 +57,7 @@ } } - breadcrumbs.push('Hubs', '/hubs'); + registerBreadcrumbs(() => [{ label: 'Hubs', href: '/hubs' }]); onMount(refreshOwnHubs); diff --git a/src/routes/(app)/hubs/[hubId=guid]/update/+page.svelte b/src/routes/(app)/hubs/[hubId=guid]/update/+page.svelte index efc94d89..9cd8342f 100644 --- a/src/routes/(app)/hubs/[hubId=guid]/update/+page.svelte +++ b/src/routes/(app)/hubs/[hubId=guid]/update/+page.svelte @@ -72,7 +72,7 @@ import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; import { getConnection } from '$lib/signalr/user.svelte'; import { serializeOtaInstallMessage } from '$lib/signalr/serializers/OtaInstall'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import { HubOnlineState, onlineHubs, @@ -197,12 +197,10 @@ $effect(() => fetchOtaLogs(page.params.hubId)); - breadcrumbs.push('Hubs', '/hubs'); - const hubCrumb = breadcrumbs.push('Update'); - - $effect(() => { - hubCrumb.label = hubName; - }); + registerBreadcrumbs(() => [ + { label: 'Hubs', href: '/hubs' }, + { label: hubName ?? 'Update' }, + ]); onMount(refreshOwnHubs); diff --git a/src/routes/(app)/profile/+page.svelte b/src/routes/(app)/profile/+page.svelte index 027a5e1f..6124040a 100644 --- a/src/routes/(app)/profile/+page.svelte +++ b/src/routes/(app)/profile/+page.svelte @@ -1,5 +1,5 @@ diff --git a/src/routes/(app)/settings/account/+page.svelte b/src/routes/(app)/settings/account/+page.svelte index b789b70e..6aeb7b62 100644 --- a/src/routes/(app)/settings/account/+page.svelte +++ b/src/routes/(app)/settings/account/+page.svelte @@ -4,11 +4,13 @@ import ChangeEmail from './ChangeEmail.svelte'; import ChangePassword from './ChangePassword.svelte'; import ChangeUsername from './ChangeUsername.svelte'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import DangerZone from './DangerZone.svelte'; - breadcrumbs.push('Settings', '/settings/account'); - breadcrumbs.push('Account'); + registerBreadcrumbs(() => [ + { label: 'Settings', href: '/settings/account' }, + { label: 'Account' }, + ]); let account = $derived(userState.self); diff --git a/src/routes/(app)/settings/api-tokens/+page.svelte b/src/routes/(app)/settings/api-tokens/+page.svelte index 9c016e35..753c6bf5 100644 --- a/src/routes/(app)/settings/api-tokens/+page.svelte +++ b/src/routes/(app)/settings/api-tokens/+page.svelte @@ -22,11 +22,13 @@ import { toast } from 'svelte-sonner'; import DataTableActions from './data-table-actions.svelte'; import TokenCreateDialog from './dialog-token-create.svelte'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import TokenCreatedDialog from './dialog-token-created.svelte'; - breadcrumbs.push('Settings', '/settings/account'); - breadcrumbs.push('API Tokens'); + registerBreadcrumbs(() => [ + { label: 'Settings', href: '/settings/account' }, + { label: 'API Tokens' }, + ]); let data = $state([]); let sorting = $state([]); diff --git a/src/routes/(app)/settings/api-tokens/new/+page.svelte b/src/routes/(app)/settings/api-tokens/new/+page.svelte index 635c51fd..df8ac148 100644 --- a/src/routes/(app)/settings/api-tokens/new/+page.svelte +++ b/src/routes/(app)/settings/api-tokens/new/+page.svelte @@ -10,11 +10,13 @@ import type { QueryParamsType } from './queryParamsType'; import { page } from '$app/state'; import { browser } from '$app/environment'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; - breadcrumbs.push('Settings', '/settings/account'); - breadcrumbs.push('API Tokens', '/settings/api-tokens'); - breadcrumbs.push('New'); + registerBreadcrumbs(() => [ + { label: 'Settings', href: '/settings/account' }, + { label: 'API Tokens', href: '/settings/api-tokens' }, + { label: 'New' }, + ]); let windowQueryParams = $state< | QueryParamsType diff --git a/src/routes/(app)/settings/connections/+page.svelte b/src/routes/(app)/settings/connections/+page.svelte index 601185fe..36ca6945 100644 --- a/src/routes/(app)/settings/connections/+page.svelte +++ b/src/routes/(app)/settings/connections/+page.svelte @@ -16,11 +16,13 @@ import { onMount } from 'svelte'; import { toast } from 'svelte-sonner'; import DisconnectDialog from './dialog-oauth-disconnect.svelte'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import { backendMetadata } from '$lib/state/backend-metadata-state.svelte'; - breadcrumbs.push('Settings', '/settings/account'); - breadcrumbs.push('Connections'); + registerBreadcrumbs(() => [ + { label: 'Settings', href: '/settings/account' }, + { label: 'Connections' }, + ]); // ---------- state let loading = $state(false); // overall refresh button state diff --git a/src/routes/(app)/settings/sessions/+page.svelte b/src/routes/(app)/settings/sessions/+page.svelte index f5fea65c..9a5b2ca6 100644 --- a/src/routes/(app)/settings/sessions/+page.svelte +++ b/src/routes/(app)/settings/sessions/+page.svelte @@ -19,11 +19,13 @@ import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; import { onMount } from 'svelte'; import { toast } from 'svelte-sonner'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import DataTableActions from './data-table-actions.svelte'; - breadcrumbs.push('Settings', '/settings/account'); - breadcrumbs.push('Sessions'); + registerBreadcrumbs(() => [ + { label: 'Settings', href: '/settings/account' }, + { label: 'Sessions' }, + ]); let data = $state([]); let sorting = $state([]); diff --git a/src/routes/(app)/shares/public/+page.svelte b/src/routes/(app)/shares/public/+page.svelte index 5c46dad9..4c102983 100644 --- a/src/routes/(app)/shares/public/+page.svelte +++ b/src/routes/(app)/shares/public/+page.svelte @@ -21,10 +21,10 @@ import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; import { onMount } from 'svelte'; import DataTableActions from './data-table-actions.svelte'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import CreatePublicShareDialog from './dialog-publicshare-create.svelte'; - breadcrumbs.push('Public Shares'); + registerBreadcrumbs(() => [{ label: 'Public Shares' }]); const columns: ColumnDef[] = [ CreateSortableColumnDef('name', 'Name', RenderCell), diff --git a/src/routes/(app)/shares/public/[shareId=guid]/edit/+page.svelte b/src/routes/(app)/shares/public/[shareId=guid]/edit/+page.svelte index 87ff50c3..6f76925e 100644 --- a/src/routes/(app)/shares/public/[shareId=guid]/edit/+page.svelte +++ b/src/routes/(app)/shares/public/[shareId=guid]/edit/+page.svelte @@ -13,12 +13,9 @@ import { onMount } from 'svelte'; import { toast } from 'svelte-sonner'; import SharedDevice from './SharedDevice.svelte'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import DialogAddShocker from './dialog-add-shocker.svelte'; - breadcrumbs.push('Public Shares', '/shares/public'); - const shareCrumb = breadcrumbs.push('Edit'); - let publicShareRequest = $state>(getPublicShare()); let publicShareData = $state(null); let showAddShockerModal = $state(false); @@ -28,9 +25,10 @@ const shareId = $derived(page.params.shareId); const publicUrl = $derived(resolve(`/shares/public/${shareId}`)); - $effect(() => { - if (publicShareData) shareCrumb.label = publicShareData.name; - }); + registerBreadcrumbs(() => [ + { label: 'Public Shares', href: '/shares/public' }, + { label: publicShareData?.name ?? 'Edit' }, + ]); onMount(() => { refreshOwnHubs(); diff --git a/src/routes/(app)/shares/user/+layout.svelte b/src/routes/(app)/shares/user/+layout.svelte index 3ede652a..2fb674b1 100644 --- a/src/routes/(app)/shares/user/+layout.svelte +++ b/src/routes/(app)/shares/user/+layout.svelte @@ -14,9 +14,9 @@ import DialogShareCodeCreated from './dialog-share-code-created.svelte'; import DialogShareCodeRedeem from './dialog-share-code-redeem.svelte'; import { resolve } from '$app/paths'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; - breadcrumbs.push('User Shares', '/shares/user/outgoing'); + registerBreadcrumbs(() => [{ label: 'User Shares', href: '/shares/user/outgoing' }]); let createDialogOpen = $state(false); let redeemDialogOpen = $state(false); diff --git a/src/routes/(app)/shockers/[shockerId=guid]/edit/+page.svelte b/src/routes/(app)/shockers/[shockerId=guid]/edit/+page.svelte index 1b973c9d..d365f181 100644 --- a/src/routes/(app)/shockers/[shockerId=guid]/edit/+page.svelte +++ b/src/routes/(app)/shockers/[shockerId=guid]/edit/+page.svelte @@ -14,7 +14,7 @@ import * as Select from '$lib/components/ui/select'; import PauseToggle from '$lib/components/utils/PauseToggle.svelte'; import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import { refreshOwnHubs } from '$lib/state/hubs-state.svelte'; import { ArrowLeft, LoaderCircle, Trash2 } from '@lucide/svelte'; import { onMount } from 'svelte'; @@ -33,12 +33,10 @@ let model = $state(ShockerModelType.CaiXianlin); let saving = $state(false); - breadcrumbs.push('Shockers', '/shockers/own'); - const shockerCrumb = breadcrumbs.push('Edit Shocker'); - - $effect(() => { - if (shocker) shockerCrumb.label = shocker.name; - }); + registerBreadcrumbs(() => [ + { label: 'Shockers', href: '/shockers/own' }, + { label: shocker?.name ?? 'Edit Shocker' }, + ]); let hasChanges = $derived( shocker !== null && diff --git a/src/routes/(app)/shockers/logs/+page.svelte b/src/routes/(app)/shockers/logs/+page.svelte index d65004fc..9ac50334 100644 --- a/src/routes/(app)/shockers/logs/+page.svelte +++ b/src/routes/(app)/shockers/logs/+page.svelte @@ -10,9 +10,9 @@ } from '$lib/components/Table/ColumnUtils'; import DataTable from '$lib/components/Table/DataTableTemplate.svelte'; import * as Card from '$lib/components/ui/card'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; - breadcrumbs.push('Shocker Logs'); + registerBreadcrumbs(() => [{ label: 'Shocker Logs' }]); let { data } = $props(); diff --git a/src/routes/(app)/shockers/logs/[shockerId=guid]/+page.svelte b/src/routes/(app)/shockers/logs/[shockerId=guid]/+page.svelte index 1e88aafe..39e9c90a 100644 --- a/src/routes/(app)/shockers/logs/[shockerId=guid]/+page.svelte +++ b/src/routes/(app)/shockers/logs/[shockerId=guid]/+page.svelte @@ -10,10 +10,12 @@ } from '$lib/components/Table/ColumnUtils'; import DataTable from '$lib/components/Table/DataTableTemplate.svelte'; import * as Card from '$lib/components/ui/card'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; - breadcrumbs.push('Shocker Logs', '/shockers/logs'); - breadcrumbs.push('Details'); + registerBreadcrumbs(() => [ + { label: 'Shocker Logs', href: '/shockers/logs' }, + { label: 'Details' }, + ]); let { data } = $props(); diff --git a/src/routes/(app)/shockers/own/+page.svelte b/src/routes/(app)/shockers/own/+page.svelte index 9a6d7282..c4150f43 100644 --- a/src/routes/(app)/shockers/own/+page.svelte +++ b/src/routes/(app)/shockers/own/+page.svelte @@ -18,12 +18,12 @@ import * as Popover from '$lib/components/ui/popover'; import { ControlDurationDefault, ControlIntensityDefault } from '$lib/constants/ControlConstants'; import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import { ownHubs, refreshOwnHubs } from '$lib/state/hubs-state.svelte'; import { onMount } from 'svelte'; import { toast } from 'svelte-sonner'; - breadcrumbs.push('Shockers'); + registerBreadcrumbs(() => [{ label: 'Shockers' }]); let shockers = $derived(Array.from(ownHubs).flatMap(([, hub]) => hub.shockers)); diff --git a/src/routes/(app)/shockers/shared/+page.svelte b/src/routes/(app)/shockers/shared/+page.svelte index 71d3d017..3a490504 100644 --- a/src/routes/(app)/shockers/shared/+page.svelte +++ b/src/routes/(app)/shockers/shared/+page.svelte @@ -4,11 +4,11 @@ import SharedShockerControlModule from '$lib/components/ControlModules/SharedShockerControlModule.svelte'; import * as Avatar from '$lib/components/ui/avatar'; import { onlineHubs } from '$lib/state/hubs-state.svelte'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import { sharedHubsState, refreshSharedHubs } from '$lib/state/shared-hubs-state.svelte'; import { onMount } from 'svelte'; - breadcrumbs.push('Shared Shockers'); + registerBreadcrumbs(() => [{ label: 'Shared Shockers' }]); onMount(refreshSharedHubs); diff --git a/src/routes/shares/public/[shareId=guid]/+page.svelte b/src/routes/shares/public/[shareId=guid]/+page.svelte index c9d227af..51a12fff 100644 --- a/src/routes/shares/public/[shareId=guid]/+page.svelte +++ b/src/routes/shares/public/[shareId=guid]/+page.svelte @@ -8,14 +8,11 @@ import * as Card from '$lib/components/ui/card/index.js'; import Input from '$lib/components/ui/input/input.svelte'; import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; - import { breadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; + import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; import { userState } from '$lib/state/user-state.svelte'; import { onMount } from 'svelte'; import ControlView from './ControlView.svelte'; - breadcrumbs.push('Public Shares', '/shares/public'); - const shareCrumb = breadcrumbs.push('Public Share'); - // Page is reactive and query parameters can change let loginUrl = $derived( resolve(`/login?redirect=${encodeURIComponent(page.url.pathname + page.url.search)}`) @@ -27,6 +24,11 @@ let guestName = $state(null); let entered = $state(false); + registerBreadcrumbs(() => [ + { label: 'Public Shares', href: '/shares/public' }, + { label: shareData?.name ?? 'Public Share' }, + ]); + // Fetch share details async function getShareDetails(): Promise { const shareId = page.params.shareId; @@ -44,10 +46,6 @@ } } - $effect(() => { - if (shareData) shareCrumb.label = shareData.name; - }); - onMount(async () => { await details; From 3a994fa9eabbb5856c1ea7d60aef1ae2c008d5f1 Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Wed, 1 Apr 2026 07:07:15 +0200 Subject: [PATCH 08/13] Update +page.svelte --- src/routes/(app)/admin/blacklists/+page.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/(app)/admin/blacklists/+page.svelte b/src/routes/(app)/admin/blacklists/+page.svelte index 55237b65..b46fd47c 100644 --- a/src/routes/(app)/admin/blacklists/+page.svelte +++ b/src/routes/(app)/admin/blacklists/+page.svelte @@ -9,8 +9,6 @@ import TextInput from '$lib/components/input/TextInput.svelte'; import { Button } from '$lib/components/ui/button'; import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte'; - - registerBreadcrumbs(() => [{ label: 'Blacklists' }]); import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card'; import { ScrollArea } from '$lib/components/ui/scroll-area'; import * as Select from '$lib/components/ui/select'; @@ -18,6 +16,8 @@ import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; import type { ValidationResult } from '$lib/types/ValidationResult'; import type { TimeoutHandle } from '$lib/types/WAPI'; + + registerBreadcrumbs(() => [{ label: 'Blacklists' }]); // --- state --- let usernameEntry = $state(''); From 582b866dda05649244acecdf3a10eb7a55ab8168 Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Wed, 1 Apr 2026 11:29:24 +0200 Subject: [PATCH 09/13] Update src/routes/Breadcrumb.svelte Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/routes/Breadcrumb.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Breadcrumb.svelte b/src/routes/Breadcrumb.svelte index 770b0560..60613de0 100644 --- a/src/routes/Breadcrumb.svelte +++ b/src/routes/Breadcrumb.svelte @@ -6,7 +6,7 @@ {#if breadcrumbs.state.length > 0} - {#each breadcrumbs.state as crumb, i (i)} + {#each breadcrumbs.state as crumb, i (crumb.href || crumb.label)} {#if i > 0} {/if} From 8380a11be2d54c5d47394dbbc66a5afca3c027e0 Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Wed, 1 Apr 2026 11:30:11 +0200 Subject: [PATCH 10/13] Update src/routes/(app)/profile/+page.svelte Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/routes/(app)/profile/+page.svelte | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/routes/(app)/profile/+page.svelte b/src/routes/(app)/profile/+page.svelte index 6124040a..56e96ece 100644 --- a/src/routes/(app)/profile/+page.svelte +++ b/src/routes/(app)/profile/+page.svelte @@ -3,3 +3,8 @@ registerBreadcrumbs(() => [{ label: 'Profile' }]); + +
+

Profile

+

Your profile page is coming soon.

+
From 788a430f2ccef23970f94daf535edea0ed695eed Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Wed, 1 Apr 2026 17:44:12 +0200 Subject: [PATCH 11/13] fix pr comments --- src/lib/state/breadcrumbs-state.svelte.ts | 4 ++-- src/routes/Breadcrumb.svelte | 3 ++- src/routes/Header.svelte | 3 ++- src/routes/shares/public/[shareId=guid]/ControlView.svelte | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/lib/state/breadcrumbs-state.svelte.ts b/src/lib/state/breadcrumbs-state.svelte.ts index 183aba85..1ce14ce0 100644 --- a/src/lib/state/breadcrumbs-state.svelte.ts +++ b/src/lib/state/breadcrumbs-state.svelte.ts @@ -1,5 +1,5 @@ import type { Pathname } from '$app/types'; -import { onMount } from 'svelte'; +import { onDestroy } from 'svelte'; export interface BreadcrumbEntry { label: string; @@ -32,7 +32,7 @@ export function registerBreadcrumbs( } }); - onMount(() => () => { + onDestroy(() => { const idx = _slots.findIndex((s) => s.id === id); if (idx !== -1) _slots.splice(idx, 1); }); diff --git a/src/routes/Breadcrumb.svelte b/src/routes/Breadcrumb.svelte index 60613de0..5397d7c7 100644 --- a/src/routes/Breadcrumb.svelte +++ b/src/routes/Breadcrumb.svelte @@ -1,6 +1,7 @@ {#if breadcrumbs.state.length > 0} @@ -12,7 +13,7 @@ {/if} {#if crumb.href && i < breadcrumbs.state.length - 1} - {crumb.label} + {crumb.label} {:else} {crumb.label} {/if} diff --git a/src/routes/Header.svelte b/src/routes/Header.svelte index 08e68adf..efa0b0ad 100644 --- a/src/routes/Header.svelte +++ b/src/routes/Header.svelte @@ -10,7 +10,8 @@ import GithubIcon from '$lib/components/svg/GithubIcon.svelte'; import { Button } from '$lib/components/ui/button'; import * as DropdownMenu from '$lib/components/ui/dropdown-menu'; - import { Separator, useSidebar } from '$lib/components/ui/sidebar'; + import { Separator } from '$lib/components/ui/separator'; + import { useSidebar } from '$lib/components/ui/sidebar'; import { userState } from '$lib/state/user-state.svelte'; import { cn } from '$lib/utils'; import Breadcrumb from './Breadcrumb.svelte'; diff --git a/src/routes/shares/public/[shareId=guid]/ControlView.svelte b/src/routes/shares/public/[shareId=guid]/ControlView.svelte index 77da0d15..afc77c75 100644 --- a/src/routes/shares/public/[shareId=guid]/ControlView.svelte +++ b/src/routes/shares/public/[shareId=guid]/ControlView.svelte @@ -60,7 +60,7 @@
- {#if userState.self} + {#if userState.self?.id === shareLinkRoot.author.id} {/if} From 633a3e57f00cbb192e46f8db9858028619819ee5 Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Wed, 1 Apr 2026 17:48:52 +0200 Subject: [PATCH 12/13] more format --- src/routes/(app)/admin/blacklists/+page.svelte | 2 +- src/routes/(app)/hubs/[hubId=guid]/update/+page.svelte | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/routes/(app)/admin/blacklists/+page.svelte b/src/routes/(app)/admin/blacklists/+page.svelte index b46fd47c..45fe5cf0 100644 --- a/src/routes/(app)/admin/blacklists/+page.svelte +++ b/src/routes/(app)/admin/blacklists/+page.svelte @@ -16,7 +16,7 @@ import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; import type { ValidationResult } from '$lib/types/ValidationResult'; import type { TimeoutHandle } from '$lib/types/WAPI'; - + registerBreadcrumbs(() => [{ label: 'Blacklists' }]); // --- state --- diff --git a/src/routes/(app)/hubs/[hubId=guid]/update/+page.svelte b/src/routes/(app)/hubs/[hubId=guid]/update/+page.svelte index 9cd8342f..505658d3 100644 --- a/src/routes/(app)/hubs/[hubId=guid]/update/+page.svelte +++ b/src/routes/(app)/hubs/[hubId=guid]/update/+page.svelte @@ -197,10 +197,7 @@ $effect(() => fetchOtaLogs(page.params.hubId)); - registerBreadcrumbs(() => [ - { label: 'Hubs', href: '/hubs' }, - { label: hubName ?? 'Update' }, - ]); + registerBreadcrumbs(() => [{ label: 'Hubs', href: '/hubs' }, { label: hubName ?? 'Update' }]); onMount(refreshOwnHubs); From af208f75f3d74fd4396bb1e4dc92f5a20e1250ed Mon Sep 17 00:00:00 2001 From: HeavenVR Date: Wed, 1 Apr 2026 17:58:13 +0200 Subject: [PATCH 13/13] Fix more stuff --- src/lib/state/breadcrumbs-state.svelte.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/state/breadcrumbs-state.svelte.ts b/src/lib/state/breadcrumbs-state.svelte.ts index 1ce14ce0..40492c1e 100644 --- a/src/lib/state/breadcrumbs-state.svelte.ts +++ b/src/lib/state/breadcrumbs-state.svelte.ts @@ -11,7 +11,7 @@ interface BreadcrumbSlot { entries: BreadcrumbEntry[]; } -let _slots = $state([]); +const _slots = $state([]); export const breadcrumbs = { get state(): BreadcrumbEntry[] {