sh3-core 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Shell.svelte +4 -4
- package/dist/api.d.ts +1 -0
- package/dist/api.js +2 -0
- package/dist/createShell.js +4 -0
- package/dist/diagnostic/DiagnosticPanel.svelte +3 -1
- package/dist/diagnostic/DiagnosticRoutes.svelte +99 -0
- package/dist/diagnostic/DiagnosticRoutes.svelte.d.ts +3 -0
- package/dist/diagnostic/diagnosticApp.js +1 -0
- package/dist/diagnostic/diagnosticShard.svelte.js +16 -1
- package/dist/layout/DragPreview.svelte +1 -1
- package/dist/layout/LayoutRenderer.svelte +6 -4
- package/dist/overlays/ModalFrame.svelte +1 -1
- package/dist/overlays/PopupFrame.svelte +1 -1
- package/dist/overlays/ToastItem.svelte +1 -1
- package/dist/primitives/ResizableSplitter.svelte +1 -1
- package/dist/primitives/TabbedPanel.svelte +4 -4
- package/dist/server-shard/types.d.ts +3 -3
- package/dist/server-shard/types.js +1 -1
- package/dist/shards/types.d.ts +1 -1
- package/dist/shell-shard/ShellHome.svelte +4 -4
- package/dist/theme.d.ts +28 -0
- package/dist/theme.js +92 -0
- package/dist/tokens.css +7 -1
- package/package.json +1 -1
package/dist/Shell.svelte
CHANGED
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
height: 100%;
|
|
102
102
|
width: 100%;
|
|
103
103
|
position: relative;
|
|
104
|
-
background: var(--shell-bg);
|
|
104
|
+
background: var(--shell-grad-bg, var(--shell-bg));
|
|
105
105
|
color: var(--shell-fg);
|
|
106
106
|
}
|
|
107
107
|
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
align-items: center;
|
|
111
111
|
gap: var(--shell-pad-md);
|
|
112
112
|
padding: 0 var(--shell-pad-md);
|
|
113
|
-
background: var(--shell-bg-elevated);
|
|
113
|
+
background: var(--shell-grad-bg-elevated, var(--shell-bg-elevated));
|
|
114
114
|
border-bottom: 1px solid var(--shell-border);
|
|
115
115
|
user-select: none;
|
|
116
116
|
}
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
.shell-content {
|
|
124
124
|
position: relative;
|
|
125
125
|
overflow: hidden;
|
|
126
|
-
background: var(--shell-bg);
|
|
126
|
+
background: var(--shell-grad-bg, var(--shell-bg));
|
|
127
127
|
min-width: 0;
|
|
128
128
|
min-height: 0;
|
|
129
129
|
}
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
align-items: center;
|
|
133
133
|
justify-content: space-between;
|
|
134
134
|
padding: 0 var(--shell-pad-md);
|
|
135
|
-
background: var(--shell-bg-sunken);
|
|
135
|
+
background: var(--shell-grad-bg-sunken, var(--shell-bg-sunken));
|
|
136
136
|
border-top: 1px solid var(--shell-border);
|
|
137
137
|
color: var(--shell-fg-muted);
|
|
138
138
|
font-size: 11px;
|
package/dist/api.d.ts
CHANGED
package/dist/api.js
CHANGED
|
@@ -43,3 +43,5 @@ export const capabilities = {
|
|
|
43
43
|
/** Whether this target supports hot-installing packages via dynamic import from blob URL. */
|
|
44
44
|
hotInstall: typeof Blob !== 'undefined' && typeof URL.createObjectURL === 'function',
|
|
45
45
|
};
|
|
46
|
+
// Theme token override API (shell-level theming support).
|
|
47
|
+
export { setTokenOverrides, clearTokenOverrides, getTokenOverrides, } from './theme';
|
package/dist/createShell.js
CHANGED
|
@@ -10,6 +10,7 @@ import { mount } from 'svelte';
|
|
|
10
10
|
import { Shell } from './index';
|
|
11
11
|
import { registerShard, registerApp, bootstrap, __setBackend, setLocalOwner, } from './host';
|
|
12
12
|
import { resolvePlatform } from './platform/index';
|
|
13
|
+
import { hydrateTokenOverrides } from './theme';
|
|
13
14
|
export async function createShell(config) {
|
|
14
15
|
var _a, _b;
|
|
15
16
|
// 1. Platform detection — must run before bootstrap so state zones
|
|
@@ -23,6 +24,9 @@ export async function createShell(config) {
|
|
|
23
24
|
if (platform.localOwner) {
|
|
24
25
|
setLocalOwner();
|
|
25
26
|
}
|
|
27
|
+
// 1c. Apply persisted theme token overrides before any component mounts,
|
|
28
|
+
// so the first frame renders with the user's chosen theme.
|
|
29
|
+
hydrateTokenOverrides();
|
|
26
30
|
// 1b. Load server-discovered packages (fetched by frontend from /api/packages).
|
|
27
31
|
if ((_a = config === null || config === void 0 ? void 0 : config.discoveredPackages) === null || _a === void 0 ? void 0 : _a.length) {
|
|
28
32
|
const { loadBundleModule } = await import('./registry/loader');
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
const layout = $derived(inspectActiveLayout());
|
|
25
25
|
const regShards = $derived(Array.from(registeredShards.values()));
|
|
26
26
|
const actShards = $derived(Array.from(activeShards.keys()));
|
|
27
|
+
|
|
27
28
|
</script>
|
|
28
29
|
|
|
29
30
|
<div class="diagnostic">
|
|
@@ -68,6 +69,7 @@
|
|
|
68
69
|
{/each}
|
|
69
70
|
</ul>
|
|
70
71
|
</section>
|
|
72
|
+
|
|
71
73
|
</div>
|
|
72
74
|
|
|
73
75
|
<style>
|
|
@@ -76,7 +78,7 @@
|
|
|
76
78
|
inset: 0;
|
|
77
79
|
padding: 12px 16px;
|
|
78
80
|
overflow: auto;
|
|
79
|
-
background: var(--shell-bg);
|
|
81
|
+
background: var(--shell-grad-bg, var(--shell-bg));
|
|
80
82
|
color: var(--shell-fg);
|
|
81
83
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
82
84
|
font-size: 12px;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/*
|
|
3
|
+
* Diagnostic routes view — lists all server API routes.
|
|
4
|
+
*
|
|
5
|
+
* Fetches the route table from GET /api/routes. GET routes render
|
|
6
|
+
* as clickable links for quick testing of parameterless endpoints.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
interface ApiRoute { method: string; path: string; }
|
|
10
|
+
let routes: ApiRoute[] = $state([]);
|
|
11
|
+
let error: string | null = $state(null);
|
|
12
|
+
|
|
13
|
+
async function fetchRoutes() {
|
|
14
|
+
try {
|
|
15
|
+
const res = await fetch('/api/routes');
|
|
16
|
+
if (!res.ok) { error = `${res.status}`; return; }
|
|
17
|
+
routes = await res.json();
|
|
18
|
+
error = null;
|
|
19
|
+
} catch {
|
|
20
|
+
error = 'unavailable';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
fetchRoutes();
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<div class="diagnostic">
|
|
28
|
+
<h2>API Routes</h2>
|
|
29
|
+
|
|
30
|
+
{#if error}
|
|
31
|
+
<p class="muted">Server not reachable or route introspection unavailable ({error}).</p>
|
|
32
|
+
{:else}
|
|
33
|
+
<p class="muted">{routes.length} unique routes</p>
|
|
34
|
+
<ul>
|
|
35
|
+
{#each routes as route}
|
|
36
|
+
<li>
|
|
37
|
+
<span class="method" class:get={route.method === 'GET'}>{route.method}</span>
|
|
38
|
+
{#if route.method === 'GET'}
|
|
39
|
+
<a href={route.path} target="_blank" rel="noopener">{route.path}</a>
|
|
40
|
+
{:else}
|
|
41
|
+
<span class="path">{route.path}</span>
|
|
42
|
+
{/if}
|
|
43
|
+
</li>
|
|
44
|
+
{/each}
|
|
45
|
+
</ul>
|
|
46
|
+
{/if}
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<style>
|
|
50
|
+
.diagnostic {
|
|
51
|
+
position: absolute;
|
|
52
|
+
inset: 0;
|
|
53
|
+
padding: 12px 16px;
|
|
54
|
+
overflow: auto;
|
|
55
|
+
background: var(--shell-grad-bg, var(--shell-bg));
|
|
56
|
+
color: var(--shell-fg);
|
|
57
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
58
|
+
font-size: 12px;
|
|
59
|
+
}
|
|
60
|
+
h2 {
|
|
61
|
+
margin: 0 0 12px;
|
|
62
|
+
color: var(--shell-accent);
|
|
63
|
+
font-size: 14px;
|
|
64
|
+
}
|
|
65
|
+
.muted {
|
|
66
|
+
color: var(--shell-fg-muted);
|
|
67
|
+
margin: 0 0 8px;
|
|
68
|
+
}
|
|
69
|
+
ul {
|
|
70
|
+
margin: 0;
|
|
71
|
+
padding: 0;
|
|
72
|
+
list-style: none;
|
|
73
|
+
}
|
|
74
|
+
li {
|
|
75
|
+
margin: 0;
|
|
76
|
+
padding: 2px 0;
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: baseline;
|
|
79
|
+
gap: 8px;
|
|
80
|
+
}
|
|
81
|
+
.method {
|
|
82
|
+
min-width: 6ch;
|
|
83
|
+
color: var(--shell-fg-muted);
|
|
84
|
+
flex-shrink: 0;
|
|
85
|
+
}
|
|
86
|
+
.method.get {
|
|
87
|
+
color: var(--shell-accent);
|
|
88
|
+
}
|
|
89
|
+
a {
|
|
90
|
+
color: var(--shell-accent);
|
|
91
|
+
text-decoration: none;
|
|
92
|
+
}
|
|
93
|
+
a:hover {
|
|
94
|
+
text-decoration: underline;
|
|
95
|
+
}
|
|
96
|
+
.path {
|
|
97
|
+
color: var(--shell-fg);
|
|
98
|
+
}
|
|
99
|
+
</style>
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import { mount, unmount } from 'svelte';
|
|
27
27
|
import DiagnosticPanel from './DiagnosticPanel.svelte';
|
|
28
|
+
import DiagnosticRoutes from './DiagnosticRoutes.svelte';
|
|
28
29
|
import DiagnosticPromptModal from './DiagnosticPromptModal.svelte';
|
|
29
30
|
import { shell, getActiveApp, spliceIntoActiveLayout, inspectActiveLayout, } from '../api';
|
|
30
31
|
export const diagnosticShard = {
|
|
@@ -32,7 +33,10 @@ export const diagnosticShard = {
|
|
|
32
33
|
id: 'diagnostic',
|
|
33
34
|
label: 'Diagnostic',
|
|
34
35
|
version: '0.1.0',
|
|
35
|
-
views: [
|
|
36
|
+
views: [
|
|
37
|
+
{ id: 'diagnostic:panel', label: 'Diagnostic' },
|
|
38
|
+
{ id: 'diagnostic:routes', label: 'API Routes' },
|
|
39
|
+
],
|
|
36
40
|
},
|
|
37
41
|
activate(ctx) {
|
|
38
42
|
const factory = {
|
|
@@ -46,6 +50,17 @@ export const diagnosticShard = {
|
|
|
46
50
|
},
|
|
47
51
|
};
|
|
48
52
|
ctx.registerView('diagnostic:panel', factory);
|
|
53
|
+
const routesFactory = {
|
|
54
|
+
mount(container, _context) {
|
|
55
|
+
const instance = mount(DiagnosticRoutes, { target: container });
|
|
56
|
+
return {
|
|
57
|
+
unmount() {
|
|
58
|
+
unmount(instance);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
ctx.registerView('diagnostic:routes', routesFactory);
|
|
49
64
|
},
|
|
50
65
|
autostart(ctx) {
|
|
51
66
|
const state = ctx.state({
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
align-items: center;
|
|
47
47
|
gap: var(--shell-pad-sm);
|
|
48
48
|
padding: var(--shell-pad-sm) var(--shell-pad-md);
|
|
49
|
-
background: var(--shell-bg-elevated);
|
|
49
|
+
background: var(--shell-grad-bg-elevated, var(--shell-bg-elevated));
|
|
50
50
|
color: var(--shell-fg);
|
|
51
51
|
border: 1px solid var(--shell-accent);
|
|
52
52
|
border-radius: 3px;
|
|
@@ -186,10 +186,12 @@
|
|
|
186
186
|
/>
|
|
187
187
|
{#snippet tabBody(i: number)}
|
|
188
188
|
{@const entry = tabs.tabs[i]}
|
|
189
|
-
|
|
190
|
-
<
|
|
191
|
-
|
|
192
|
-
|
|
189
|
+
{#if entry}
|
|
190
|
+
<div class="tab-slot-wrapper">
|
|
191
|
+
<SlotContainer node={{ type: 'slot', slotId: entry.slotId, viewId: entry.viewId }} label={entry.label} />
|
|
192
|
+
<SlotDropZone path={path} />
|
|
193
|
+
</div>
|
|
194
|
+
{/if}
|
|
193
195
|
{/snippet}
|
|
194
196
|
{:else if tabs?.persistent}
|
|
195
197
|
<div class="empty-tabs-placeholder">
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
<style>
|
|
74
74
|
.popup-frame {
|
|
75
75
|
position: absolute;
|
|
76
|
-
background: var(--shell-bg-elevated);
|
|
76
|
+
background: var(--shell-grad-bg-elevated, var(--shell-bg-elevated));
|
|
77
77
|
color: var(--shell-fg);
|
|
78
78
|
border: 1px solid var(--shell-border-strong);
|
|
79
79
|
border-radius: 3px;
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
align-items: center;
|
|
45
45
|
gap: var(--shell-pad-md);
|
|
46
46
|
padding: var(--shell-pad-sm) var(--shell-pad-md);
|
|
47
|
-
background: var(--shell-bg-elevated);
|
|
47
|
+
background: var(--shell-grad-bg-elevated, var(--shell-bg-elevated));
|
|
48
48
|
color: var(--shell-fg);
|
|
49
49
|
border: 1px solid var(--shell-border-strong);
|
|
50
50
|
border-left-width: 3px;
|
|
@@ -269,7 +269,7 @@
|
|
|
269
269
|
display: flex;
|
|
270
270
|
align-items: center;
|
|
271
271
|
justify-content: center;
|
|
272
|
-
background: var(--shell-bg-elevated);
|
|
272
|
+
background: var(--shell-grad-bg-elevated, var(--shell-bg-elevated));
|
|
273
273
|
border: none;
|
|
274
274
|
color: var(--shell-fg-muted);
|
|
275
275
|
cursor: pointer;
|
|
@@ -206,7 +206,7 @@
|
|
|
206
206
|
height: 100%;
|
|
207
207
|
min-width: 0;
|
|
208
208
|
min-height: 0;
|
|
209
|
-
background: var(--shell-bg);
|
|
209
|
+
background: var(--shell-grad-bg, var(--shell-bg));
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
.tab-strip {
|
|
@@ -214,7 +214,7 @@
|
|
|
214
214
|
flex: 0 0 auto;
|
|
215
215
|
display: flex;
|
|
216
216
|
gap: 1px;
|
|
217
|
-
background: var(--shell-bg-sunken);
|
|
217
|
+
background: var(--shell-grad-bg-sunken, var(--shell-bg-sunken));
|
|
218
218
|
border-bottom: 1px solid var(--shell-border);
|
|
219
219
|
padding: 0 var(--shell-pad-sm);
|
|
220
220
|
user-select: none;
|
|
@@ -243,11 +243,11 @@
|
|
|
243
243
|
}
|
|
244
244
|
.tab:hover {
|
|
245
245
|
color: var(--shell-fg);
|
|
246
|
-
background: var(--shell-bg-elevated);
|
|
246
|
+
background: var(--shell-grad-bg-elevated, var(--shell-bg-elevated));
|
|
247
247
|
}
|
|
248
248
|
.tab.active {
|
|
249
249
|
color: var(--shell-fg);
|
|
250
|
-
background: var(--shell-bg);
|
|
250
|
+
background: var(--shell-grad-bg, var(--shell-bg));
|
|
251
251
|
border-top-color: var(--shell-accent);
|
|
252
252
|
}
|
|
253
253
|
.tab-icon { font-size: 11px; }
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A server shard is the optional backend counterpart to a client shard.
|
|
5
5
|
* It runs in Node inside sh3-server and declares routes that are mounted
|
|
6
|
-
* under `/
|
|
6
|
+
* under `/api/<shard-id>/`. Server shards have full Node access — filesystem,
|
|
7
7
|
* child_process, network, etc. — and are trusted by the admin who installed
|
|
8
8
|
* them.
|
|
9
9
|
*
|
|
@@ -39,8 +39,8 @@ export interface ServerShard {
|
|
|
39
39
|
id: string;
|
|
40
40
|
/**
|
|
41
41
|
* Called once at mount time. Register Hono routes on the provided router.
|
|
42
|
-
* Routes are relative to `/
|
|
43
|
-
* becomes `GET /
|
|
42
|
+
* Routes are relative to `/api/<shard-id>/` — e.g. `router.get('/data', ...)`
|
|
43
|
+
* becomes `GET /api/<shard-id>/data`.
|
|
44
44
|
*
|
|
45
45
|
* May be async if the shard needs to initialise resources before serving.
|
|
46
46
|
*/
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A server shard is the optional backend counterpart to a client shard.
|
|
5
5
|
* It runs in Node inside sh3-server and declares routes that are mounted
|
|
6
|
-
* under `/
|
|
6
|
+
* under `/api/<shard-id>/`. Server shards have full Node access — filesystem,
|
|
7
7
|
* child_process, network, etc. — and are trusted by the admin who installed
|
|
8
8
|
* them.
|
|
9
9
|
*
|
package/dist/shards/types.d.ts
CHANGED
|
@@ -98,7 +98,7 @@ export interface ShardManifest {
|
|
|
98
98
|
/**
|
|
99
99
|
* Optional filename of a server-side bundle for this shard. When present,
|
|
100
100
|
* sh3-server loads the bundle at boot and mounts its routes at
|
|
101
|
-
* `/
|
|
101
|
+
* `/api/<shard-id>/`. The server bundle runs in Node with full access.
|
|
102
102
|
* Only relevant for shards installed via the package store; framework-
|
|
103
103
|
* shipped shards do not use this field.
|
|
104
104
|
*/
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
<section class="shell-home-section">
|
|
98
98
|
<h2 class="shell-home-section-title">Admin Mode</h2>
|
|
99
99
|
<p class="shell-home-elevate-hint">
|
|
100
|
-
|
|
100
|
+
Elevate Permissions
|
|
101
101
|
</p>
|
|
102
102
|
<form class="shell-home-elevate-form" onsubmit={(e) => { e.preventDefault(); handleElevate(); }}>
|
|
103
103
|
<input
|
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
justify-content: flex-start;
|
|
137
137
|
padding: 48px 24px;
|
|
138
138
|
overflow: auto;
|
|
139
|
-
background: var(--shell-bg);
|
|
139
|
+
background: var(--shell-grad-bg, var(--shell-bg));
|
|
140
140
|
color: var(--shell-fg);
|
|
141
141
|
font-family: system-ui, sans-serif;
|
|
142
142
|
}
|
|
@@ -196,7 +196,7 @@
|
|
|
196
196
|
gap: 4px 16px;
|
|
197
197
|
align-items: center;
|
|
198
198
|
padding: 14px 18px;
|
|
199
|
-
background: var(--shell-bg-elevated);
|
|
199
|
+
background: var(--shell-grad-bg-elevated, var(--shell-bg-elevated));
|
|
200
200
|
border: 1px solid var(--shell-border);
|
|
201
201
|
border-radius: 6px;
|
|
202
202
|
}
|
|
@@ -250,7 +250,7 @@
|
|
|
250
250
|
.shell-home-key-input {
|
|
251
251
|
flex: 1;
|
|
252
252
|
padding: 8px 12px;
|
|
253
|
-
background: var(--shell-bg-elevated);
|
|
253
|
+
background: var(--shell-grad-bg-elevated, var(--shell-bg-elevated));
|
|
254
254
|
color: var(--shell-fg);
|
|
255
255
|
border: 1px solid var(--shell-border);
|
|
256
256
|
border-radius: 4px;
|
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apply CSS token overrides to :root and persist to User zone.
|
|
3
|
+
*
|
|
4
|
+
* Keys are token names without the `--` prefix (e.g. `'shell-accent'`).
|
|
5
|
+
* Values are any valid CSS value string.
|
|
6
|
+
*
|
|
7
|
+
* Calling this replaces ALL previous overrides — tokens not present in
|
|
8
|
+
* the new map are removed from :root.
|
|
9
|
+
*/
|
|
10
|
+
export declare function setTokenOverrides(overrides: Record<string, string>): void;
|
|
11
|
+
/**
|
|
12
|
+
* Remove all token overrides from :root and clear persisted state.
|
|
13
|
+
* The shell reverts to the default tokens defined in tokens.css.
|
|
14
|
+
*/
|
|
15
|
+
export declare function clearTokenOverrides(): void;
|
|
16
|
+
/**
|
|
17
|
+
* Read the currently persisted token overrides.
|
|
18
|
+
* Returns an empty object if no overrides are stored.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getTokenOverrides(): Record<string, string>;
|
|
21
|
+
/**
|
|
22
|
+
* Apply persisted token overrides to :root. Called once during shell boot
|
|
23
|
+
* before any component mounts, so the themed colors are visible from the
|
|
24
|
+
* first frame.
|
|
25
|
+
*
|
|
26
|
+
* This is framework-internal — not part of the shard-facing API.
|
|
27
|
+
*/
|
|
28
|
+
export declare function hydrateTokenOverrides(): void;
|
package/dist/theme.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Token override API — allows external packages (like sh3-style) to
|
|
3
|
+
* dynamically change shell CSS tokens and persist the selection to
|
|
4
|
+
* the User zone.
|
|
5
|
+
*
|
|
6
|
+
* The shell reads persisted overrides at boot (see createShell.ts) so
|
|
7
|
+
* themes survive page reloads. This module provides the write path.
|
|
8
|
+
*/
|
|
9
|
+
const STORAGE_KEY = 'sh3:user:__shell__:theme';
|
|
10
|
+
/** Keys currently set on :root by setTokenOverrides, tracked for clearTokenOverrides. */
|
|
11
|
+
let appliedKeys = [];
|
|
12
|
+
/**
|
|
13
|
+
* Apply CSS token overrides to :root and persist to User zone.
|
|
14
|
+
*
|
|
15
|
+
* Keys are token names without the `--` prefix (e.g. `'shell-accent'`).
|
|
16
|
+
* Values are any valid CSS value string.
|
|
17
|
+
*
|
|
18
|
+
* Calling this replaces ALL previous overrides — tokens not present in
|
|
19
|
+
* the new map are removed from :root.
|
|
20
|
+
*/
|
|
21
|
+
export function setTokenOverrides(overrides) {
|
|
22
|
+
// Remove previously applied keys that are not in the new set
|
|
23
|
+
const newKeys = Object.keys(overrides);
|
|
24
|
+
for (const key of appliedKeys) {
|
|
25
|
+
if (!newKeys.includes(key)) {
|
|
26
|
+
document.documentElement.style.removeProperty(`--${key}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Apply new overrides
|
|
30
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
31
|
+
document.documentElement.style.setProperty(`--${key}`, value);
|
|
32
|
+
}
|
|
33
|
+
appliedKeys = newKeys;
|
|
34
|
+
// Persist
|
|
35
|
+
try {
|
|
36
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(overrides));
|
|
37
|
+
}
|
|
38
|
+
catch (_a) {
|
|
39
|
+
// Storage full or unavailable — theme applies for this session only
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Remove all token overrides from :root and clear persisted state.
|
|
44
|
+
* The shell reverts to the default tokens defined in tokens.css.
|
|
45
|
+
*/
|
|
46
|
+
export function clearTokenOverrides() {
|
|
47
|
+
for (const key of appliedKeys) {
|
|
48
|
+
document.documentElement.style.removeProperty(`--${key}`);
|
|
49
|
+
}
|
|
50
|
+
appliedKeys = [];
|
|
51
|
+
try {
|
|
52
|
+
localStorage.removeItem(STORAGE_KEY);
|
|
53
|
+
}
|
|
54
|
+
catch (_a) {
|
|
55
|
+
// Ignore
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Read the currently persisted token overrides.
|
|
60
|
+
* Returns an empty object if no overrides are stored.
|
|
61
|
+
*/
|
|
62
|
+
export function getTokenOverrides() {
|
|
63
|
+
try {
|
|
64
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
65
|
+
if (!raw)
|
|
66
|
+
return {};
|
|
67
|
+
const parsed = JSON.parse(raw);
|
|
68
|
+
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
|
69
|
+
return parsed;
|
|
70
|
+
}
|
|
71
|
+
return {};
|
|
72
|
+
}
|
|
73
|
+
catch (_a) {
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Apply persisted token overrides to :root. Called once during shell boot
|
|
79
|
+
* before any component mounts, so the themed colors are visible from the
|
|
80
|
+
* first frame.
|
|
81
|
+
*
|
|
82
|
+
* This is framework-internal — not part of the shard-facing API.
|
|
83
|
+
*/
|
|
84
|
+
export function hydrateTokenOverrides() {
|
|
85
|
+
const overrides = getTokenOverrides();
|
|
86
|
+
if (Object.keys(overrides).length === 0)
|
|
87
|
+
return;
|
|
88
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
89
|
+
document.documentElement.style.setProperty(`--${key}`, value);
|
|
90
|
+
}
|
|
91
|
+
appliedKeys = Object.keys(overrides);
|
|
92
|
+
}
|
package/dist/tokens.css
CHANGED
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
--shell-bg: #1a1b1e;
|
|
12
12
|
--shell-bg-elevated: #22232a;
|
|
13
13
|
--shell-bg-sunken: #141518;
|
|
14
|
+
|
|
15
|
+
/* Gradient layer — empty by default, overrides surface when set */
|
|
16
|
+
--shell-grad-bg: ;
|
|
17
|
+
--shell-grad-bg-elevated: ;
|
|
18
|
+
--shell-grad-bg-sunken: ;
|
|
19
|
+
|
|
14
20
|
--shell-border: #2e3038;
|
|
15
21
|
--shell-border-strong: #3c3f4a;
|
|
16
22
|
|
|
@@ -66,7 +72,7 @@ body {
|
|
|
66
72
|
padding: 0;
|
|
67
73
|
height: 100%;
|
|
68
74
|
overflow: hidden; /* SH3 is a shell, not a scrollable document */
|
|
69
|
-
background: var(--shell-bg);
|
|
75
|
+
background: var(--shell-grad-bg, var(--shell-bg));
|
|
70
76
|
color: var(--shell-fg);
|
|
71
77
|
font-family: var(--shell-font-ui);
|
|
72
78
|
font-size: var(--shell-font-size);
|