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,141 @@
1
+ /*
2
+ * createStateZones — turns a declarative zone schema into live reactive
3
+ * per-zone objects backed by the right store for each zone.
4
+ *
5
+ * Usage (from a shard, or the shell itself):
6
+ *
7
+ * const state = createStateZones('graphlive', {
8
+ * ephemeral: { hoveredId: null as string | null },
9
+ * session: { lastFocus: null as string | null },
10
+ * workspace: { collapsed: false, panelSizes: [0.3, 0.7] },
11
+ * user: { showIcons: true },
12
+ * });
13
+ *
14
+ * state.workspace.collapsed = true; // persists to localStorage
15
+ * state.ephemeral.hoveredId = 'foo'; // in-memory only
16
+ *
17
+ * Guarantees (phase 3 subset of docs/design/state-zones.md):
18
+ * - Hydration before return: persistent zones are populated from the
19
+ * backend before this function returns, so reads in the next line of
20
+ * user code see persisted values.
21
+ * - Debounced flush: writes within the same microtask coalesce to a
22
+ * single backend write per (zone, shardId).
23
+ * - Atomicity per write: each flush serializes a full zone snapshot.
24
+ * - Isolation: zone data is keyed by shardId; shards cannot collide.
25
+ *
26
+ * Lifetime management (tracking teardown on shard deactivation) arrives in
27
+ * phase 4 when the shard contract exists. For phase 3, roots live until the
28
+ * page unloads, which is fine for a single-session shell.
29
+ *
30
+ * This file must be `.svelte.ts` so it can use the Svelte 5 runes
31
+ * `$state`, `$effect.root`, `$effect`, and `$state.snapshot` at module level.
32
+ */
33
+ import { MemoryBackend, LocalStorageBackend } from './backends';
34
+ import { PERSISTENT_ZONES } from './types';
35
+ const backends = {
36
+ ephemeral: new MemoryBackend(),
37
+ session: new MemoryBackend(),
38
+ workspace: new LocalStorageBackend('sh3:workspace:'),
39
+ user: new LocalStorageBackend('sh3:user:'),
40
+ };
41
+ const pending = new Map();
42
+ let flushScheduled = false;
43
+ function scheduleFlush(zone, shardId, value) {
44
+ pending.set(`${zone}:${shardId}`, { zone, shardId, value });
45
+ if (flushScheduled)
46
+ return;
47
+ flushScheduled = true;
48
+ queueMicrotask(() => {
49
+ flushScheduled = false;
50
+ for (const { zone, shardId, value } of pending.values()) {
51
+ backends[zone].write(shardId, value);
52
+ }
53
+ pending.clear();
54
+ });
55
+ }
56
+ /**
57
+ * Create live reactive state zones for a shard (or the shell itself).
58
+ *
59
+ * Each zone declared in `schema` is backed by the appropriate store:
60
+ * `ephemeral` and `session` are in-memory; `workspace` and `user` are
61
+ * persisted to localStorage and hydrated before this call returns.
62
+ *
63
+ * Writes to persistent zones are debounced and coalesced per microtask.
64
+ * Zones not declared in `schema` are not created; the result only
65
+ * contains the keys the caller asked for.
66
+ *
67
+ * @param shardId - Unique shard identifier used as the storage namespace.
68
+ * @param schema - Record of zone names to their default values.
69
+ * @returns A reactive `StateZones<T>` object keyed to the declared zones.
70
+ */
71
+ export function createStateZones(shardId, schema) {
72
+ const result = {};
73
+ for (const key of Object.keys(schema)) {
74
+ const defaults = schema[key];
75
+ const backend = backends[key];
76
+ // Hydrate: shallow-merge stored values over the schema defaults so new
77
+ // fields added to the schema in a later version get their defaults even
78
+ // for shards whose backend entries predate them.
79
+ const stored = backend.read(shardId);
80
+ const initial = Object.assign(Object.assign({}, defaults), (stored !== null && stored !== void 0 ? stored : {}));
81
+ // The reactive proxy. Deep reactivity via $state lets shards mutate
82
+ // nested fields (e.g. state.workspace.panelSizes[0] = 0.4) and still
83
+ // trigger the flush effect.
84
+ const proxy = $state(initial);
85
+ result[key] = proxy;
86
+ // Set up change tracking for persistent zones. Memory-only zones don't
87
+ // need it: nothing reads from them beyond the in-process proxy.
88
+ if (!PERSISTENT_ZONES.includes(key))
89
+ continue;
90
+ // $effect.root creates a standalone reactivity scope outside any
91
+ // component. The returned cleanup is intentionally not captured in
92
+ // phase 3 — see the "Lifetime management" note at the top of the file.
93
+ $effect.root(() => {
94
+ let first = true;
95
+ $effect(() => {
96
+ // $state.snapshot deeply reads every reactive field, which both
97
+ // establishes dependency tracking and produces a plain-object copy
98
+ // safe to hand to the backend.
99
+ const snap = $state.snapshot(proxy);
100
+ if (first) {
101
+ first = false;
102
+ return; // don't write back the just-hydrated value
103
+ }
104
+ scheduleFlush(key, shardId, snap);
105
+ });
106
+ });
107
+ }
108
+ return result;
109
+ }
110
+ // ---------- test hooks ----------------------------------------------------
111
+ /**
112
+ * Swap a backend implementation. Intended for tests and for phase 4+ when
113
+ * Tauri or IndexedDB backends replace the defaults. Not part of the shard-
114
+ * facing API.
115
+ */
116
+ export function __setBackend(zone, backend) {
117
+ backends[zone] = backend;
118
+ }
119
+ /**
120
+ * Read a raw persisted zone entry without creating a reactive proxy.
121
+ *
122
+ * Intended for framework-internal consumers (e.g. the shell's layout
123
+ * persistence) that need to inspect a stored value before deciding
124
+ * whether to pass it to `createStateZones`. Not part of the shard-facing
125
+ * API — shards always go through `createStateZones` so hydration is
126
+ * reactive and debounced-flush applies.
127
+ *
128
+ * Returns `undefined` for missing, corrupt, or memory-only-zone entries
129
+ * that have no stored value.
130
+ */
131
+ export function peekZone(zone, shardId) {
132
+ return backends[zone].read(shardId);
133
+ }
134
+ /**
135
+ * Delete a persisted zone entry. Intended for framework-internal use
136
+ * when a stored value is known to be incompatible (version mismatch,
137
+ * corruption) and should be removed before `createStateZones` hydrates.
138
+ */
139
+ export function clearZone(zone, shardId) {
140
+ backends[zone].delete(shardId);
141
+ }
@@ -0,0 +1,201 @@
1
+ <script lang="ts">
2
+ /*
3
+ * InstalledView — lists installed packages with uninstall capability.
4
+ *
5
+ * Simpler than the browse view: a flat list of installed packages with
6
+ * metadata (type, version, contract version, source registry, install date)
7
+ * and an uninstall button per entry.
8
+ */
9
+
10
+ import { storeContext } from './storeShard.svelte';
11
+ import { uninstallPackage } from '../registry/installer';
12
+
13
+ const ctx = storeContext;
14
+
15
+ let uninstallingIds = $state<Set<string>>(new Set());
16
+
17
+ async function handleUninstall(id: string) {
18
+ if (uninstallingIds.has(id)) return;
19
+
20
+ uninstallingIds = new Set([...uninstallingIds, id]);
21
+ try {
22
+ await uninstallPackage(id);
23
+ await ctx.refreshInstalled();
24
+ } catch (err) {
25
+ console.warn('[sh3-store] Uninstall failed:', err instanceof Error ? err.message : err);
26
+ } finally {
27
+ const next = new Set(uninstallingIds);
28
+ next.delete(id);
29
+ uninstallingIds = next;
30
+ }
31
+ }
32
+
33
+ function handleRefresh() {
34
+ ctx.refreshInstalled();
35
+ }
36
+
37
+ function formatDate(iso: string): string {
38
+ try {
39
+ return new Date(iso).toLocaleDateString(undefined, {
40
+ year: 'numeric',
41
+ month: 'short',
42
+ day: 'numeric',
43
+ });
44
+ } catch {
45
+ return iso;
46
+ }
47
+ }
48
+ </script>
49
+
50
+ <div class="installed-view">
51
+ <header class="installed-header">
52
+ <h2>Installed Packages</h2>
53
+ <button class="installed-refresh" onclick={handleRefresh}>Refresh</button>
54
+ </header>
55
+
56
+ {#if ctx.state.ephemeral.installed.length === 0}
57
+ <div class="installed-empty">No packages installed.</div>
58
+ {:else}
59
+ <ul class="installed-list">
60
+ {#each ctx.state.ephemeral.installed as pkg (pkg.id)}
61
+ {@const uninstalling = uninstallingIds.has(pkg.id)}
62
+ <li class="installed-item">
63
+ <div class="installed-item-main">
64
+ <span class="installed-item-id">{pkg.id}</span>
65
+ <span class="installed-item-badge" class:badge-shard={pkg.type === 'shard'} class:badge-app={pkg.type === 'app'}>
66
+ {pkg.type}
67
+ </span>
68
+ <span class="installed-item-version">{pkg.version}</span>
69
+ </div>
70
+ <div class="installed-item-meta">
71
+ <span>Contract: v{pkg.contractVersion}</span>
72
+ <span>Source: {pkg.sourceRegistry}</span>
73
+ <span>Installed: {formatDate(pkg.installedAt)}</span>
74
+ </div>
75
+ <div class="installed-item-actions">
76
+ <button
77
+ class="installed-uninstall-btn"
78
+ onclick={() => handleUninstall(pkg.id)}
79
+ disabled={uninstalling}
80
+ >
81
+ {uninstalling ? 'Removing...' : 'Uninstall'}
82
+ </button>
83
+ </div>
84
+ </li>
85
+ {/each}
86
+ </ul>
87
+ {/if}
88
+ </div>
89
+
90
+ <style>
91
+ .installed-view {
92
+ font-family: var(--shell-font, system-ui, sans-serif);
93
+ color: var(--shell-fg, #e0e0e0);
94
+ background: var(--shell-bg, #1e1e1e);
95
+ padding: 16px;
96
+ height: 100%;
97
+ overflow-y: auto;
98
+ box-sizing: border-box;
99
+ }
100
+ .installed-header {
101
+ display: flex;
102
+ align-items: center;
103
+ justify-content: space-between;
104
+ margin-bottom: 16px;
105
+ }
106
+ .installed-header h2 {
107
+ margin: 0;
108
+ font-size: 1.25rem;
109
+ font-weight: 600;
110
+ }
111
+ .installed-refresh {
112
+ padding: 6px 14px;
113
+ background: var(--shell-accent, #007acc);
114
+ color: #fff;
115
+ border: none;
116
+ border-radius: 4px;
117
+ cursor: pointer;
118
+ font-family: inherit;
119
+ font-size: 0.875rem;
120
+ }
121
+ .installed-empty {
122
+ text-align: center;
123
+ padding: 32px 16px;
124
+ color: var(--shell-fg-muted, #888);
125
+ font-size: 0.875rem;
126
+ }
127
+ .installed-list {
128
+ list-style: none;
129
+ margin: 0;
130
+ padding: 0;
131
+ display: flex;
132
+ flex-direction: column;
133
+ gap: 8px;
134
+ }
135
+ .installed-item {
136
+ background: var(--shell-input-bg, #2a2a2a);
137
+ border: 1px solid var(--shell-border, #444);
138
+ border-radius: 6px;
139
+ padding: 12px 14px;
140
+ display: flex;
141
+ flex-direction: column;
142
+ gap: 6px;
143
+ }
144
+ .installed-item-main {
145
+ display: flex;
146
+ align-items: center;
147
+ gap: 8px;
148
+ }
149
+ .installed-item-id {
150
+ font-weight: 600;
151
+ font-size: 0.9375rem;
152
+ }
153
+ .installed-item-badge {
154
+ font-size: 0.6875rem;
155
+ padding: 1px 6px;
156
+ border-radius: 3px;
157
+ text-transform: uppercase;
158
+ font-weight: 600;
159
+ letter-spacing: 0.04em;
160
+ }
161
+ .badge-shard {
162
+ background: color-mix(in srgb, var(--shell-accent, #007acc) 25%, transparent);
163
+ color: var(--shell-accent, #007acc);
164
+ }
165
+ .badge-app {
166
+ background: color-mix(in srgb, var(--shell-success, #4caf50) 25%, transparent);
167
+ color: var(--shell-success, #4caf50);
168
+ }
169
+ .installed-item-version {
170
+ font-size: 0.75rem;
171
+ color: var(--shell-fg-muted, #888);
172
+ }
173
+ .installed-item-meta {
174
+ display: flex;
175
+ gap: 16px;
176
+ flex-wrap: wrap;
177
+ font-size: 0.75rem;
178
+ color: var(--shell-fg-muted, #888);
179
+ }
180
+ .installed-item-actions {
181
+ display: flex;
182
+ justify-content: flex-end;
183
+ }
184
+ .installed-uninstall-btn {
185
+ padding: 4px 12px;
186
+ background: transparent;
187
+ color: var(--shell-error, #d32f2f);
188
+ border: 1px solid var(--shell-error, #d32f2f);
189
+ border-radius: 4px;
190
+ cursor: pointer;
191
+ font-family: inherit;
192
+ font-size: 0.8125rem;
193
+ }
194
+ .installed-uninstall-btn:hover:not(:disabled) {
195
+ background: color-mix(in srgb, var(--shell-error, #d32f2f) 15%, transparent);
196
+ }
197
+ .installed-uninstall-btn:disabled {
198
+ opacity: 0.6;
199
+ cursor: not-allowed;
200
+ }
201
+ </style>
@@ -0,0 +1,3 @@
1
+ declare const InstalledView: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type InstalledView = ReturnType<typeof InstalledView>;
3
+ export default InstalledView;