nandi/gleanpublic Fork 0
ff72525
Commits
Clone
git clone https://git.rickub.com/nandi/glean.git
git clone ssh://git@rickub.com/nandi/glean.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

feat(web): add visable loading stateUnverified

Julien Robert committed 2026-08-03T10:45:06+02:00 Browse files
ff72525 parent: b2d3a5b
added web/src/lib/components/NavigationProgress.svelte +65 -0
new file mode 100644
@@ -0,0 +1,65 @@
1+<script lang="ts">
2+ import { navigating } from "$app/state";
3+
4+ // Avoid flashing on instant navigations: only show the bar after a short
5+ // delay if the navigation is still in progress.
6+ let visible = $state(false);
7+ let timer: ReturnType<typeof setTimeout> | undefined;
8+
9+ $effect(() => {
10+ const active = navigating.to !== null;
11+ if (active) {
12+ timer = setTimeout(() => (visible = true), 100);
13+ } else {
14+ if (timer) clearTimeout(timer);
15+ visible = false;
16+ }
17+ });
18+</script>
19+
20+{#if visible}
21+ <div
22+ class="nav-progress"
23+ role="progressbar"
24+ aria-label="Loading"
25+ aria-busy="true"
26+ ></div>
27+{/if}
28+
29+<style>
30+ .nav-progress {
31+ position: fixed;
32+ top: 0;
33+ left: 0;
34+ height: 3px;
35+ width: 100%;
36+ background: var(--accent);
37+ z-index: 60;
38+ /* Indeterminate sweep: shrink-grow across the viewport. */
39+ transform-origin: left center;
40+ animation: nav-progress-indeterminate 1s ease-in-out infinite;
41+ }
42+
43+ @keyframes nav-progress-indeterminate {
44+ 0% {
45+ transform: scaleX(0);
46+ opacity: 0.85;
47+ }
48+ 50% {
49+ transform: scaleX(0.7);
50+ opacity: 1;
51+ }
52+ 100% {
53+ transform: scaleX(0);
54+ opacity: 0.85;
55+ }
56+ }
57+
58+ @media (prefers-reduced-motion: reduce) {
59+ .nav-progress {
60+ animation: none;
61+ transform: scaleX(1);
62+ opacity: 1;
63+ }
64+ }
65+</style>
new file mode 100644
@@ -0,0 +1,65 @@
1+<script lang="ts">
2+ import { navigating } from "$app/state";
3+
4+ // Avoid flashing on instant navigations: only show the bar after a short
5+ // delay if the navigation is still in progress.
6+ let visible = $state(false);
7+ let timer: ReturnType<typeof setTimeout> | undefined;
8+
9+ $effect(() => {
10+ const active = navigating.to !== null;
11+ if (active) {
12+ timer = setTimeout(() => (visible = true), 100);
13+ } else {
14+ if (timer) clearTimeout(timer);
15+ visible = false;
16+ }
17+ });
18+</script>
19+
20+{#if visible}
21+ <div
22+ class="nav-progress"
23+ role="progressbar"
24+ aria-label="Loading"
25+ aria-busy="true"
26+ ></div>
27+{/if}
28+
29+<style>
30+ .nav-progress {
31+ position: fixed;
32+ top: 0;
33+ left: 0;
34+ height: 3px;
35+ width: 100%;
36+ background: var(--accent);
37+ z-index: 60;
38+ /* Indeterminate sweep: shrink-grow across the viewport. */
39+ transform-origin: left center;
40+ animation: nav-progress-indeterminate 1s ease-in-out infinite;
41+ }
42+
43+ @keyframes nav-progress-indeterminate {
44+ 0% {
45+ transform: scaleX(0);
46+ opacity: 0.85;
47+ }
48+ 50% {
49+ transform: scaleX(0.7);
50+ opacity: 1;
51+ }
52+ 100% {
53+ transform: scaleX(0);
54+ opacity: 0.85;
55+ }
56+ }
57+
58+ @media (prefers-reduced-motion: reduce) {
59+ .nav-progress {
60+ animation: none;
61+ transform: scaleX(1);
62+ opacity: 1;
63+ }
64+ }
65+</style>
modified web/src/routes/+layout.svelte +3 -0
@@ -8,6 +8,7 @@
88 import ThemeToggle from "$lib/components/ThemeToggle.svelte";
99 import ShortcutsDialog from "$lib/components/ShortcutsDialog.svelte";
1010 import InstallDialog from "$lib/components/InstallDialog.svelte";
11+ import NavigationProgress from "$lib/components/NavigationProgress.svelte";
1112 import { setCsrfToken, endpoints } from "$lib/api";
1213
1314 let {
@@ -128,6 +129,8 @@
128129
129130 <svelte:window onkeydown={handleKeydown} />
130131
132+<NavigationProgress />
133+
131134 <div class="min-h-screen flex flex-col">
132135 <!-- Top header bar -->
133136 {#if page.url.pathname !== "/"}
@@ -8,6 +8,7 @@
8 import ThemeToggle from "$lib/components/ThemeToggle.svelte";8 import ThemeToggle from "$lib/components/ThemeToggle.svelte";
9 import ShortcutsDialog from "$lib/components/ShortcutsDialog.svelte";9 import ShortcutsDialog from "$lib/components/ShortcutsDialog.svelte";
10 import InstallDialog from "$lib/components/InstallDialog.svelte";10 import InstallDialog from "$lib/components/InstallDialog.svelte";
11+ import NavigationProgress from "$lib/components/NavigationProgress.svelte";
11 import { setCsrfToken, endpoints } from "$lib/api";12 import { setCsrfToken, endpoints } from "$lib/api";
12 13
13 let {14 let {
@@ -128,6 +129,8 @@
128 129
129 <svelte:window onkeydown={handleKeydown} />130 <svelte:window onkeydown={handleKeydown} />
130 131
132+<NavigationProgress />
133+
131 <div class="min-h-screen flex flex-col">134 <div class="min-h-screen flex flex-col">
132 <!-- Top header bar -->135 <!-- Top header bar -->
133 {#if page.url.pathname !== "/"}136 {#if page.url.pathname !== "/"}