sh3-core 0.6.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 (242) hide show
  1. package/README.md +9 -0
  2. package/dist/Shell.svelte +283 -0
  3. package/dist/Shell.svelte.d.ts +5 -0
  4. package/dist/api.d.ts +28 -0
  5. package/dist/api.js +50 -0
  6. package/dist/app/admin/ApiKeysView.svelte +169 -0
  7. package/dist/app/admin/ApiKeysView.svelte.d.ts +3 -0
  8. package/dist/app/admin/AuthSettingsView.svelte +105 -0
  9. package/dist/app/admin/AuthSettingsView.svelte.d.ts +3 -0
  10. package/dist/app/admin/SystemView.svelte +73 -0
  11. package/dist/app/admin/SystemView.svelte.d.ts +3 -0
  12. package/dist/app/admin/UsersView.svelte +188 -0
  13. package/dist/app/admin/UsersView.svelte.d.ts +3 -0
  14. package/dist/app/admin/adminApp.d.ts +7 -0
  15. package/dist/app/admin/adminApp.js +25 -0
  16. package/dist/app/admin/adminShard.svelte.d.ts +4 -0
  17. package/dist/app/admin/adminShard.svelte.js +62 -0
  18. package/dist/app/store/InstalledView.svelte +246 -0
  19. package/dist/app/store/InstalledView.svelte.d.ts +3 -0
  20. package/dist/app/store/StoreView.svelte +522 -0
  21. package/dist/app/store/StoreView.svelte.d.ts +3 -0
  22. package/dist/app/store/storeApp.d.ts +10 -0
  23. package/dist/app/store/storeApp.js +26 -0
  24. package/dist/app/store/storeShard.svelte.d.ts +38 -0
  25. package/dist/app/store/storeShard.svelte.js +218 -0
  26. package/dist/apps/lifecycle.d.ts +42 -0
  27. package/dist/apps/lifecycle.js +184 -0
  28. package/dist/apps/registry.svelte.d.ts +40 -0
  29. package/dist/apps/registry.svelte.js +59 -0
  30. package/dist/apps/terminal/manifest.d.ts +8 -0
  31. package/dist/apps/terminal/manifest.js +13 -0
  32. package/dist/apps/terminal/terminal-app.d.ts +7 -0
  33. package/dist/apps/terminal/terminal-app.js +14 -0
  34. package/dist/apps/types.d.ts +93 -0
  35. package/dist/apps/types.js +10 -0
  36. package/dist/artifact.d.ts +32 -0
  37. package/dist/artifact.js +1 -0
  38. package/dist/assets/SH3.png +0 -0
  39. package/dist/assets/icons.svg +1126 -0
  40. package/dist/assets.d.ts +13 -0
  41. package/dist/auth/GuestBanner.svelte +134 -0
  42. package/dist/auth/GuestBanner.svelte.d.ts +3 -0
  43. package/dist/auth/SignInWall.svelte +203 -0
  44. package/dist/auth/SignInWall.svelte.d.ts +7 -0
  45. package/dist/auth/auth.svelte.d.ts +69 -0
  46. package/dist/auth/auth.svelte.js +165 -0
  47. package/dist/auth/index.d.ts +2 -0
  48. package/dist/auth/index.js +1 -0
  49. package/dist/auth/types.d.ts +41 -0
  50. package/dist/auth/types.js +6 -0
  51. package/dist/build.d.ts +49 -0
  52. package/dist/build.js +236 -0
  53. package/dist/contract.d.ts +20 -0
  54. package/dist/contract.js +28 -0
  55. package/dist/createShell.d.ts +24 -0
  56. package/dist/createShell.js +131 -0
  57. package/dist/documents/backends.d.ts +17 -0
  58. package/dist/documents/backends.js +156 -0
  59. package/dist/documents/config.d.ts +7 -0
  60. package/dist/documents/config.js +27 -0
  61. package/dist/documents/handle.d.ts +6 -0
  62. package/dist/documents/handle.js +154 -0
  63. package/dist/documents/http-backend.d.ts +22 -0
  64. package/dist/documents/http-backend.js +78 -0
  65. package/dist/documents/index.d.ts +6 -0
  66. package/dist/documents/index.js +8 -0
  67. package/dist/documents/notifications.d.ts +9 -0
  68. package/dist/documents/notifications.js +39 -0
  69. package/dist/documents/types.d.ts +97 -0
  70. package/dist/documents/types.js +12 -0
  71. package/dist/env/client.d.ts +44 -0
  72. package/dist/env/client.js +106 -0
  73. package/dist/env/index.d.ts +2 -0
  74. package/dist/env/index.js +1 -0
  75. package/dist/env/types.d.ts +12 -0
  76. package/dist/env/types.js +8 -0
  77. package/dist/host-entry.d.ts +13 -0
  78. package/dist/host-entry.js +17 -0
  79. package/dist/host.d.ts +15 -0
  80. package/dist/host.js +86 -0
  81. package/dist/index.d.ts +4 -0
  82. package/dist/index.js +14 -0
  83. package/dist/layout/DragPreview.svelte +63 -0
  84. package/dist/layout/DragPreview.svelte.d.ts +3 -0
  85. package/dist/layout/LayoutRenderer.svelte +262 -0
  86. package/dist/layout/LayoutRenderer.svelte.d.ts +6 -0
  87. package/dist/layout/SlotContainer.svelte +140 -0
  88. package/dist/layout/SlotContainer.svelte.d.ts +8 -0
  89. package/dist/layout/SlotDropZone.svelte +122 -0
  90. package/dist/layout/SlotDropZone.svelte.d.ts +8 -0
  91. package/dist/layout/drag.svelte.d.ts +45 -0
  92. package/dist/layout/drag.svelte.js +200 -0
  93. package/dist/layout/inspection.d.ts +72 -0
  94. package/dist/layout/inspection.js +209 -0
  95. package/dist/layout/ops.d.ts +100 -0
  96. package/dist/layout/ops.js +310 -0
  97. package/dist/layout/slotHostPool.svelte.d.ts +36 -0
  98. package/dist/layout/slotHostPool.svelte.js +229 -0
  99. package/dist/layout/store.svelte.d.ts +39 -0
  100. package/dist/layout/store.svelte.js +153 -0
  101. package/dist/layout/tree-walk.d.ts +15 -0
  102. package/dist/layout/tree-walk.js +33 -0
  103. package/dist/layout/types.d.ts +108 -0
  104. package/dist/layout/types.js +25 -0
  105. package/dist/migrations/shell-rename.d.ts +16 -0
  106. package/dist/migrations/shell-rename.js +48 -0
  107. package/dist/overlays/ModalFrame.svelte +87 -0
  108. package/dist/overlays/ModalFrame.svelte.d.ts +10 -0
  109. package/dist/overlays/PopupFrame.svelte +85 -0
  110. package/dist/overlays/PopupFrame.svelte.d.ts +10 -0
  111. package/dist/overlays/ToastItem.svelte +77 -0
  112. package/dist/overlays/ToastItem.svelte.d.ts +9 -0
  113. package/dist/overlays/focusTrap.d.ts +1 -0
  114. package/dist/overlays/focusTrap.js +64 -0
  115. package/dist/overlays/modal.d.ts +9 -0
  116. package/dist/overlays/modal.js +141 -0
  117. package/dist/overlays/popup.d.ts +9 -0
  118. package/dist/overlays/popup.js +108 -0
  119. package/dist/overlays/roots.d.ts +4 -0
  120. package/dist/overlays/roots.js +31 -0
  121. package/dist/overlays/toast.d.ts +6 -0
  122. package/dist/overlays/toast.js +93 -0
  123. package/dist/overlays/types.d.ts +31 -0
  124. package/dist/overlays/types.js +15 -0
  125. package/dist/platform/index.d.ts +10 -0
  126. package/dist/platform/index.js +33 -0
  127. package/dist/platform/tauri-backend.d.ts +15 -0
  128. package/dist/platform/tauri-backend.js +58 -0
  129. package/dist/primitives/.gitkeep +0 -0
  130. package/dist/primitives/ResizableSplitter.svelte +333 -0
  131. package/dist/primitives/ResizableSplitter.svelte.d.ts +35 -0
  132. package/dist/primitives/TabbedPanel.svelte +305 -0
  133. package/dist/primitives/TabbedPanel.svelte.d.ts +50 -0
  134. package/dist/primitives/base.css +42 -0
  135. package/dist/registry/client.d.ts +74 -0
  136. package/dist/registry/client.js +117 -0
  137. package/dist/registry/index.d.ts +13 -0
  138. package/dist/registry/index.js +14 -0
  139. package/dist/registry/installer.d.ts +53 -0
  140. package/dist/registry/installer.js +168 -0
  141. package/dist/registry/integrity.d.ts +32 -0
  142. package/dist/registry/integrity.js +92 -0
  143. package/dist/registry/loader.d.ts +50 -0
  144. package/dist/registry/loader.js +145 -0
  145. package/dist/registry/schema.d.ts +47 -0
  146. package/dist/registry/schema.js +185 -0
  147. package/dist/registry/storage.d.ts +37 -0
  148. package/dist/registry/storage.js +101 -0
  149. package/dist/registry/types.d.ts +262 -0
  150. package/dist/registry/types.js +14 -0
  151. package/dist/server-shard/types.d.ts +67 -0
  152. package/dist/server-shard/types.js +13 -0
  153. package/dist/sh3core-shard/ShellHome.svelte +192 -0
  154. package/dist/sh3core-shard/ShellHome.svelte.d.ts +3 -0
  155. package/dist/sh3core-shard/ShellTitle.svelte +171 -0
  156. package/dist/sh3core-shard/ShellTitle.svelte.d.ts +3 -0
  157. package/dist/sh3core-shard/sh3coreShard.svelte.d.ts +2 -0
  158. package/dist/sh3core-shard/sh3coreShard.svelte.js +53 -0
  159. package/dist/shards/activate.svelte.d.ts +52 -0
  160. package/dist/shards/activate.svelte.js +186 -0
  161. package/dist/shards/registry.d.ts +4 -0
  162. package/dist/shards/registry.js +28 -0
  163. package/dist/shards/types.d.ts +207 -0
  164. package/dist/shards/types.js +20 -0
  165. package/dist/shell-shard/InputLine.svelte +133 -0
  166. package/dist/shell-shard/InputLine.svelte.d.ts +11 -0
  167. package/dist/shell-shard/ScrollbackView.svelte +47 -0
  168. package/dist/shell-shard/ScrollbackView.svelte.d.ts +7 -0
  169. package/dist/shell-shard/Terminal.svelte +122 -0
  170. package/dist/shell-shard/Terminal.svelte.d.ts +8 -0
  171. package/dist/shell-shard/entries/PromptEntry.svelte +25 -0
  172. package/dist/shell-shard/entries/PromptEntry.svelte.d.ts +7 -0
  173. package/dist/shell-shard/entries/RichEntry.svelte +19 -0
  174. package/dist/shell-shard/entries/RichEntry.svelte.d.ts +8 -0
  175. package/dist/shell-shard/entries/StatusEntry.svelte +22 -0
  176. package/dist/shell-shard/entries/StatusEntry.svelte.d.ts +7 -0
  177. package/dist/shell-shard/entries/TextEntry.svelte +25 -0
  178. package/dist/shell-shard/entries/TextEntry.svelte.d.ts +7 -0
  179. package/dist/shell-shard/manifest.d.ts +2 -0
  180. package/dist/shell-shard/manifest.js +11 -0
  181. package/dist/shell-shard/protocol.d.ts +90 -0
  182. package/dist/shell-shard/protocol.js +11 -0
  183. package/dist/shell-shard/registry.d.ts +69 -0
  184. package/dist/shell-shard/registry.js +47 -0
  185. package/dist/shell-shard/rich/AppCard.svelte +25 -0
  186. package/dist/shell-shard/rich/AppCard.svelte.d.ts +10 -0
  187. package/dist/shell-shard/rich/AppsTable.svelte +29 -0
  188. package/dist/shell-shard/rich/AppsTable.svelte.d.ts +12 -0
  189. package/dist/shell-shard/rich/EnvTable.svelte +27 -0
  190. package/dist/shell-shard/rich/EnvTable.svelte.d.ts +8 -0
  191. package/dist/shell-shard/rich/HelpTable.svelte +29 -0
  192. package/dist/shell-shard/rich/HelpTable.svelte.d.ts +12 -0
  193. package/dist/shell-shard/rich/HistoryList.svelte +37 -0
  194. package/dist/shell-shard/rich/HistoryList.svelte.d.ts +9 -0
  195. package/dist/shell-shard/rich/ShardsTable.svelte +28 -0
  196. package/dist/shell-shard/rich/ShardsTable.svelte.d.ts +12 -0
  197. package/dist/shell-shard/rich/ViewsTable.svelte +31 -0
  198. package/dist/shell-shard/rich/ViewsTable.svelte.d.ts +13 -0
  199. package/dist/shell-shard/rich/ZoneTree.svelte +19 -0
  200. package/dist/shell-shard/rich/ZoneTree.svelte.d.ts +8 -0
  201. package/dist/shell-shard/rich/ZonesTable.svelte +27 -0
  202. package/dist/shell-shard/rich/ZonesTable.svelte.d.ts +11 -0
  203. package/dist/shell-shard/scrollback.svelte.d.ts +36 -0
  204. package/dist/shell-shard/scrollback.svelte.js +43 -0
  205. package/dist/shell-shard/session-client.svelte.d.ts +23 -0
  206. package/dist/shell-shard/session-client.svelte.js +120 -0
  207. package/dist/shell-shard/shellShard.svelte.d.ts +2 -0
  208. package/dist/shell-shard/shellShard.svelte.js +139 -0
  209. package/dist/shell-shard/verbs/apps.d.ts +3 -0
  210. package/dist/shell-shard/verbs/apps.js +50 -0
  211. package/dist/shell-shard/verbs/clear.d.ts +2 -0
  212. package/dist/shell-shard/verbs/clear.js +7 -0
  213. package/dist/shell-shard/verbs/help.d.ts +2 -0
  214. package/dist/shell-shard/verbs/help.js +21 -0
  215. package/dist/shell-shard/verbs/history.d.ts +2 -0
  216. package/dist/shell-shard/verbs/history.js +20 -0
  217. package/dist/shell-shard/verbs/index.d.ts +2 -0
  218. package/dist/shell-shard/verbs/index.js +29 -0
  219. package/dist/shell-shard/verbs/session.d.ts +5 -0
  220. package/dist/shell-shard/verbs/session.js +65 -0
  221. package/dist/shell-shard/verbs/shards.d.ts +2 -0
  222. package/dist/shell-shard/verbs/shards.js +14 -0
  223. package/dist/shell-shard/verbs/views.d.ts +4 -0
  224. package/dist/shell-shard/verbs/views.js +90 -0
  225. package/dist/shell-shard/verbs/zones.d.ts +3 -0
  226. package/dist/shell-shard/verbs/zones.js +38 -0
  227. package/dist/shellRuntime.svelte.d.ts +27 -0
  228. package/dist/shellRuntime.svelte.js +27 -0
  229. package/dist/state/backends.d.ts +26 -0
  230. package/dist/state/backends.js +99 -0
  231. package/dist/state/manage.d.ts +14 -0
  232. package/dist/state/manage.js +40 -0
  233. package/dist/state/types.d.ts +55 -0
  234. package/dist/state/types.js +17 -0
  235. package/dist/state/zones.svelte.d.ts +53 -0
  236. package/dist/state/zones.svelte.js +141 -0
  237. package/dist/theme.d.ts +28 -0
  238. package/dist/theme.js +92 -0
  239. package/dist/tokens.css +102 -0
  240. package/dist/version.d.ts +2 -0
  241. package/dist/version.js +2 -0
  242. package/package.json +60 -0
