sh3-core 0.1.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.
Files changed (134) hide show
  1. package/dist/Shell.svelte +185 -0
  2. package/dist/Shell.svelte.d.ts +4 -0
  3. package/dist/api.d.ts +22 -0
  4. package/dist/api.js +45 -0
  5. package/dist/apps/lifecycle.d.ts +37 -0
  6. package/dist/apps/lifecycle.js +153 -0
  7. package/dist/apps/registry.svelte.d.ts +37 -0
  8. package/dist/apps/registry.svelte.js +60 -0
  9. package/dist/apps/types.d.ts +61 -0
  10. package/dist/apps/types.js +10 -0
  11. package/dist/assets/icons.svg +1119 -0
  12. package/dist/auth/auth.svelte.d.ts +44 -0
  13. package/dist/auth/auth.svelte.js +119 -0
  14. package/dist/auth/index.d.ts +1 -0
  15. package/dist/auth/index.js +1 -0
  16. package/dist/build.d.ts +29 -0
  17. package/dist/build.js +85 -0
  18. package/dist/contract.d.ts +20 -0
  19. package/dist/contract.js +28 -0
  20. package/dist/documents/backends.d.ts +17 -0
  21. package/dist/documents/backends.js +156 -0
  22. package/dist/documents/config.d.ts +7 -0
  23. package/dist/documents/config.js +27 -0
  24. package/dist/documents/handle.d.ts +6 -0
  25. package/dist/documents/handle.js +154 -0
  26. package/dist/documents/http-backend.d.ts +22 -0
  27. package/dist/documents/http-backend.js +78 -0
  28. package/dist/documents/index.d.ts +6 -0
  29. package/dist/documents/index.js +8 -0
  30. package/dist/documents/notifications.d.ts +9 -0
  31. package/dist/documents/notifications.js +39 -0
  32. package/dist/documents/types.d.ts +97 -0
  33. package/dist/documents/types.js +12 -0
  34. package/dist/host-entry.d.ts +9 -0
  35. package/dist/host-entry.js +15 -0
  36. package/dist/host.d.ts +13 -0
  37. package/dist/host.js +73 -0
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.js +13 -0
  40. package/dist/layout/DragPreview.svelte +63 -0
  41. package/dist/layout/DragPreview.svelte.d.ts +3 -0
  42. package/dist/layout/LayoutRenderer.svelte +260 -0
  43. package/dist/layout/LayoutRenderer.svelte.d.ts +6 -0
  44. package/dist/layout/SlotContainer.svelte +140 -0
  45. package/dist/layout/SlotContainer.svelte.d.ts +8 -0
  46. package/dist/layout/SlotDropZone.svelte +122 -0
  47. package/dist/layout/SlotDropZone.svelte.d.ts +8 -0
  48. package/dist/layout/drag.svelte.d.ts +45 -0
  49. package/dist/layout/drag.svelte.js +191 -0
  50. package/dist/layout/inspection.d.ts +52 -0
  51. package/dist/layout/inspection.js +157 -0
  52. package/dist/layout/ops.d.ts +78 -0
  53. package/dist/layout/ops.js +281 -0
  54. package/dist/layout/slotHostPool.svelte.d.ts +36 -0
  55. package/dist/layout/slotHostPool.svelte.js +229 -0
  56. package/dist/layout/store.svelte.d.ts +39 -0
  57. package/dist/layout/store.svelte.js +150 -0
  58. package/dist/layout/tree-walk.d.ts +15 -0
  59. package/dist/layout/tree-walk.js +33 -0
  60. package/dist/layout/types.d.ts +108 -0
  61. package/dist/layout/types.js +25 -0
  62. package/dist/overlays/ModalFrame.svelte +87 -0
  63. package/dist/overlays/ModalFrame.svelte.d.ts +10 -0
  64. package/dist/overlays/PopupFrame.svelte +85 -0
  65. package/dist/overlays/PopupFrame.svelte.d.ts +10 -0
  66. package/dist/overlays/ToastItem.svelte +77 -0
  67. package/dist/overlays/ToastItem.svelte.d.ts +9 -0
  68. package/dist/overlays/focusTrap.d.ts +1 -0
  69. package/dist/overlays/focusTrap.js +64 -0
  70. package/dist/overlays/modal.d.ts +9 -0
  71. package/dist/overlays/modal.js +141 -0
  72. package/dist/overlays/popup.d.ts +9 -0
  73. package/dist/overlays/popup.js +108 -0
  74. package/dist/overlays/roots.d.ts +4 -0
  75. package/dist/overlays/roots.js +31 -0
  76. package/dist/overlays/toast.d.ts +6 -0
  77. package/dist/overlays/toast.js +93 -0
  78. package/dist/overlays/types.d.ts +31 -0
  79. package/dist/overlays/types.js +15 -0
  80. package/dist/primitives/.gitkeep +0 -0
  81. package/dist/primitives/ResizableSplitter.svelte +333 -0
  82. package/dist/primitives/ResizableSplitter.svelte.d.ts +35 -0
  83. package/dist/primitives/TabbedPanel.svelte +305 -0
  84. package/dist/primitives/TabbedPanel.svelte.d.ts +50 -0
  85. package/dist/registry/client.d.ts +74 -0
  86. package/dist/registry/client.js +118 -0
  87. package/dist/registry/index.d.ts +13 -0
  88. package/dist/registry/index.js +14 -0
  89. package/dist/registry/installer.d.ts +53 -0
  90. package/dist/registry/installer.js +170 -0
  91. package/dist/registry/integrity.d.ts +32 -0
  92. package/dist/registry/integrity.js +92 -0
  93. package/dist/registry/loader.d.ts +50 -0
  94. package/dist/registry/loader.js +145 -0
  95. package/dist/registry/schema.d.ts +47 -0
  96. package/dist/registry/schema.js +180 -0
  97. package/dist/registry/storage.d.ts +37 -0
  98. package/dist/registry/storage.js +101 -0
  99. package/dist/registry/types.d.ts +245 -0
  100. package/dist/registry/types.js +14 -0
  101. package/dist/registry-shard/RegistryView.svelte +561 -0
  102. package/dist/registry-shard/RegistryView.svelte.d.ts +3 -0
  103. package/dist/registry-shard/registryApp.d.ts +10 -0
  104. package/dist/registry-shard/registryApp.js +24 -0
  105. package/dist/registry-shard/registryShard.svelte.d.ts +45 -0
  106. package/dist/registry-shard/registryShard.svelte.js +125 -0
  107. package/dist/shards/activate.svelte.d.ts +45 -0
  108. package/dist/shards/activate.svelte.js +124 -0
  109. package/dist/shards/registry.d.ts +4 -0
  110. package/dist/shards/registry.js +28 -0
  111. package/dist/shards/types.d.ts +155 -0
  112. package/dist/shards/types.js +20 -0
  113. package/dist/shell-shard/ShellHome.svelte +285 -0
  114. package/dist/shell-shard/ShellHome.svelte.d.ts +3 -0
  115. package/dist/shell-shard/shellShard.svelte.d.ts +2 -0
  116. package/dist/shell-shard/shellShard.svelte.js +47 -0
  117. package/dist/shellRuntime.svelte.d.ts +27 -0
  118. package/dist/shellRuntime.svelte.js +27 -0
  119. package/dist/state/backends.d.ts +26 -0
  120. package/dist/state/backends.js +99 -0
  121. package/dist/state/types.d.ts +38 -0
  122. package/dist/state/types.js +15 -0
  123. package/dist/state/zones.svelte.d.ts +52 -0
  124. package/dist/state/zones.svelte.js +141 -0
  125. package/dist/store/InstalledView.svelte +201 -0
  126. package/dist/store/InstalledView.svelte.d.ts +3 -0
  127. package/dist/store/StoreView.svelte +470 -0
  128. package/dist/store/StoreView.svelte.d.ts +3 -0
  129. package/dist/store/storeApp.d.ts +11 -0
  130. package/dist/store/storeApp.js +26 -0
  131. package/dist/store/storeShard.svelte.d.ts +29 -0
  132. package/dist/store/storeShard.svelte.js +99 -0
  133. package/dist/tokens.css +79 -0
  134. package/package.json +50 -0
