sh3-core 0.5.2 → 0.5.5

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.

Potentially problematic release.


This version of sh3-core might be problematic. Click here for more details.

Files changed (54) hide show
  1. package/dist/Shell.svelte +6 -3
  2. package/dist/admin/AuthSettingsView.svelte +105 -0
  3. package/dist/admin/AuthSettingsView.svelte.d.ts +3 -0
  4. package/dist/admin/SystemView.svelte +73 -0
  5. package/dist/admin/SystemView.svelte.d.ts +3 -0
  6. package/dist/admin/UsersView.svelte +189 -0
  7. package/dist/admin/UsersView.svelte.d.ts +3 -0
  8. package/dist/admin/adminApp.d.ts +7 -0
  9. package/dist/admin/adminApp.js +24 -0
  10. package/dist/admin/adminShard.svelte.d.ts +4 -0
  11. package/dist/admin/adminShard.svelte.js +52 -0
  12. package/dist/api.d.ts +2 -1
  13. package/dist/api.js +1 -1
  14. package/dist/apps/lifecycle.d.ts +6 -1
  15. package/dist/apps/lifecycle.js +28 -4
  16. package/dist/apps/registry.svelte.d.ts +5 -2
  17. package/dist/apps/registry.svelte.js +6 -7
  18. package/dist/apps/types.d.ts +13 -0
  19. package/dist/auth/GuestBanner.svelte +144 -0
  20. package/dist/auth/GuestBanner.svelte.d.ts +3 -0
  21. package/dist/auth/SignInWall.svelte +213 -0
  22. package/dist/auth/SignInWall.svelte.d.ts +8 -0
  23. package/dist/auth/auth.svelte.d.ts +42 -31
  24. package/dist/auth/auth.svelte.js +106 -89
  25. package/dist/auth/index.d.ts +2 -1
  26. package/dist/auth/index.js +1 -1
  27. package/dist/auth/types.d.ts +41 -0
  28. package/dist/auth/types.js +6 -0
  29. package/dist/build.js +70 -16
  30. package/dist/createShell.d.ts +2 -2
  31. package/dist/createShell.js +78 -33
  32. package/dist/diagnostic/DiagnosticPromptModal.svelte +1 -1
  33. package/dist/host-entry.d.ts +2 -1
  34. package/dist/host-entry.js +2 -2
  35. package/dist/host.d.ts +0 -2
  36. package/dist/host.js +11 -25
  37. package/dist/layout/DragPreview.svelte +1 -1
  38. package/dist/overlays/ModalFrame.svelte +1 -1
  39. package/dist/overlays/PopupFrame.svelte +1 -1
  40. package/dist/overlays/ToastItem.svelte +1 -1
  41. package/dist/primitives/TabbedPanel.svelte +1 -1
  42. package/dist/registry/installer.js +0 -2
  43. package/dist/shards/activate.svelte.d.ts +13 -6
  44. package/dist/shards/activate.svelte.js +19 -8
  45. package/dist/shards/types.d.ts +11 -0
  46. package/dist/shell-shard/ShellHome.svelte +32 -118
  47. package/dist/store/InstalledView.svelte +7 -7
  48. package/dist/store/StoreView.svelte +16 -16
  49. package/dist/store/storeApp.js +1 -1
  50. package/dist/store/storeShard.svelte.js +5 -4
  51. package/dist/tokens.css +14 -0
  52. package/dist/version.d.ts +1 -1
  53. package/dist/version.js +1 -1
  54. package/package.json +1 -1
package/dist/host.js CHANGED
@@ -22,10 +22,12 @@ import { shellShard } from './shell-shard/shellShard.svelte';
22
22
  import { storeShard } from './store/storeShard.svelte';
23
23
  import { __setBackend } from './state/zones.svelte';
24
24
  import { loadInstalledPackages } from './registry/installer';
25
- import { initAuth, isAdmin, setLocalOwner } from './auth/index';
25
+ import { setLocalOwner } from './auth/index';
26
26
  import { storeApp } from './store/storeApp';
