ConfigShell.svelte RawHistoryFolder
1<script lang="ts">
2 import { onMount } from 'svelte';
3 import { Home } from '@lucide/svelte';
4
5 interface Props {
6 children?: import('svelte').Snippet;
7 }
8
9 let { children }: Props = $props();
10
11 function navigateWithViewTransition(url: string) {
12 if (typeof document.startViewTransition !== 'function') {
13 window.location.href = url;
14 return;
15 }
16
17 const transition = document.startViewTransition(() => {
18 document.documentElement.dataset.vt = 'leaving';
19 });
20
21 transition.finished.finally(() => {
22 window.location.href = url;
23 });
24 }
25
26 onMount(() => {
27 // Avoid default WebKit focus rings.
28 document.documentElement.dataset.vt = 'ready';
29 });
30</script>
31
32<svelte:head>
33 <title>dumb://config</title>
34 <meta name="description" content="Dumber Browser - Settings" />
35 <!-- eslint-disable-next-line svelte/no-at-html-tags -- trusted static styles for shell initialization -->
36 {@html `<style>
37 html, body { margin: 0; padding: 0; }
38 html { background: var(--background, #0a0a0a); }
39 body { background: var(--background, #0a0a0a); color: var(--foreground, #e5e5e5); }
40 *:focus { outline: none !important; }
41 *:focus-visible { outline: none !important; }
42 button:focus, input:focus, a:focus { outline: none !important; box-shadow: none !important; }
43 * { -webkit-overflow-scrolling: touch; }
44 </style>`}
45</svelte:head>
46
47<div class="config-shell browser-ui">
48 <div class="terminal-frame">
49 <header class="terminal-header">
50 <div class="header-left">
51 <span class="terminal-path">dumb://config</span>
52 </div>
53 <div class="header-right">
54 <button
55 class="action-button"
56 type="button"
57 onclick={() => navigateWithViewTransition('dumb://home')}
58 title="Back to Home"
59 >
60 <Home size={14} strokeWidth={2} />
61 <span class="button-text">HOME</span>
62 </button>
63 </div>
64 </header>
65
66 <main class="terminal-body scrollable">
67 {#if children}
68 {@render children()}
69 {/if}
70 </main>
71
72 <footer class="terminal-footer">
73 <span class="footer-text">Dumber Browser</span>
74 </footer>
75 </div>
76</div>
77
78<style>
79 :global(html),
80 :global(body) {
81 height: 100%;
82 }
83
84 :global(body) {
85 overflow: hidden;
86 overscroll-behavior: contain;
87 }
88
89 .config-shell {
90 height: 100vh;
91 min-height: 100vh;
92 display: flex;
93 flex-direction: column;
94 font-family: ui-monospace, 'Fira Code', 'Cascadia Code', Menlo, Monaco, Consolas, monospace;
95 line-height: 1.5;
96 color: var(--foreground);
97 background: var(--background);
98 overflow: hidden;
99 }
100
101 .terminal-frame {
102 flex: 1;
103 display: grid;
104 grid-template-rows: auto 1fr auto;
105 width: 100%;
106 height: 100%;
107 background: color-mix(in srgb, var(--card) 60%, var(--background) 40%);
108 }
109
110 :global(.dark) .terminal-frame {
111 box-shadow: 0 24px 48px -12px rgb(0 0 0 / 0.5);
112 }
113
114 .terminal-header {
115 display: flex;
116 align-items: center;
117 justify-content: space-between;
118 gap: 1rem;
119 padding: 0.75rem 1rem;
120 min-height: 3.5rem;
121 border-bottom: 1px solid var(--border);
122 background: color-mix(in srgb, var(--background) 90%, var(--card) 10%);
123 position: sticky;
124 top: 0;
125 z-index: 5;
126 }
127
128 .header-left {
129 display: flex;
130 align-items: center;
131 gap: 1.5rem;
132 }
133
134 .terminal-path {
135 font-size: 0.8rem;
136 font-weight: 600;
137 color: var(--primary, #4ade80);
138 letter-spacing: 0.05em;
139 }
140
141 .header-right {
142 display: flex;
143 align-items: center;
144 gap: 0.5rem;
145 }
146
147 .action-button {
148 display: flex;
149 align-items: center;
150 gap: 0.5rem;
151 padding: 0.4rem 0.75rem;
152 font-size: 0.7rem;
153 font-weight: 500;
154 letter-spacing: 0.08em;
155 color: var(--muted-foreground);
156 background: transparent;
157 border-width: 1px;
158 border-style: solid;
159 border-color: var(--border);
160 cursor: pointer;
161 transition: all 120ms ease;
162 }
163
164 .action-button:hover,
165 .action-button:focus-visible {
166 color: var(--foreground);
167 border-color: color-mix(in srgb, var(--border) 50%, var(--foreground) 50%);
168 background: color-mix(in srgb, var(--card) 30%, transparent);
169 outline: none;
170 }
171
172 .terminal-body {
173 display: flex;
174 flex-direction: column;
175 min-height: 0;
176 overflow: hidden;
177 background: var(--background);
178 }
179
180 .terminal-body.scrollable {
181 overflow-y: auto;
182 -webkit-overflow-scrolling: touch;
183 touch-action: pan-y;
184 }
185
186 .terminal-footer {
187 display: flex;
188 align-items: center;
189 justify-content: center;
190 padding: 0.75rem 1rem;
191 border-top: 1px solid var(--border);
192 background: color-mix(in srgb, var(--background) 92%, var(--card) 8%);
193 }
194
195 .footer-text {
196 font-size: 0.7rem;
197 letter-spacing: 0.08em;
198 color: var(--muted-foreground);
199 }
200
201 /* View Transition helpers */
202 :global(html[data-vt='leaving']) {
203 view-transition-name: config-root;
204 }
205
206 :global(::view-transition-old(config-root)) {
207 animation: vt-fade-out 180ms ease both;
208 }
209
210 :global(::view-transition-new(config-root)) {
211 animation: vt-fade-in 220ms ease both;
212 }
213
214 @keyframes vt-fade-out {
215 to {
216 opacity: 0;
217 transform: translateY(6px);
218 }
219 }
220
221 @keyframes vt-fade-in {
222 from {
223 opacity: 0;
224 transform: translateY(-6px);
225 }
226 }
227</style>