@@ -0,0 +1,470 @@
1
+ <script lang="ts">
2
+ /*
3
+ * StoreView — browse catalog view for the store shard.
4
+ *
5
+ * Displays a searchable, filterable grid of packages from configured
6
+ * registries. Each card shows package metadata and an install button.
7
+ */
8
+
9
+ import { storeContext } from './storeShard.svelte';
10
+ import { fetchBundle, buildPackageMeta } from '../registry/client';
11
+ import { installPackage } from '../registry/installer';
12
+ import { contract } from '../contract';
13
+ import type { ResolvedPackage } from '../registry/client';
14
+ import type { InstalledPackage } from '../registry/types';
15
+
16
+ let search = $state('');
17
+ let typeFilter = $state<'all' | 'shard' | 'app'>('all');
18
+ let installingIds = $state<Set<string>>(new Set());
19
+ let installError = $state<string | null>(null);
20
+ let newRegistryUrl = $state('');
21
+
22
+ const ctx = storeContext;
23
+
24
+ function handleAddRegistry() {
25
+ const url = newRegistryUrl.trim();
26
+ if (!url) return;
27
+ const registries = ctx.state.user.registries;
28
+ if (registries.includes(url)) return;
29
+ ctx.state.user.registries = [...registries, url];
30
+ newRegistryUrl = '';
31
+ ctx.refreshCatalog();
32
+ ctx.refreshInstalled();
33
+ }
34
+
35
+ function handleRemoveRegistry(url: string) {
36
+ ctx.state.user.registries = ctx.state.user.registries.filter((r: string) => r !== url);
37
+ ctx.refreshCatalog();
38
+ }
39
+
40
+ const filtered = $derived.by(() => {
41
+ const q = search.toLowerCase().trim();
42
+ return ctx.state.ephemeral.catalog.filter((pkg: ResolvedPackage) => {
43
+ if (typeFilter !== 'all' && pkg.entry.type !== typeFilter) return false;
44
+ if (!q) return true;
45
+ return (
46
+ pkg.entry.id.toLowerCase().includes(q) ||
47
+ pkg.entry.label.toLowerCase().includes(q) ||
48
+ pkg.entry.description.toLowerCase().includes(q)
49
+ );
50
+ });
51
+ });
52
+
53
+ function isInstalled(id: string): boolean {
54
+ return ctx.state.ephemeral.installed.some((p: InstalledPackage) => p.id === id);
55
+ }
56
+
57
+ function hasContractMismatch(pkg: ResolvedPackage): boolean {
58
+ return String(pkg.latest.contractVersion) !== String(contract.version);
59
+ }
60
+
61
+ async function handleInstall(pkg: ResolvedPackage) {
62
+ const id = pkg.entry.id;
63
+ if (installingIds.has(id)) return;
64
+
65
+ installingIds = new Set([...installingIds, id]);
66
+ installError = null;
67
+
68
+ try {
69
+ const bundle = await fetchBundle(pkg.latest, pkg.sourceRegistry);
70
+ const meta = buildPackageMeta(pkg, pkg.latest);
71
+ const result = await installPackage(bundle, meta);
72
+ if (!result.success) {
73
+ installError = result.error ?? 'Install failed';
74
+ return;
75
+ }
76
+ // Refresh installed list to reflect the new package.
77
+ await ctx.refreshInstalled();
78
+ } catch (err) {
79
+ installError = err instanceof Error ? err.message : String(err);
80
+ } finally {
81
+ const next = new Set(installingIds);
82
+ next.delete(id);
83
+ installingIds = next;
84
+ }
85
+ }
86
+
87
+ function handleRefresh() {
88
+ ctx.refreshCatalog();
89
+ ctx.refreshInstalled();
90
+ }
91
+ </script>
92
+
93
+ <div class="store-view">
94
+ <header class="store-header">
95
+ <h2>Package Store</h2>
96
+ <div class="store-controls">
97
+ <input
98
+ class="store-search"
99
+ type="text"
100
+ placeholder="Search packages..."
101
+ bind:value={search}
102
+ />
103
+ <select class="store-filter" bind:value={typeFilter}>
104
+ <option value="all">All</option>
105
+ <option value="shard">Shards</option>
106
+ <option value="app">Apps</option>
107
+ </select>
108
+ <button
109
+ class="store-refresh"
110
+ onclick={handleRefresh}
111
+ disabled={ctx.state.ephemeral.loading}
112
+ >
113
+ {ctx.state.ephemeral.loading ? 'Loading...' : 'Refresh'}
114
+ </button>
115
+ </div>
116
+ </header>
117
+
118
+ {#if ctx.state.user.registries.length > 0}
119
+ <div class="store-registries">
120
+ {#each ctx.state.user.registries as url}
121
+ <div class="store-registry-entry">
122
+ <span class="store-registry-url">{url}</span>
123
+ <button class="store-registry-remove" onclick={() => handleRemoveRegistry(url)}>
124
+ Remove
125
+ </button>
126
+ </div>
127
+ {/each}
128
+ </div>
129
+ {/if}
130
+
131
+ <form class="store-add-registry" onsubmit={(e) => { e.preventDefault(); handleAddRegistry(); }}>
132
+ <input
133
+ class="store-registry-input"
134
+ type="url"
135
+ placeholder="Registry URL (e.g. https://sh3.example.com/registry.json)"
136
+ bind:value={newRegistryUrl}
137
+ />
138
+ <button type="submit" class="store-add-btn" disabled={!newRegistryUrl.trim()}>
139
+ Add
140
+ </button>
141
+ </form>
142
+
143
+ {#if ctx.state.ephemeral.error}
144
+ <div class="store-error">{ctx.state.ephemeral.error}</div>
145
+ {/if}
146
+
147
+ {#if installError}
148
+ <div class="store-error">{installError}</div>
149
+ {/if}
150
+
151
+ <div class="store-grid">
152
+ {#each filtered as pkg (pkg.entry.id)}
153
+ {@const installed = isInstalled(pkg.entry.id)}
154
+ {@const mismatch = hasContractMismatch(pkg)}
155
+ {@const installing = installingIds.has(pkg.entry.id)}
156
+ <div class="store-card">
157
+ <div class="store-card-header">
158
+ <div class="store-card-icon">
159
+ {#if pkg.entry.icon}
160
+ <img src={pkg.entry.icon} alt="" class="store-icon-img" />
161
+ {:else}
162
+ <span class="store-icon-placeholder">
163
+ {pkg.entry.type === 'shard' ? 'S' : 'A'}
164
+ </span>
165
+ {/if}
166
+ </div>
167
+ <div class="store-card-title">
168
+ <span class="store-card-label">{pkg.entry.label}</span>
169
+ <span class="store-card-badge" class:badge-shard={pkg.entry.type === 'shard'} class:badge-app={pkg.entry.type === 'app'}>
170
+ {pkg.entry.type}
171
+ </span>
172
+ <span class="store-card-version">{pkg.latest.version}</span>
173
+ </div>
174
+ </div>
175
+ <p class="store-card-desc">{pkg.entry.description}</p>
176
+ <div class="store-card-author">{pkg.entry.author.name}</div>
177
+ {#if mismatch}
178
+ <div class="store-card-warning">
179
+ Contract mismatch: package targets v{pkg.latest.contractVersion}, running v{contract.version}
180
+ </div>
181
+ {/if}
182
+ <div class="store-card-actions">
183
+ {#if installed}
184
+ <span class="store-installed-label">Installed</span>
185
+ {:else}
186
+ <button
187
+ class="store-install-btn"
188
+ onclick={() => handleInstall(pkg)}
189
+ disabled={installing}
190
+ >
191
+ {installing ? 'Installing...' : 'Install'}
192
+ </button>
193
+ {/if}
194
+ </div>
195
+ </div>
196
+ {/each}
197
+ </div>
198
+
199
+ {#if !ctx.state.ephemeral.loading && filtered.length === 0}
200
+ <div class="store-empty">
201
+ {#if ctx.state.ephemeral.catalog.length === 0}
202
+ No packages found. Add a registry URL above to get started.
203
+ {:else}
204
+ No packages match the current filter.
205
+ {/if}
206
+ </div>
207
+ {/if}
208
+ </div>
209
+
210
+ <style>
211
+ .store-view {
212
+ font-family: var(--shell-font, system-ui, sans-serif);
213
+ color: var(--shell-fg, #e0e0e0);
214
+ background: var(--shell-bg, #1e1e1e);
215
+ padding: 16px;
216
+ height: 100%;
217
+ overflow-y: auto;
218
+ box-sizing: border-box;
219
+ }
220
+ .store-header {
221
+ margin-bottom: 16px;
222
+ }
223
+ .store-header h2 {
224
+ margin: 0 0 8px 0;
225
+ font-size: 1.25rem;
226
+ font-weight: 600;
227
+ }
228
+ .store-controls {
229
+ display: flex;
230
+ gap: 8px;
231
+ flex-wrap: wrap;
232
+ }
233
+ .store-search {
234
+ flex: 1;
235
+ min-width: 160px;
236
+ padding: 6px 10px;
237
+ background: var(--shell-input-bg, #2a2a2a);
238
+ color: var(--shell-fg, #e0e0e0);
239
+ border: 1px solid var(--shell-border, #444);
240
+ border-radius: 4px;
241
+ font-family: inherit;
242
+ font-size: 0.875rem;
243
+ }
244
+ .store-search::placeholder {
245
+ color: var(--shell-fg-muted, #888);
246
+ }
247
+ .store-filter {
248
+ padding: 6px 10px;
249
+ background: var(--shell-input-bg, #2a2a2a);
250
+ color: var(--shell-fg, #e0e0e0);
251
+ border: 1px solid var(--shell-border, #444);
252
+ border-radius: 4px;
253
+ font-family: inherit;
254
+ font-size: 0.875rem;
255
+ }
256
+ .store-refresh {
257
+ padding: 6px 14px;
258
+ background: var(--shell-accent, #007acc);
259
+ color: #fff;
260
+ border: none;
261
+ border-radius: 4px;
262
+ cursor: pointer;
263
+ font-family: inherit;
264
+ font-size: 0.875rem;
265
+ }
266
+ .store-refresh:disabled {
267
+ opacity: 0.6;
268
+ cursor: not-allowed;
269
+ }
270
+ .store-error {
271
+ padding: 8px 12px;
272
+ margin-bottom: 12px;
273
+ background: color-mix(in srgb, var(--shell-error, #d32f2f) 15%, transparent);
274
+ color: var(--shell-error, #d32f2f);
275
+ border: 1px solid var(--shell-error, #d32f2f);
276
+ border-radius: 4px;
277
+ font-size: 0.8125rem;
278
+ }
279
+ .store-grid {
280
+ display: grid;
281
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
282
+ gap: 12px;
283
+ }
284
+ .store-card {
285
+ background: var(--shell-input-bg, #2a2a2a);
286
+ border: 1px solid var(--shell-border, #444);
287
+ border-radius: 6px;
288
+ padding: 14px;
289
+ display: flex;
290
+ flex-direction: column;
291
+ gap: 8px;
292
+ }
293
+ .store-card:hover {
294
+ border-color: var(--shell-accent, #007acc);
295
+ }
296
+ .store-card-header {
297
+ display: flex;
298
+ align-items: center;
299
+ gap: 10px;
300
+ }
301
+ .store-card-icon {
302
+ width: 36px;
303
+ height: 36px;
304
+ flex-shrink: 0;
305
+ display: flex;
306
+ align-items: center;
307
+ justify-content: center;
308
+ }
309
+ .store-icon-img {
310
+ width: 36px;
311
+ height: 36px;
312
+ border-radius: 4px;
313
+ object-fit: cover;
314
+ }
315
+ .store-icon-placeholder {
316
+ width: 36px;
317
+ height: 36px;
318
+ display: flex;
319
+ align-items: center;
320
+ justify-content: center;
321
+ background: var(--shell-accent, #007acc);
322
+ color: #fff;
323
+ border-radius: 4px;
324
+ font-weight: 700;
325
+ font-size: 1rem;
326
+ }
327
+ .store-card-title {
328
+ display: flex;
329
+ align-items: center;
330
+ gap: 6px;
331
+ flex-wrap: wrap;
332
+ }
333
+ .store-card-label {
334
+ font-weight: 600;
335
+ font-size: 0.9375rem;
336
+ }
337
+ .store-card-badge {
338
+ font-size: 0.6875rem;
339
+ padding: 1px 6px;
340
+ border-radius: 3px;
341
+ text-transform: uppercase;
342
+ font-weight: 600;
343
+ letter-spacing: 0.04em;
344
+ }
345
+ .badge-shard {
346
+ background: color-mix(in srgb, var(--shell-accent, #007acc) 25%, transparent);
347
+ color: var(--shell-accent, #007acc);
348
+ }
349
+ .badge-app {
350
+ background: color-mix(in srgb, var(--shell-success, #4caf50) 25%, transparent);
351
+ color: var(--shell-success, #4caf50);
352
+ }
353
+ .store-card-version {
354
+ font-size: 0.75rem;
355
+ color: var(--shell-fg-muted, #888);
356
+ }
357
+ .store-card-desc {
358
+ margin: 0;
359
+ font-size: 0.8125rem;
360
+ color: var(--shell-fg-muted, #888);
361
+ line-height: 1.4;
362
+ }
363
+ .store-card-author {
364
+ font-size: 0.75rem;
365
+ color: var(--shell-fg-muted, #888);
366
+ }
367
+ .store-card-warning {
368
+ font-size: 0.75rem;
369
+ color: var(--shell-warning, #ff9800);
370
+ padding: 4px 8px;
371
+ background: color-mix(in srgb, var(--shell-warning, #ff9800) 10%, transparent);
372
+ border-radius: 3px;
373
+ }
374
+ .store-card-actions {
375
+ margin-top: auto;
376
+ display: flex;
377
+ justify-content: flex-end;
378
+ }
379
+ .store-install-btn {
380
+ padding: 5px 14px;
381
+ background: var(--shell-accent, #007acc);
382
+ color: #fff;
383
+ border: none;
384
+ border-radius: 4px;
385
+ cursor: pointer;
386
+ font-family: inherit;
387
+ font-size: 0.8125rem;
388
+ }
389
+ .store-install-btn:disabled {
390
+ opacity: 0.6;
391
+ cursor: not-allowed;
392
+ }
393
+ .store-installed-label {
394
+ font-size: 0.8125rem;
395
+ color: var(--shell-success, #4caf50);
396
+ font-weight: 600;
397
+ }
398
+ .store-empty {
399
+ text-align: center;
400
+ padding: 32px 16px;
401
+ color: var(--shell-fg-muted, #888);
402
+ font-size: 0.875rem;
403
+ }
404
+ .store-registries {
405
+ display: flex;
406
+ flex-direction: column;
407
+ gap: 4px;
408
+ margin-bottom: 8px;
409
+ }
410
+ .store-registry-entry {
411
+ display: flex;
412
+ align-items: center;
413
+ justify-content: space-between;
414
+ padding: 4px 8px;
415
+ background: var(--shell-input-bg, #2a2a2a);
416
+ border: 1px solid var(--shell-border, #444);
417
+ border-radius: 4px;
418
+ font-size: 0.8125rem;
419
+ }
420
+ .store-registry-url {
421
+ overflow: hidden;
422
+ text-overflow: ellipsis;
423
+ white-space: nowrap;
424
+ color: var(--shell-fg-muted, #888);
425
+ }
426
+ .store-registry-remove {
427
+ padding: 2px 8px;
428
+ background: transparent;
429
+ color: var(--shell-error, #d32f2f);
430
+ border: 1px solid var(--shell-error, #d32f2f);
431
+ border-radius: 3px;
432
+ cursor: pointer;
433
+ font-size: 0.75rem;
434
+ flex-shrink: 0;
435
+ margin-left: 8px;
436
+ }
437
+ .store-add-registry {
438
+ display: flex;
439
+ gap: 8px;
440
+ margin-bottom: 12px;
441
+ }
442
+ .store-registry-input {
443
+ flex: 1;
444
+ padding: 6px 10px;
445
+ background: var(--shell-input-bg, #2a2a2a);
446
+ color: var(--shell-fg, #e0e0e0);
447
+ border: 1px solid var(--shell-border, #444);
448
+ border-radius: 4px;
449
+ font-family: inherit;
450
+ font-size: 0.8125rem;
451
+ }
452
+ .store-registry-input::placeholder {
453
+ color: var(--shell-fg-muted, #888);
454
+ }
455
+ .store-add-btn {
456
+ padding: 6px 14px;
457
+ background: var(--shell-accent, #007acc);
458
+ color: #fff;
459
+ border: none;
460
+ border-radius: 4px;
461
+ cursor: pointer;
462
+ font-family: inherit;
463
+ font-size: 0.8125rem;
464
+ white-space: nowrap;
465
+ }
466
+ .store-add-btn:disabled {
467
+ opacity: 0.6;
468
+ cursor: not-allowed;
469
+ }
470
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const StoreView: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type StoreView = ReturnType<typeof StoreView>;
3
+ export default StoreView;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Built-in Store app — composes the sh3-store shard views into a
3
+ * management interface for browsing, installing, and uninstalling
4
+ * packages.
5
+ *
6
+ * Framework-shipped: registered in host.ts during bootstrap.
7
+ * Admin-gated: the shell home checks adminAppIds to gate visibility;
8
+ * the AppManifest itself carries no admin flag (ADR-011).
9
+ */
10
+ import type { App } from '../apps/types';
11
+ export declare const storeApp: App;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Built-in Store app — composes the sh3-store shard views into a
3
+ * management interface for browsing, installing, and uninstalling
4
+ * packages.
5
+ *
6
+ * Framework-shipped: registered in host.ts during bootstrap.
7
+ * Admin-gated: the shell home checks adminAppIds to gate visibility;
8
+ * the AppManifest itself carries no admin flag (ADR-011).
9
+ */
10
+ export const storeApp = {
11
+ manifest: {
12
+ id: 'sh3-store-app',
13
+ label: 'Package Store',
14
+ version: '0.1.0',
15
+ requiredShards: ['sh3-store'],
16
+ layoutVersion: 1,
17
+ },
18
+ initialLayout: {
19
+ type: 'tabs',
20
+ activeTab: 0,
21
+ tabs: [
22
+ { slotId: 'store.browse', viewId: 'sh3-store:browse', label: 'Browse' },
23
+ { slotId: 'store.installed', viewId: 'sh3-store:installed', label: 'Installed' },
24
+ ],
25
+ },
26
+ };
@@ -0,0 +1,29 @@
1
+ import type { Shard } from '../shards/types';
2
+ import type { StateZones } from '../state/zones.svelte';
3
+ import type { ResolvedPackage } from '../registry/client';
4
+ import type { InstalledPackage } from '../registry/types';
5
+ /** Schema shape for state zone typing. */
6
+ interface StoreZoneSchema {
7
+ user: {
8
+ registries: string[];
9
+ };
10
+ ephemeral: {
11
+ catalog: ResolvedPackage[];
12
+ installed: InstalledPackage[];
13
+ loading: boolean;
14
+ error: string | null;
15
+ };
16
+ }
17
+ /** Reactive context exposed to the view components. */
18
+ export interface StoreContext {
19
+ state: StateZones<StoreZoneSchema>;
20
+ refreshCatalog(): Promise<void>;
21
+ refreshInstalled(): Promise<void>;
22
+ }
23
+ /**
24
+ * Module-level context set during activate(). Imported by the Svelte
25
+ * view components so they can read/write store state and trigger refreshes.
26
+ */
27
+ export declare let storeContext: StoreContext;
28
+ export declare const storeShard: Shard;
29
+ export {};
@@ -0,0 +1,99 @@
1
+ /*
2
+ * Store shard — framework-shipped shard for browsing and managing
3
+ * installed packages.
4
+ *
5
+ * Contributes two views:
6
+ * - `sh3-store:browse` — searchable/filterable catalog of available packages
7
+ * - `sh3-store:installed` — list of installed packages with uninstall
8
+ *
9
+ * Uses a user-zone for registry URLs (persisted) and an ephemeral-zone
10
+ * for the live catalog / installed list / loading / error state.
11
+ *
12
+ * `.svelte.ts` because mounting Svelte components requires rune access.
13
+ */
14
+ import { mount, unmount } from 'svelte';
15
+ import StoreView from './StoreView.svelte';
16
+ import InstalledView from './InstalledView.svelte';
17
+ import { fetchRegistries } from '../registry/client';
18
+ import { listInstalledPackages } from '../registry/installer';
19
+ /**
20
+ * Module-level context set during activate(). Imported by the Svelte
21
+ * view components so they can read/write store state and trigger refreshes.
22
+ */
23
+ export let storeContext = undefined;
24
+ export const storeShard = {
25
+ manifest: {
26
+ id: 'sh3-store',
27
+ label: 'Package Store',
28
+ version: '0.1.0',
29
+ views: [
30
+ { id: 'sh3-store:browse', label: 'Store' },
31
+ { id: 'sh3-store:installed', label: 'Installed' },
32
+ ],
33
+ },
34
+ activate(ctx) {
35
+ const state = ctx.state({
36
+ user: { registries: [] },
37
+ ephemeral: {
38
+ catalog: [],
39
+ installed: [],
40
+ loading: false,
41
+ error: null,
42
+ },
43
+ });
44
+ async function refreshCatalog() {
45
+ state.ephemeral.loading = true;
46
+ state.ephemeral.error = null;
47
+ try {
48
+ const results = await fetchRegistries(state.user.registries);
49
+ state.ephemeral.catalog = results;
50
+ }
51
+ catch (err) {
52
+ state.ephemeral.error =
53
+ err instanceof Error ? err.message : String(err);
54
+ }
55
+ finally {
56
+ state.ephemeral.loading = false;
57
+ }
58
+ }
59
+ async function refreshInstalled() {
60
+ try {
61
+ const packages = await listInstalledPackages();
62
+ state.ephemeral.installed = packages;
63
+ }
64
+ catch (err) {
65
+ console.warn('[sh3-store] Failed to list installed packages:', err instanceof Error ? err.message : err);
66
+ }
67
+ }
68
+ // Set the module-level context so view components can import it.
69
+ storeContext = { state, refreshCatalog, refreshInstalled };
70
+ // --- View factories ---
71
+ const browseFactory = {
72
+ mount(container, _context) {
73
+ const instance = mount(StoreView, { target: container });
74
+ return {
75
+ unmount() {
76
+ unmount(instance);
77
+ },
78
+ };
79
+ },
80
+ };
81
+ const installedFactory = {
82
+ mount(container, _context) {
83
+ const instance = mount(InstalledView, { target: container });
84
+ return {
85
+ unmount() {
86
+ unmount(instance);
87
+ },
88
+ };
89
+ },
90
+ };
91
+ ctx.registerView('sh3-store:browse', browseFactory);
92
+ ctx.registerView('sh3-store:installed', installedFactory);
93
+ },
94
+ autostart() {
95
+ // Intentionally empty. Defining autostart puts the store shard on
96
+ // the self-starting path at boot so its views are available from
97
+ // the launcher without requiring an app to declare it.
98
+ },
99
+ };