27
27
  import { diagnosticShard } from './diagnostic/diagnosticShard.svelte';
28
28
  import { diagnosticApp } from './diagnostic/diagnosticApp';
29
+ import { adminShard } from './admin/adminShard.svelte';
30
+ import { adminApp } from './admin/adminApp';
29
31
  export { __setBackend };
30
32
  export { setLocalOwner };
31
33
  export { __setTenantId, __setDocumentBackend } from './documents/config';
@@ -35,43 +37,27 @@ export function registerShard(shard) {
35
37
  export { registerApp };
36
38
  export async function bootstrap(config) {
37
39
  const exShards = new Set(config === null || config === void 0 ? void 0 : config.excludeShards);
38
- const exApps = new Set(config === null || config === void 0 ? void 0 : config.excludeApps);
39
- // 1. Framework-owned shards registered first so installed packages
40
- // cannot claim reserved IDs like __shell__ or sh3-store.
41
- const frameworkShards = [shellShard, storeShard, diagnosticShard];
40
+ // 1. Framework-owned shards
41
+ const frameworkShards = [shellShard, storeShard, diagnosticShard, adminShard];
42
42
  for (const shard of frameworkShards) {
43
43
  if (!exShards.has(shard.manifest.id)) {
44
44
  registerShardInternal(shard);
45
45
  }
46
46
  }
47
- // 2. Framework-shipped admin apps.
48
- const frameworkApps = [storeApp, diagnosticApp];
47
+ // 2. Framework-shipped apps
48
+ const frameworkApps = [storeApp, diagnosticApp, adminApp];
49
49
  for (const app of frameworkApps) {
50
- if (!exApps.has(app.manifest.id)) {
51
- registerApp(app);
52
- }
53
- }
54
- // 3. Auth — if the host already called setLocalOwner() (Tauri / dev),
55
- // skip server verification. Otherwise verify the stored admin key
56
- // against the server (3s timeout, fails open).
57
- if (!isAdmin()) {
58
- await initAuth();
50
+ registerApp(app);
59
51
  }
60
- // 4. Load any packages that were hot-installed in a previous session
61
- // from IndexedDB. Runs after framework shards but before activation
62
- // so installed packages participate in the self-starting pass.
52
+ // 3. Load any packages installed in a previous session from IndexedDB
63
53
  await loadInstalledPackages();
64
- // 5. Activate every self-starting shard (any shard whose `autostart`
65
- // field is defined). Iteration order is insertion order, which
66
- // puts __shell__ first and glob-discovered shards after — exactly
67
- // what we need.
54
+ // 4. Activate every self-starting shard
68
55
  for (const [id, shard] of registeredShards) {
69
56
  if (shard.autostart) {
70
57
  await activateShard(id);
71
58
  }
72
59
  }
73
- // 6. Read the last-active app from the user zone. If it names a still-
74
- // registered app, launch it; otherwise leave the shell on home.
60
+ // 5. Read the last-active app from the user zone
75
61
  const lastId = readLastApp();
76
62
  if (lastId && registeredApps.has(lastId)) {
77
63
  await launchApp(lastId);
@@ -49,7 +49,7 @@
49
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
- border-radius: 3px;
52
+ border-radius: var(--shell-radius-sm);
53
53
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
54
54
  font-size: 12px;
55
55
  font-family: var(--shell-font-ui);
@@ -76,7 +76,7 @@
76
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
- border-radius: 4px;
79
+ border-radius: var(--shell-radius);
80
80
  min-width: 320px;
81
81
  max-width: min(640px, 90vw);
82
82
  max-height: 90vh;
@@ -76,7 +76,7 @@
76
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
- border-radius: 3px;
79
+ border-radius: var(--shell-radius-sm);
80
80
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
81
81
  min-width: 120px;
82
82
  outline: none;
@@ -48,7 +48,7 @@
48
48
  color: var(--shell-fg);
49
49
  border: 1px solid var(--shell-border-strong);
50
50
  border-left-width: 3px;
51
- border-radius: 3px;
51
+ border-radius: var(--shell-radius-sm);
52
52
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
53
53
  font-size: 12px;
54
54
  min-width: 220px;
@@ -265,7 +265,7 @@
265
265
  font-size: 10px;
266
266
  line-height: 1;
267
267
  padding: 2px;
268
- border-radius: 3px;
268
+ border-radius: var(--shell-radius-sm);
269
269
  color: var(--shell-fg-muted);
270
270
  cursor: pointer;
271
271
  flex-shrink: 0;
@@ -97,8 +97,6 @@ export async function installPackage(bundle, meta) {
97
97
  registerApp(app);
98
98
  }
99
99
  catch (err) {
100
- // Registration failure (e.g. duplicate id) -- the package is persisted
101
- // but not usable until reload. Log and continue.
102
100
  console.warn(`[sh3] Package "${meta.id}" installed but registration failed (will retry on next boot):`, err instanceof Error ? err.message : err);
103
101
  hotLoaded = false;
104
102
  }
@@ -1,4 +1,4 @@
1
- import type { Shard } from './types';
1
+ import type { Shard, ShardContext } from './types';
2
2
  /**
3
3
  * Reactive registry of every shard known to the host. Keys are shard ids.
4
4
  * Populated once at boot by the glob-discovery loop in main.ts (through
@@ -10,12 +10,14 @@ import type { Shard } from './types';
10
10
  export declare const registeredShards: Map<string, Shard>;
11
11
  export declare const activeShards: Map<string, Shard>;
12
12
  /**
13
- * Register a shard with the framework so it can later be activated. Records
14
- * the shard in `registeredShards` but does not run `activate` — that happens
15
- * at app launch (or immediately for self-starting shards).
13
+ * Register (or re-register) a shard with the framework so it can later be
14
+ * activated. Records the shard in `registeredShards` but does not run
15
+ * `activate` — that happens at app launch (or for self-starting shards).
16
16
  *
17
- * @param shard - The shard module to register. `shard.manifest.id` must be unique.
18
- * @throws If a shard with the same id is already registered.
17
+ * If a shard with the same id already exists it is silently replaced,
18
+ * which is the expected path during package updates. If the old shard was
19
+ * active it is deactivated first so the new version can be cleanly
20
+ * activated on next launch.
19
21
  */
20
22
  export declare function registerShard(shard: Shard): void;
21
23
  /**
@@ -43,3 +45,8 @@ export declare function deactivateShard(id: string): void;
43
45
  * @param id - The `ShardManifest.id` to check.
44
46
  */
45
47
  export declare function isActive(id: string): boolean;
48
+ /**
49
+ * Return the ShardContext for an active shard, or undefined if not active.
50
+ * Used by lifecycle.ts to pass context to `shard.resume()`.
51
+ */
52
+ export declare function getShardContext(id: string): ShardContext | undefined;
@@ -39,17 +39,19 @@ export const registeredShards = $state(new Map());
39
39
  const active = new Map();
40
40
  export const activeShards = $state(new Map());
41
41
  /**
42
- * Register a shard with the framework so it can later be activated. Records
43
- * the shard in `registeredShards` but does not run `activate` — that happens
44
- * at app launch (or immediately for self-starting shards).
42
+ * Register (or re-register) a shard with the framework so it can later be
43
+ * activated. Records the shard in `registeredShards` but does not run
44
+ * `activate` — that happens at app launch (or for self-starting shards).
45
45
  *
46
- * @param shard - The shard module to register. `shard.manifest.id` must be unique.
47
- * @throws If a shard with the same id is already registered.
46
+ * If a shard with the same id already exists it is silently replaced,
47
+ * which is the expected path during package updates. If the old shard was
48
+ * active it is deactivated first so the new version can be cleanly
49
+ * activated on next launch.
48
50
  */
49
51
  export function registerShard(shard) {
50
52
  const id = shard.manifest.id;
51
- if (registeredShards.has(id)) {
52
- throw new Error(`Shard "${id}" is already registered`);
53
+ if (registeredShards.has(id) && activeShards.has(id)) {
54
+ deactivateShard(id);
53
55
  }
54
56
  registeredShards.set(id, shard);
55
57
  }
@@ -73,7 +75,7 @@ export async function activateShard(id) {
73
75
  // and is now being required by an app). Idempotent — no error.
74
76
  return;
75
77
  }
76
- const entry = { shard, viewIds: new Set(), cleanupFns: [] };
78
+ const entry = { shard, ctx: undefined, viewIds: new Set(), cleanupFns: [] };
77
79
  // envState holds the reactive env data for this shard.
78
80
  // Must be declared with $state at variable declaration time (Svelte 5 rule).
79
81
  const envState = $state({
@@ -122,6 +124,7 @@ export async function activateShard(id) {
122
124
  ? createZoneManager()
123
125
  : undefined,
124
126
  };
127
+ entry.ctx = ctx;
125
128
  active.set(id, entry);
126
129
  activeShards.set(id, shard);
127
130
  await shard.activate(ctx);
@@ -173,3 +176,11 @@ export function deactivateShard(id) {
173
176
  export function isActive(id) {
174
177
  return active.has(id);
175
178
  }
179
+ /**
180
+ * Return the ShardContext for an active shard, or undefined if not active.
181
+ * Used by lifecycle.ts to pass context to `shard.resume()`.
182
+ */
183
+ export function getShardContext(id) {
184
+ var _a;
185
+ return (_a = active.get(id)) === null || _a === void 0 ? void 0 : _a.ctx;
186
+ }
@@ -193,4 +193,15 @@ export interface Shard {
193
193
  autostart?(ctx: ShardContext): void | Promise<void>;
194
194
  /** Optional cleanup hook called when the shard is deactivated. Release timers, subscriptions, and external resources here. */
195
195
  deactivate?(): void | Promise<void>;
196
+ /**
197
+ * Called when the owning app is suspended (Home button). The shard
198
+ * remains active; its views and state are preserved. Return `false`
199
+ * (sync or async) to cancel the navigation.
200
+ */
201
+ suspend?(): void | false | Promise<void | false>;
202
+ /**
203
+ * Called when the owning app resumes from Home. Receives the same
204
+ * `ShardContext` that `activate` received.
205
+ */
206
+ resume?(ctx: ShardContext): void | Promise<void>;
196
207
  }
@@ -2,37 +2,18 @@
2
2
  /*
3
3
  * Shell home — the view shown when no app is active. Sections:
4
4
  * 1. User apps — always visible
5
- * 2. Admin apps — visible when elevated (admin mode)
6
- * 3. Elevate prompt — shown when not elevated
5
+ * 2. Admin apps — visible when user has admin role
7
6
  */
8
7
 
9
8
  import { listRegisteredApps, launchApp, isAdmin, VERSION } from '../api';
10
- import { elevate, deescalate } from '../auth/index';
9
+ import { logout, isAuthenticated, getUser } from '../auth/index';
11
10
 
12
11
  const apps = $derived(listRegisteredApps());
13
12
  const userApps = $derived(apps.filter(m => !m.admin));
14
13
  const adminApps = $derived(apps.filter(m => m.admin));
15
14
  const elevated = $derived(isAdmin());
16
-
17
- let keyInput = $state('');
18
- let elevateError = $state<string | null>(null);
19
- let elevating = $state(false);
20
-
21
- async function handleElevate() {
22
- if (!keyInput.trim() || elevating) return;
23
- elevating = true;
24
- elevateError = null;
25
- const ok = await elevate(keyInput.trim());
26
- if (!ok) {
27
- elevateError = 'Invalid API key';
28
- }
29
- keyInput = '';
30
- elevating = false;
31
- }
32
-
33
- function handleDeescalate() {
34
- deescalate();
35
- }
15
+ const authenticated = $derived(isAuthenticated());
16
+ const user = $derived(getUser());
36
17
  </script>
37
18
 
38
19
  <div class="shell-home">
@@ -40,9 +21,10 @@
40
21
  <h1>SH3</h1>
41
22
  <span class="shell-home-version">v{VERSION}</span>
42
23
  <span class="shell-home-alpha">alpha</span>
43
- {#if elevated}
44
- <button type="button" class="shell-home-deescalate" onclick={handleDeescalate}>
45
- Exit admin mode
24
+ {#if authenticated && user}
25
+ <span class="shell-home-user">{user.displayName}</span>
26
+ <button type="button" class="shell-home-deescalate" onclick={() => logout()}>
27
+ Sign out
46
28
  </button>
47
29
  {/if}
48
30
  </header>
@@ -70,54 +52,26 @@
70
52
  </section>
71
53
  {/if}
72
54
 
73
- {#if elevated}
74
- {#if adminApps.length > 0}
75
- <section class="shell-home-section">
76
- <h2 class="shell-home-section-title">Admin</h2>
77
- <ul class="shell-home-list">
78
- {#each adminApps as manifest (manifest.id)}
79
- <li class="shell-home-entry">
80
- <div class="shell-home-entry-label">{manifest.label}</div>
81
- <div class="shell-home-entry-meta">
82
- {manifest.id} · v{manifest.version}
83
- </div>
84
- <button
85
- type="button"
86
- class="shell-home-launch"
87
- onclick={() => launchApp(manifest.id)}
88
- >
89
- Launch
90
- </button>
91
- </li>
92
- {/each}
93
- </ul>
94
- </section>
95
- {/if}
96
- {:else}
55
+ {#if elevated && adminApps.length > 0}
97
56
  <section class="shell-home-section">
98
- <h2 class="shell-home-section-title">Admin Mode</h2>
99
- <p class="shell-home-elevate-hint">
100
- Elevate Permissions
101
- </p>
102
- <form class="shell-home-elevate-form" onsubmit={(e) => { e.preventDefault(); handleElevate(); }}>
103
- <input
104
- class="shell-home-key-input"
105
- type="password"
106
- placeholder="API key"
107
- bind:value={keyInput}
108
- disabled={elevating}
109
- />
110
- <button
111
- type="submit"
112
- class="shell-home-elevate-btn"
113
- disabled={elevating || !keyInput.trim()}
114
- >
115
- {elevating ? 'Verifying...' : 'Elevate'}
116
- </button>
117
- </form>
118
- {#if elevateError}
119
- <div class="shell-home-elevate-error">{elevateError}</div>
120
- {/if}
57
+ <h2 class="shell-home-section-title">Admin</h2>
58
+ <ul class="shell-home-list">
59
+ {#each adminApps as manifest (manifest.id)}
60
+ <li class="shell-home-entry">
61
+ <div class="shell-home-entry-label">{manifest.label}</div>
62
+ <div class="shell-home-entry-meta">
63
+ {manifest.id} · v{manifest.version}
64
+ </div>
65
+ <button
66
+ type="button"
67
+ class="shell-home-launch"
68
+ onclick={() => launchApp(manifest.id)}
69
+ >
70
+ Launch
71
+ </button>
72
+ </li>
73
+ {/each}
74
+ </ul>
121
75
  </section>
122
76
  {/if}
123
77
 
@@ -203,7 +157,7 @@
203
157
  padding: 14px 18px;
204
158
  background: var(--shell-grad-bg-elevated, var(--shell-bg-elevated));
205
159
  border: 1px solid var(--shell-border);
206
- border-radius: 6px;
160
+ border-radius: var(--shell-radius-md);
207
161
  }
208
162
  .shell-home-entry-label {
209
163
  grid-column: 1;
@@ -223,7 +177,7 @@
223
177
  background: var(--shell-accent);
224
178
  color: var(--shell-bg);
225
179
  border: none;
226
- border-radius: 4px;
180
+ border-radius: var(--shell-radius);
227
181
  font-weight: 600;
228
182
  cursor: pointer;
229
183
  }
@@ -235,7 +189,7 @@
235
189
  background: transparent;
236
190
  color: var(--shell-fg-subtle);
237
191
  border: 1px solid var(--shell-border);
238
- border-radius: 4px;
192
+ border-radius: var(--shell-radius);
239
193
  cursor: pointer;
240
194
  font-size: 12px;
241
195
  }
@@ -243,48 +197,8 @@
243
197
  color: var(--shell-fg);
244
198
  border-color: var(--shell-fg-subtle);
245
199
  }
246
- .shell-home-elevate-hint {
247
- margin: 0 0 12px;
200
+ .shell-home-user {
248
201
  font-size: 13px;
249
- color: var(--shell-fg-muted);
250
- }
251
- .shell-home-elevate-form {
252
- display: flex;
253
- gap: 8px;
254
- }
255
- .shell-home-key-input {
256
- flex: 1;
257
- padding: 8px 12px;
258
- background: var(--shell-grad-bg-elevated, var(--shell-bg-elevated));
259
- color: var(--shell-fg);
260
- border: 1px solid var(--shell-border);
261
- border-radius: 4px;
262
- font-family: monospace;
263
- font-size: 13px;
264
- }
265
- .shell-home-key-input::placeholder {
266
- color: var(--shell-fg-muted);
267
- }
268
- .shell-home-elevate-btn {
269
- padding: 8px 16px;
270
- background: var(--shell-accent);
271
- color: var(--shell-bg);
272
- border: none;
273
- border-radius: 4px;
274
- font-weight: 600;
275
- cursor: pointer;
276
- white-space: nowrap;
277
- }
278
- .shell-home-elevate-btn:disabled {
279
- opacity: 0.6;
280
- cursor: not-allowed;
281
- }
282
- .shell-home-elevate-error {
283
- margin-top: 8px;
284
- padding: 6px 10px;
285
- font-size: 12px;
286
- color: var(--shell-error, #d32f2f);
287
- background: color-mix(in srgb, var(--shell-error, #d32f2f) 10%, transparent);
288
- border-radius: 4px;
202
+ color: var(--shell-fg-subtle);
289
203
  }
290
204
  </style>
@@ -125,7 +125,7 @@
125
125
 
126
126
  <style>
127
127
  .installed-view {
128
- font-family: var(--shell-font, system-ui, sans-serif);
128
+ font-family: var(--shell-font-ui);
129
129
  color: var(--shell-fg, #e0e0e0);
130
130
  background: var(--shell-bg, #1e1e1e);
131
131
  padding: 16px;
@@ -149,7 +149,7 @@
149
149
  background: var(--shell-accent, #007acc);
150
150
  color: #fff;
151
151
  border: none;
152
- border-radius: 4px;
152
+ border-radius: var(--shell-radius);
153
153
  cursor: pointer;
154
154
  font-family: inherit;
155
155
  font-size: 0.875rem;
@@ -171,7 +171,7 @@
171
171
  .installed-item {
172
172
  background: var(--shell-input-bg, #2a2a2a);
173
173
  border: 1px solid var(--shell-border, #444);
174
- border-radius: 6px;
174
+ border-radius: var(--shell-radius-md);
175
175
  padding: 12px 14px;
176
176
  display: flex;
177
177
  flex-direction: column;
@@ -189,7 +189,7 @@
189
189
  .installed-item-badge {
190
190
  font-size: 0.6875rem;
191
191
  padding: 1px 6px;
192
- border-radius: 3px;
192
+ border-radius: var(--shell-radius-sm);
193
193
  text-transform: uppercase;
194
194
  font-weight: 600;
195
195
  letter-spacing: 0.04em;
@@ -223,7 +223,7 @@
223
223
  background: transparent;
224
224
  color: var(--shell-error, #d32f2f);
225
225
  border: 1px solid var(--shell-error, #d32f2f);
226
- border-radius: 4px;
226
+ border-radius: var(--shell-radius);
227
227
  cursor: pointer;
228
228
  font-family: inherit;
229
229
  font-size: 0.8125rem;
@@ -240,7 +240,7 @@
240
240
  background: var(--shell-warning, #ff9800);
241
241
  color: #fff;
242
242
  border: none;
243
- border-radius: 4px;
243
+ border-radius: var(--shell-radius);
244
244
  cursor: pointer;
245
245
  font-family: inherit;
246
246
  font-size: 0.8125rem;
@@ -258,7 +258,7 @@
258
258
  background: color-mix(in srgb, var(--shell-error, #d32f2f) 15%, transparent);
259
259
  color: var(--shell-error, #d32f2f);
260
260
  border: 1px solid var(--shell-error, #d32f2f);
261
- border-radius: 4px;
261
+ border-radius: var(--shell-radius);
262
262
  font-size: 0.8125rem;
263
263
  }
264
264
  </style>
@@ -273,7 +273,7 @@
273
273
 
274
274
  <style>
275
275
  .store-view {
276
- font-family: var(--shell-font, system-ui, sans-serif);
276
+ font-family: var(--shell-font-ui);
277
277
  color: var(--shell-fg, #e0e0e0);
278
278
  background: var(--shell-bg, #1e1e1e);
279
279
  padding: 16px;
@@ -301,7 +301,7 @@
301
301
  background: var(--shell-input-bg, #2a2a2a);
302
302
  color: var(--shell-fg, #e0e0e0);
303
303
  border: 1px solid var(--shell-border, #444);
304
- border-radius: 4px;
304
+ border-radius: var(--shell-radius);
305
305
  font-family: inherit;
306
306
  font-size: 0.875rem;
307
307
  }
@@ -313,7 +313,7 @@
313
313
  background: var(--shell-input-bg, #2a2a2a);
314
314
  color: var(--shell-fg, #e0e0e0);
315
315
  border: 1px solid var(--shell-border, #444);
316
- border-radius: 4px;
316
+ border-radius: var(--shell-radius);
317
317
  font-family: inherit;
318
318
  font-size: 0.875rem;
319
319
  }
@@ -322,7 +322,7 @@
322
322
  background: var(--shell-accent, #007acc);
323
323
  color: #fff;
324
324
  border: none;
325
- border-radius: 4px;
325
+ border-radius: var(--shell-radius);
326
326
  cursor: pointer;
327
327
  font-family: inherit;
328
328
  font-size: 0.875rem;
@@ -337,7 +337,7 @@
337
337
  background: color-mix(in srgb, var(--shell-error, #d32f2f) 15%, transparent);
338
338
  color: var(--shell-error, #d32f2f);
339
339
  border: 1px solid var(--shell-error, #d32f2f);
340
- border-radius: 4px;
340
+ border-radius: var(--shell-radius);
341
341
  font-size: 0.8125rem;
342
342
  }
343
343
  .store-grid {
@@ -348,7 +348,7 @@
348
348
  .store-card {
349
349
  background: var(--shell-input-bg, #2a2a2a);
350
350
  border: 1px solid var(--shell-border, #444);
351
- border-radius: 6px;
351
+ border-radius: var(--shell-radius-md);
352
352
  padding: 14px;
353
353
  display: flex;
354
354
  flex-direction: column;
@@ -373,7 +373,7 @@
373
373
  .store-icon-img {
374
374
  width: 36px;
375
375
  height: 36px;
376
- border-radius: 4px;
376
+ border-radius: var(--shell-radius);
377
377
  object-fit: cover;
378
378
  }
379
379
  .store-icon-placeholder {
@@ -384,7 +384,7 @@
384
384
  justify-content: center;
385
385
  background: var(--shell-accent, #007acc);
386
386
  color: #fff;
387
- border-radius: 4px;
387
+ border-radius: var(--shell-radius);
388
388
  font-weight: 700;
389
389
  font-size: 1rem;
390
390
  }
@@ -401,7 +401,7 @@
401
401
  .store-card-badge {
402
402
  font-size: 0.6875rem;
403
403
  padding: 1px 6px;
404
- border-radius: 3px;
404
+ border-radius: var(--shell-radius-sm);
405
405
  text-transform: uppercase;
406
406
  font-weight: 600;
407
407
  letter-spacing: 0.04em;
@@ -433,7 +433,7 @@
433
433
  color: var(--shell-warning, #ff9800);
434
434
  padding: 4px 8px;
435
435
  background: color-mix(in srgb, var(--shell-warning, #ff9800) 10%, transparent);
436
- border-radius: 3px;
436
+ border-radius: var(--shell-radius-sm);
437
437
  }
438
438
  .store-card-actions {
439
439
  margin-top: auto;
@@ -445,7 +445,7 @@
445
445
  background: var(--shell-accent, #007acc);
446
446
  color: #fff;
447
447
  border: none;
448
- border-radius: 4px;
448
+ border-radius: var(--shell-radius);
449
449
  cursor: pointer;
450
450
  font-family: inherit;
451
451
  font-size: 0.8125rem;
@@ -464,7 +464,7 @@
464
464
  background: var(--shell-warning, #ff9800);
465
465
  color: #fff;
466
466
  border: none;
467
- border-radius: 4px;
467
+ border-radius: var(--shell-radius);
468
468
  cursor: pointer;
469
469
  font-family: inherit;
470
470
  font-size: 0.8125rem;
@@ -495,7 +495,7 @@
495
495
  padding: 4px 8px;
496
496
  background: var(--shell-input-bg, #2a2a2a);
497
497
  border: 1px solid var(--shell-border, #444);
498
- border-radius: 4px;
498
+ border-radius: var(--shell-radius);
499
499
  font-size: 0.8125rem;
500
500
  }
501
501
  .store-registry-url {
@@ -509,7 +509,7 @@
509
509
  background: transparent;
510
510
  color: var(--shell-error, #d32f2f);
511
511
  border: 1px solid var(--shell-error, #d32f2f);
512
- border-radius: 3px;
512
+ border-radius: var(--shell-radius-sm);
513
513
  cursor: pointer;
514
514
  font-size: 0.75rem;
515
515
  flex-shrink: 0;
@@ -526,7 +526,7 @@
526
526
  background: var(--shell-input-bg, #2a2a2a);
527
527
  color: var(--shell-fg, #e0e0e0);
528
528
  border: 1px solid var(--shell-border, #444);
529
- border-radius: 4px;
529
+ border-radius: var(--shell-radius);
530
530
  font-family: inherit;
531
531
  font-size: 0.8125rem;
532
532
  }
@@ -538,7 +538,7 @@
538
538
  background: var(--shell-accent, #007acc);
539
539
  color: #fff;
540
540
  border: none;
541
- border-radius: 4px;
541
+ border-radius: var(--shell-radius);
542
542
  cursor: pointer;
543
543
  font-family: inherit;
544
544
  font-size: 0.8125rem;
@@ -10,7 +10,7 @@ export const storeApp = {
10
10
  manifest: {
11
11
  id: 'sh3-store-app',
12
12
  label: 'Package Store',
13
- version: '0.2.0',
13
+ version: '0.2.1',
14
14
  requiredShards: ['sh3-store'],
15
15
  layoutVersion: 1,
16
16
  admin: true,
@@ -48,7 +48,7 @@ export const storeShard = {
48
48
  manifest: {
49
49
  id: 'sh3-store',
50
50
  label: 'Package Store',
51
- version: '0.2.0',
51
+ version: '0.2.1',
52
52
  views: [
53
53
  { id: 'sh3-store:browse', label: 'Store' },
54
54
  { id: 'sh3-store:installed', label: 'Installed' },
@@ -208,10 +208,11 @@ export const storeShard = {
208
208
  };
209
209
  ctx.registerView('sh3-store:browse', browseFactory);
210
210
  ctx.registerView('sh3-store:installed', installedFactory);
211
+ // refreshInstalled can run immediately (hits server, no env needed).
212
+ refreshInstalled();
211
213
  },
212
214
  autostart() {
213
- // Intentionally empty. Defining autostart puts the store shard on
214
- // the self-starting path at boot so its views are available from
215
- // the launcher without requiring an app to declare it.
215
+ // Runs after env hydration, so registries are populated.
216
+ storeContext.refreshCatalog();
216
217
  },
217
218
  };