@@ -0,0 +1,38 @@
1
+ import ZonesTable from '../rich/ZonesTable.svelte';
2
+ import ZoneTree from '../rich/ZoneTree.svelte';
3
+ export const zonesVerb = {
4
+ name: 'zones',
5
+ summary: 'List zones for the current user (optionally scoped to a shard).',
6
+ async run(ctx, args) {
7
+ const rows = ctx.shell.listZones(args[0]);
8
+ ctx.scrollback.push({
9
+ kind: 'rich',
10
+ component: ZonesTable,
11
+ props: { data: { rows } },
12
+ ts: Date.now(),
13
+ });
14
+ },
15
+ };
16
+ export const zoneVerb = {
17
+ name: 'zone',
18
+ summary: 'Dump the contents of a zone as a collapsible JSON tree.',
19
+ async run(ctx, args) {
20
+ const [shardId, zoneName] = args;
21
+ if (!shardId || !zoneName) {
22
+ ctx.scrollback.push({
23
+ kind: 'status',
24
+ text: 'usage: zone <shardId> <zoneName>',
25
+ level: 'warn',
26
+ ts: Date.now(),
27
+ });
28
+ return;
29
+ }
30
+ const value = ctx.shell.readZone(shardId, zoneName);
31
+ ctx.scrollback.push({
32
+ kind: 'rich',
33
+ component: ZoneTree,
34
+ props: { data: { value } },
35
+ ts: Date.now(),
36
+ });
37
+ },
38
+ };
@@ -0,0 +1,27 @@
1
+ import type { StateZones } from './state/zones.svelte';
2
+ import type { ZoneSchema } from './state/types';
3
+ import { type ModalManager } from './overlays/modal';
4
+ import { type PopupManager } from './overlays/popup';
5
+ import { type ToastManager } from './overlays/toast';
6
+ /**
7
+ * The process-wide shell singleton exposed to shards and the shell's own
8
+ * internal code. Provides state zone creation and overlay managers.
9
+ * Shards receive a pre-bound version via `ShardContext` rather than
10
+ * accessing `shell` directly.
11
+ */
12
+ export interface Shell {
13
+ /**
14
+ * Declare the state zones a shard (or the shell itself) wants to use.
15
+ * Returns a live reactive object per declared zone. Persistent zones
16
+ * are hydrated from the backend before the call returns.
17
+ */
18
+ state<T extends ZoneSchema>(shardId: string, schema: T): StateZones<T>;
19
+ /** Stackable, focus-trapped dialogs. See overlays/modal.ts. */
20
+ modal: ModalManager;
21
+ /** Anchored, single-item, outside-click-dismissable popups. */
22
+ popup: PopupManager;
23
+ /** Auto-dismissing notification toasts. */
24
+ toast: ToastManager;
25
+ }
26
+ /** The process-wide shell instance. Framework-internal code uses this directly; shards receive a scoped view via `ShardContext`. */
27
+ export declare const shell: Shell;
@@ -0,0 +1,27 @@
1
+ /*
2
+ * shell — the framework-facing entry point passed to shards.
3
+ *
4
+ * Conceptually this is the object a shard receives from its activation
5
+ * callback; it exposes the APIs the shard is allowed to touch.
6
+ *
7
+ * Phase 3: state zones.
8
+ * Phase 4: registerView (lives on ShardContext, not here).
9
+ * Phase 5: modal / popup / toast overlay managers.
10
+ *
11
+ * For now `shell` is a module-level singleton. Once the shard lifecycle
12
+ * exists in a fuller form, per-shard shells will be constructed per
13
+ * activation with the shardId baked in, so shards no longer need to pass
14
+ * their own id to `state`. Overlay managers stay process-wide because
15
+ * the layer stack is a shell-global resource.
16
+ */
17
+ import { createStateZones } from './state/zones.svelte';
18
+ import { modalManager } from './overlays/modal';
19
+ import { popupManager } from './overlays/popup';
20
+ import { toastManager } from './overlays/toast';
21
+ /** The process-wide shell instance. Framework-internal code uses this directly; shards receive a scoped view via `ShardContext`. */
22
+ export const shell = {
23
+ state: createStateZones,
24
+ modal: modalManager,
25
+ popup: popupManager,
26
+ toast: toastManager,
27
+ };
@@ -0,0 +1,26 @@
1
+ import type { Backend } from './types';
2
+ export declare class MemoryBackend implements Backend {
3
+ #private;
4
+ read(shardId: string): unknown | undefined;
5
+ write(shardId: string, value: unknown): void;
6
+ delete(shardId: string): void;
7
+ list(): string[];
8
+ }
9
+ /**
10
+ * Persists one JSON blob per shardId under a prefixed localStorage key.
11
+ *
12
+ * Keys are of the form `${prefix}${shardId}`. Each zone that uses this
13
+ * backend picks its own prefix so keys don't collide across zones (e.g.
14
+ * `sh3:workspace:` for the workspace zone, `sh3:user:` for the user zone).
15
+ *
16
+ * Values are JSON-serialized; non-serializable values are silently dropped
17
+ * by `JSON.stringify`. Callers are expected to keep zone contents plain.
18
+ */
19
+ export declare class LocalStorageBackend implements Backend {
20
+ #private;
21
+ constructor(prefix: string);
22
+ read(shardId: string): unknown | undefined;
23
+ write(shardId: string, value: unknown): void;
24
+ delete(shardId: string): void;
25
+ list(): string[];
26
+ }
@@ -0,0 +1,99 @@
1
+ /*
2
+ * Backend implementations for phase 3.
3
+ *
4
+ * MemoryBackend — ephemeral/session zones (and tests)
5
+ * LocalStorageBackend — workspace/user zones on web
6
+ *
7
+ * Tauri FS backends, IndexedDB, and remote sync are deferred per the
8
+ * roadmap. Any future backend need only conform to the `Backend` interface
9
+ * in ./types.ts for shard code to work against it unchanged.
10
+ */
11
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
12
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
13
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
14
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
15
+ };
16
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
17
+ if (kind === "m") throw new TypeError("Private method is not writable");
18
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
19
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
20
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
21
+ };
22
+ var _MemoryBackend_map, _LocalStorageBackend_prefix;
23
+ export class MemoryBackend {
24
+ constructor() {
25
+ _MemoryBackend_map.set(this, new Map());
26
+ }
27
+ read(shardId) {
28
+ return __classPrivateFieldGet(this, _MemoryBackend_map, "f").get(shardId);
29
+ }
30
+ write(shardId, value) {
31
+ __classPrivateFieldGet(this, _MemoryBackend_map, "f").set(shardId, value);
32
+ }
33
+ delete(shardId) {
34
+ __classPrivateFieldGet(this, _MemoryBackend_map, "f").delete(shardId);
35
+ }
36
+ list() {
37
+ return [...__classPrivateFieldGet(this, _MemoryBackend_map, "f").keys()];
38
+ }
39
+ }
40
+ _MemoryBackend_map = new WeakMap();
41
+ /**
42
+ * Persists one JSON blob per shardId under a prefixed localStorage key.
43
+ *
44
+ * Keys are of the form `${prefix}${shardId}`. Each zone that uses this
45
+ * backend picks its own prefix so keys don't collide across zones (e.g.
46
+ * `sh3:workspace:` for the workspace zone, `sh3:user:` for the user zone).
47
+ *
48
+ * Values are JSON-serialized; non-serializable values are silently dropped
49
+ * by `JSON.stringify`. Callers are expected to keep zone contents plain.
50
+ */
51
+ export class LocalStorageBackend {
52
+ constructor(prefix) {
53
+ _LocalStorageBackend_prefix.set(this, void 0);
54
+ __classPrivateFieldSet(this, _LocalStorageBackend_prefix, prefix, "f");
55
+ }
56
+ read(shardId) {
57
+ if (typeof localStorage === 'undefined')
58
+ return undefined;
59
+ const raw = localStorage.getItem(__classPrivateFieldGet(this, _LocalStorageBackend_prefix, "f") + shardId);
60
+ if (raw == null)
61
+ return undefined;
62
+ try {
63
+ return JSON.parse(raw);
64
+ }
65
+ catch (_a) {
66
+ // Corrupt entry — treat as missing rather than crashing the shard.
67
+ return undefined;
68
+ }
69
+ }
70
+ write(shardId, value) {
71
+ if (typeof localStorage === 'undefined')
72
+ return;
73
+ try {
74
+ localStorage.setItem(__classPrivateFieldGet(this, _LocalStorageBackend_prefix, "f") + shardId, JSON.stringify(value));
75
+ }
76
+ catch (_a) {
77
+ // Quota exceeded or serialization failure — swallow for phase 3;
78
+ // phase "post-prototype" will surface a proper error channel.
79
+ }
80
+ }
81
+ delete(shardId) {
82
+ if (typeof localStorage === 'undefined')
83
+ return;
84
+ localStorage.removeItem(__classPrivateFieldGet(this, _LocalStorageBackend_prefix, "f") + shardId);
85
+ }
86
+ list() {
87
+ if (typeof localStorage === 'undefined')
88
+ return [];
89
+ const out = [];
90
+ for (let i = 0; i < localStorage.length; i++) {
91
+ const key = localStorage.key(i);
92
+ if (key && key.startsWith(__classPrivateFieldGet(this, _LocalStorageBackend_prefix, "f"))) {
93
+ out.push(key.slice(__classPrivateFieldGet(this, _LocalStorageBackend_prefix, "f").length));
94
+ }
95
+ }
96
+ return out;
97
+ }
98
+ }
99
+ _LocalStorageBackend_prefix = new WeakMap();
@@ -0,0 +1,14 @@
1
+ import type { ZoneName, ZoneManager } from './types';
2
+ /** Return all shard IDs that have data in the given zone. */
3
+ export declare function listZoneEntries(zone: ZoneName): string[];
4
+ /** Read a raw zone entry without creating a reactive proxy. */
5
+ export declare function peekZoneEntry(zone: ZoneName, shardId: string): unknown | undefined;
6
+ /** Delete one shard's data from a zone. */
7
+ export declare function clearZoneEntry(zone: ZoneName, shardId: string): void;
8
+ /** Delete all entries in a zone. Safe because `list()` returns a snapshot array. */
9
+ export declare function clearAllZoneEntries(zone: ZoneName): void;
10
+ /**
11
+ * Build a `ZoneManager` object. Called by context factories when the
12
+ * manifest declares the `state:manage` permission.
13
+ */
14
+ export declare function createZoneManager(): ZoneManager;
@@ -0,0 +1,40 @@
1
+ /*
2
+ * Zone management — public wrappers over the private backends for
3
+ * cross-shard state inspection and cleanup.
4
+ *
5
+ * These functions are not part of the shard-facing API directly.
6
+ * They are consumed by `createZoneManager()` which builds the
7
+ * `ZoneManager` object conditionally attached to contexts when
8
+ * the manifest declares the `state:manage` permission.
9
+ */
10
+ import { backends } from './zones.svelte';
11
+ /** Return all shard IDs that have data in the given zone. */
12
+ export function listZoneEntries(zone) {
13
+ return backends[zone].list();
14
+ }
15
+ /** Read a raw zone entry without creating a reactive proxy. */
16
+ export function peekZoneEntry(zone, shardId) {
17
+ return backends[zone].read(shardId);
18
+ }
19
+ /** Delete one shard's data from a zone. */
20
+ export function clearZoneEntry(zone, shardId) {
21
+ backends[zone].delete(shardId);
22
+ }
23
+ /** Delete all entries in a zone. Safe because `list()` returns a snapshot array. */
24
+ export function clearAllZoneEntries(zone) {
25
+ for (const id of backends[zone].list()) {
26
+ backends[zone].delete(id);
27
+ }
28
+ }
29
+ /**
30
+ * Build a `ZoneManager` object. Called by context factories when the
31
+ * manifest declares the `state:manage` permission.
32
+ */
33
+ export function createZoneManager() {
34
+ return {
35
+ list: listZoneEntries,
36
+ peek: peekZoneEntry,
37
+ clear: clearZoneEntry,
38
+ clearAll: clearAllZoneEntries,
39
+ };
40
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The four state zones available to shards. Each zone has different lifetime
3
+ * and persistence semantics:
4
+ * - `ephemeral`: in-memory only; cleared when the shard is unloaded.
5
+ * - `session`: in-memory only; cleared when the app is closed.
6
+ * - `workspace`: persisted to localStorage; survives page reload.
7
+ * - `user`: persisted to localStorage; shared across all workspaces for this user.
8
+ */
9
+ export type ZoneName = 'ephemeral' | 'session' | 'workspace' | 'user';
10
+ /** Zones whose contents are flushed to a persistent backend on change. */
11
+ export declare const PERSISTENT_ZONES: readonly ZoneName[];
12
+ /**
13
+ * A backend is a tiny KV store keyed by shardId. Each zone owns one backend.
14
+ * Values are arbitrary JSON-serializable objects — the backend is responsible
15
+ * for any (de)serialization needed for its medium.
16
+ *
17
+ * Future backends: IndexedDB, Tauri FS, remote sync — all conform to this
18
+ * interface so shard code is unaffected by deployment target.
19
+ */
20
+ export interface Backend {
21
+ /** Read the stored value for a shard. Returns undefined if not present. */
22
+ read(shardId: string): unknown | undefined;
23
+ /** Write (create or overwrite) the stored value for a shard. */
24
+ write(shardId: string, value: unknown): void;
25
+ /** Delete the stored value for a shard. No-op if not present. */
26
+ delete(shardId: string): void;
27
+ /** Return all shard ids that have stored entries in this backend. */
28
+ list(): string[];
29
+ }
30
+ /**
31
+ * A zone schema is a record of zone name → initial state object. Any subset
32
+ * of zones may be declared; unused zones are simply omitted. Values inside
33
+ * each zone object become the defaults used when the backend has no prior
34
+ * entry for this shard.
35
+ */
36
+ export type ZoneSchema = {
37
+ [K in ZoneName]?: Record<string, unknown>;
38
+ };
39
+ /**
40
+ * Cross-shard zone management API. Allows enumeration, inspection, and
41
+ * cleanup of zone data across all shards. Only available on contexts
42
+ * whose manifest declares the `state:manage` permission.
43
+ */
44
+ export interface ZoneManager {
45
+ /** Return all shard IDs that have data in the given zone. */
46
+ list(zone: ZoneName): string[];
47
+ /** Read a raw zone entry without creating a reactive proxy. Returns undefined if absent. */
48
+ peek(zone: ZoneName, shardId: string): unknown | undefined;
49
+ /** Delete one shard's data from a zone. */
50
+ clear(zone: ZoneName, shardId: string): void;
51
+ /** Delete all entries in a zone. */
52
+ clearAll(zone: ZoneName): void;
53
+ }
54
+ /** Permission string for cross-shard zone management access. */
55
+ export declare const PERMISSION_STATE_MANAGE = "state:manage";
@@ -0,0 +1,17 @@
1
+ /*
2
+ * State zone types — framework plumbing for docs/design/state-zones.md.
3
+ *
4
+ * Phase 3 implements four of the six zones:
5
+ * ephemeral — in-memory, cleared on shard unload
6
+ * session — in-memory, cleared on app close
7
+ * workspace — persisted (localStorage KV for web)
8
+ * user — persisted (localStorage KV for web)
9
+ *
10
+ * Deferred until later phases:
11
+ * document — file-on-disk (phase "post-prototype")
12
+ * shared — lives on the bus, not storage (not a zone in the same sense)
13
+ */
14
+ /** Zones whose contents are flushed to a persistent backend on change. */
15
+ export const PERSISTENT_ZONES = ['workspace', 'user'];
16
+ /** Permission string for cross-shard zone management access. */
17
+ export const PERMISSION_STATE_MANAGE = 'state:manage';
@@ -0,0 +1,53 @@
1
+ import type { Backend, ZoneName, ZoneSchema } from './types';
2
+ export declare const backends: Record<ZoneName, Backend>;
3
+ /**
4
+ * Live reactive state object returned by `createStateZones`. Each key
5
+ * mirrors a zone declared in the schema; the value is a deeply-reactive
6
+ * proxy of the same shape. Undeclared zones are absent from the result.
7
+ *
8
+ * @typeParam T - The `ZoneSchema` used to declare zones and their defaults.
9
+ */
10
+ export type StateZones<T extends ZoneSchema> = {
11
+ [K in keyof T]: T[K];
12
+ };
13
+ /**
14
+ * Create live reactive state zones for a shard (or the shell itself).
15
+ *
16
+ * Each zone declared in `schema` is backed by the appropriate store:
17
+ * `ephemeral` and `session` are in-memory; `workspace` and `user` are
18
+ * persisted to localStorage and hydrated before this call returns.
19
+ *
20
+ * Writes to persistent zones are debounced and coalesced per microtask.
21
+ * Zones not declared in `schema` are not created; the result only
22
+ * contains the keys the caller asked for.
23
+ *
24
+ * @param shardId - Unique shard identifier used as the storage namespace.
25
+ * @param schema - Record of zone names to their default values.
26
+ * @returns A reactive `StateZones<T>` object keyed to the declared zones.
27
+ */
28
+ export declare function createStateZones<T extends ZoneSchema>(shardId: string, schema: T): StateZones<T>;
29
+ /**
30
+ * Swap a backend implementation. Intended for tests and for phase 4+ when
31
+ * Tauri or IndexedDB backends replace the defaults. Not part of the shard-
32
+ * facing API.
33
+ */
34
+ export declare function __setBackend(zone: ZoneName, backend: Backend): void;
35
+ /**
36
+ * Read a raw persisted zone entry without creating a reactive proxy.
37
+ *
38
+ * Intended for framework-internal consumers (e.g. the shell's layout
39
+ * persistence) that need to inspect a stored value before deciding
40
+ * whether to pass it to `createStateZones`. Not part of the shard-facing
41
+ * API — shards always go through `createStateZones` so hydration is
42
+ * reactive and debounced-flush applies.
43
+ *
44
+ * Returns `undefined` for missing, corrupt, or memory-only-zone entries
45
+ * that have no stored value.
46
+ */
47
+ export declare function peekZone(zone: ZoneName, shardId: string): unknown | undefined;
48
+ /**
49
+ * Delete a persisted zone entry. Intended for framework-internal use
50
+ * when a stored value is known to be incompatible (version mismatch,
51
+ * corruption) and should be removed before `createStateZones` hydrates.
52
+ */
53
+ export declare function clearZone(zone: ZoneName, shardId: string): void;
@@ -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
+ export 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,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:__sh3core__: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
+ }