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,125 @@
1
+ /*
2
+ * Registry shard — framework-shipped shard for managing packages on the
3
+ * local SH3 server's registry.
4
+ *
5
+ * Contributes one view:
6
+ * - `sh3-registry:manage` — package list, publish, edit, update, delete
7
+ *
8
+ * Auth is handled via getAuthHeader() from the framework's auth module.
9
+ * No user zone — all server calls include the Authorization header
10
+ * automatically.
11
+ *
12
+ * `.svelte.ts` because mounting Svelte components requires rune access.
13
+ */
14
+ import { mount, unmount } from 'svelte';
15
+ import RegistryView from './RegistryView.svelte';
16
+ import { getAuthHeader } from '../auth/index';
17
+ /**
18
+ * Module-level context set during activate(). Imported by the Svelte
19
+ * view component so it can read/write state and trigger actions.
20
+ */
21
+ export let registryContext = undefined;
22
+ /** Build fetch headers with auth. Throws if not elevated. */
23
+ function authHeaders(contentType) {
24
+ const auth = getAuthHeader();
25
+ const headers = {};
26
+ if (auth)
27
+ headers['Authorization'] = auth;
28
+ if (contentType)
29
+ headers['Content-Type'] = contentType;
30
+ return headers;
31
+ }
32
+ /** Server base URL — same-origin. */
33
+ const apiBase = '';
34
+ export const registryShard = {
35
+ manifest: {
36
+ id: 'sh3-registry',
37
+ label: 'Registry Manager',
38
+ version: '0.1.0',
39
+ views: [
40
+ { id: 'sh3-registry:manage', label: 'Registry' },
41
+ ],
42
+ },
43
+ activate(ctx) {
44
+ const state = ctx.state({
45
+ ephemeral: {
46
+ packages: [],
47
+ loading: false,
48
+ error: null,
49
+ },
50
+ });
51
+ async function refreshPackages() {
52
+ var _a;
53
+ state.ephemeral.loading = true;
54
+ state.ephemeral.error = null;
55
+ try {
56
+ const res = await fetch(`${apiBase}/registry.json`);
57
+ if (!res.ok)
58
+ throw new Error(`HTTP ${res.status} ${res.statusText}`);
59
+ const data = await res.json();
60
+ state.ephemeral.packages = (_a = data.packages) !== null && _a !== void 0 ? _a : [];
61
+ }
62
+ catch (err) {
63
+ state.ephemeral.error = err instanceof Error ? err.message : String(err);
64
+ }
65
+ finally {
66
+ state.ephemeral.loading = false;
67
+ }
68
+ }
69
+ async function publishPackage(form) {
70
+ var _a;
71
+ const res = await fetch(`${apiBase}/api/registry/publish`, {
72
+ method: 'POST',
73
+ headers: authHeaders(),
74
+ body: form,
75
+ });
76
+ if (!res.ok) {
77
+ const data = await res.json().catch(() => ({ error: res.statusText }));
78
+ throw new Error((_a = data.error) !== null && _a !== void 0 ? _a : `HTTP ${res.status}`);
79
+ }
80
+ await refreshPackages();
81
+ }
82
+ async function patchPackage(id, fields) {
83
+ var _a;
84
+ const res = await fetch(`${apiBase}/api/registry/packages/${id}`, {
85
+ method: 'PATCH',
86
+ headers: authHeaders('application/json'),
87
+ body: JSON.stringify(fields),
88
+ });
89
+ if (!res.ok) {
90
+ const data = await res.json().catch(() => ({ error: res.statusText }));
91
+ throw new Error((_a = data.error) !== null && _a !== void 0 ? _a : `HTTP ${res.status}`);
92
+ }
93
+ await refreshPackages();
94
+ }
95
+ async function deletePackage(id) {
96
+ var _a;
97
+ const res = await fetch(`${apiBase}/api/registry/packages/${id}`, {
98
+ method: 'DELETE',
99
+ headers: authHeaders(),
100
+ });
101
+ if (!res.ok) {
102
+ const data = await res.json().catch(() => ({ error: res.statusText }));
103
+ throw new Error((_a = data.error) !== null && _a !== void 0 ? _a : `HTTP ${res.status}`);
104
+ }
105
+ await refreshPackages();
106
+ }
107
+ // Set the module-level context so the view component can import it.
108
+ registryContext = { state, refreshPackages, publishPackage, patchPackage, deletePackage };
109
+ const manageFactory = {
110
+ mount(container, _context) {
111
+ const instance = mount(RegistryView, { target: container });
112
+ void refreshPackages();
113
+ return {
114
+ unmount() {
115
+ unmount(instance);
116
+ },
117
+ };
118
+ },
119
+ };
120
+ ctx.registerView('sh3-registry:manage', manageFactory);
121
+ },
122
+ autostart() {
123
+ // Self-starting so the view is available from the launcher.
124
+ },
125
+ };
@@ -0,0 +1,45 @@
1
+ import type { Shard } from './types';
2
+ /**
3
+ * Reactive registry of every shard known to the host. Keys are shard ids.
4
+ * Populated once at boot by the glob-discovery loop in main.ts (through
5
+ * `registerShard`); a future runtime loader appends late without touching
6
+ * the rest of the framework. Callers that need a live list subscribe via
7
+ * Svelte reactivity (e.g. shell home listing apps cross-references this
8
+ * map to show which shards they require).
9
+ */
10
+ export declare const registeredShards: Map<string, Shard>;
11
+ export declare const activeShards: Map<string, Shard>;
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).
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.
19
+ */
20
+ export declare function registerShard(shard: Shard): void;
21
+ /**
22
+ * Activate a registered shard. Builds a `ShardContext`, calls `shard.activate`,
23
+ * verifies that every view declared in the manifest received a factory, then
24
+ * calls `shard.autostart` if defined. Idempotent — calling on an already-active
25
+ * shard is a no-op.
26
+ *
27
+ * @param id - The `ShardManifest.id` of the shard to activate. Must be registered.
28
+ * @throws If the shard is not registered, or if a manifest view has no factory after activation.
29
+ */
30
+ export declare function activateShard(id: string): Promise<void>;
31
+ /**
32
+ * Deactivate an active shard. Calls `shard.deactivate`, flushes and disposes
33
+ * all document handles, unregisters all view factories, and removes the shard
34
+ * from `activeShards`. The shard remains in `registeredShards` and can be
35
+ * re-activated. No-op if the shard is not currently active.
36
+ *
37
+ * @param id - The `ShardManifest.id` of the shard to deactivate.
38
+ */
39
+ export declare function deactivateShard(id: string): void;
40
+ /**
41
+ * Return true if the shard with the given id is currently active.
42
+ *
43
+ * @param id - The `ShardManifest.id` to check.
44
+ */
45
+ export declare function isActive(id: string): boolean;
@@ -0,0 +1,124 @@
1
+ /*
2
+ * Shard lifecycle — phase 8 split into register and activate.
3
+ *
4
+ * registerShard(shard): adds the shard's manifest to the reactive
5
+ * `registeredShards` map. No side effects. The host (main.ts) calls
6
+ * this once per shard via its glob-discovery boot loop. A future
7
+ * runtime loader calls it after fetching a shard module.
8
+ *
9
+ * activateShard(id): looks up a registered shard, builds its
10
+ * ShardContext, runs `activate(ctx)`, verifies every manifest view id
11
+ * received a factory, then runs `autostart(ctx)` if present. The shard
12
+ * moves into the reactive `activeShards` map. Called internally by
13
+ * app launch and by the bootstrap self-starting pass. Not public.
14
+ *
15
+ * deactivateShard(id): inverse of activate. Calls `deactivate()`,
16
+ * unregisters view factories, drops from `activeShards`. The shard
17
+ * stays in `registeredShards` — it's still known, just not running.
18
+ */
19
+ import { shell } from '../shellRuntime.svelte';
20
+ import { registerView, unregisterView } from './registry';
21
+ import { createDocumentHandle, getTenantId, getDocumentBackend } from '../documents';
22
+ /**
23
+ * Reactive registry of every shard known to the host. Keys are shard ids.
24
+ * Populated once at boot by the glob-discovery loop in main.ts (through
25
+ * `registerShard`); a future runtime loader appends late without touching
26
+ * the rest of the framework. Callers that need a live list subscribe via
27
+ * Svelte reactivity (e.g. shell home listing apps cross-references this
28
+ * map to show which shards they require).
29
+ */
30
+ export const registeredShards = $state(new Map());
31
+ /**
32
+ * Reactive map of currently-active shards. A shard lands here when
33
+ * `activateShard` runs successfully and stays until `deactivateShard`.
34
+ */
35
+ const active = new Map();
36
+ export const activeShards = $state(new Map());
37
+ /**
38
+ * Register a shard with the framework so it can later be activated. Records
39
+ * the shard in `registeredShards` but does not run `activate` — that happens
40
+ * at app launch (or immediately for self-starting shards).
41
+ *
42
+ * @param shard - The shard module to register. `shard.manifest.id` must be unique.
43
+ * @throws If a shard with the same id is already registered.
44
+ */
45
+ export function registerShard(shard) {
46
+ const id = shard.manifest.id;
47
+ if (registeredShards.has(id)) {
48
+ throw new Error(`Shard "${id}" is already registered`);
49
+ }
50
+ registeredShards.set(id, shard);
51
+ }
52
+ /**
53
+ * Activate a registered shard. Builds a `ShardContext`, calls `shard.activate`,
54
+ * verifies that every view declared in the manifest received a factory, then
55
+ * calls `shard.autostart` if defined. Idempotent — calling on an already-active
56
+ * shard is a no-op.
57
+ *
58
+ * @param id - The `ShardManifest.id` of the shard to activate. Must be registered.
59
+ * @throws If the shard is not registered, or if a manifest view has no factory after activation.
60
+ */
61
+ export async function activateShard(id) {
62
+ var _a;
63
+ const shard = registeredShards.get(id);
64
+ if (!shard) {
65
+ throw new Error(`Cannot activate shard "${id}": not registered`);
66
+ }
67
+ if (active.has(id)) {
68
+ // Already active (e.g. self-starting shard that was activated at boot
69
+ // and is now being required by an app). Idempotent — no error.
70
+ return;
71
+ }
72
+ const entry = { shard, viewIds: new Set(), cleanupFns: [] };
73
+ const ctx = {
74
+ state: (schema) => shell.state(id, schema),
75
+ registerView: (viewId, factory) => {
76
+ registerView(viewId, factory);
77
+ entry.viewIds.add(viewId);
78
+ },
79
+ documents: (options) => {
80
+ const handle = createDocumentHandle(getTenantId(), id, getDocumentBackend(), options);
81
+ entry.cleanupFns.push(() => handle.dispose());
82
+ return handle;
83
+ },
84
+ };
85
+ active.set(id, entry);
86
+ activeShards.set(id, shard);
87
+ await shard.activate(ctx);
88
+ for (const view of shard.manifest.views) {
89
+ if (!entry.viewIds.has(view.id)) {
90
+ throw new Error(`Shard "${id}" declared view "${view.id}" in its manifest but registered no factory for it.`);
91
+ }
92
+ }
93
+ void ((_a = shard.autostart) === null || _a === void 0 ? void 0 : _a.call(shard, ctx));
94
+ }
95
+ /**
96
+ * Deactivate an active shard. Calls `shard.deactivate`, flushes and disposes
97
+ * all document handles, unregisters all view factories, and removes the shard
98
+ * from `activeShards`. The shard remains in `registeredShards` and can be
99
+ * re-activated. No-op if the shard is not currently active.
100
+ *
101
+ * @param id - The `ShardManifest.id` of the shard to deactivate.
102
+ */
103
+ export function deactivateShard(id) {
104
+ var _a, _b;
105
+ const entry = active.get(id);
106
+ if (!entry)
107
+ return;
108
+ void ((_b = (_a = entry.shard).deactivate) === null || _b === void 0 ? void 0 : _b.call(_a));
109
+ // Flush and dispose document handles before tearing down views.
110
+ for (const fn of entry.cleanupFns)
111
+ void fn();
112
+ for (const viewId of entry.viewIds)
113
+ unregisterView(viewId);
114
+ active.delete(id);
115
+ activeShards.delete(id);
116
+ }
117
+ /**
118
+ * Return true if the shard with the given id is currently active.
119
+ *
120
+ * @param id - The `ShardManifest.id` to check.
121
+ */
122
+ export function isActive(id) {
123
+ return active.has(id);
124
+ }
@@ -0,0 +1,4 @@
1
+ import type { ViewFactory } from './types';
2
+ export declare function registerView(viewId: string, factory: ViewFactory): void;
3
+ export declare function getView(viewId: string): ViewFactory | undefined;
4
+ export declare function unregisterView(viewId: string): void;
@@ -0,0 +1,28 @@
1
+ /*
2
+ * Contribution registry — phase 4 stub.
3
+ *
4
+ * Tracks which ViewFactory answers a given viewId. In this phase the
5
+ * registry is a flat module-level Map with no awareness of shard identity;
6
+ * writes come from `activateShard` which additionally remembers which
7
+ * viewIds a given shard registered so they can be torn down in
8
+ * `deactivateShard`.
9
+ *
10
+ * The shape of this registry is deliberately narrow so later phases can
11
+ * expand it without breaking callers:
12
+ * - Resolution by viewId is the only query slots need.
13
+ * - Commands, toolbar items, menus, hotkeys get their own sibling maps
14
+ * (one per contribution kind) when those kinds land.
15
+ */
16
+ const views = new Map();
17
+ export function registerView(viewId, factory) {
18
+ if (views.has(viewId)) {
19
+ throw new Error(`View "${viewId}" is already registered`);
20
+ }
21
+ views.set(viewId, factory);
22
+ }
23
+ export function getView(viewId) {
24
+ return views.get(viewId);
25
+ }
26
+ export function unregisterView(viewId) {
27
+ views.delete(viewId);
28
+ }
@@ -0,0 +1,155 @@
1
+ import type { StateZones } from '../state/zones.svelte';
2
+ import type { ZoneSchema } from '../state/types';
3
+ import type { DocumentHandle, DocumentHandleOptions } from '../documents/types';
4
+ /**
5
+ * The object returned by `ViewFactory.mount`. The framework calls
6
+ * `unmount()` when the slot goes away, and `onResize(w, h)` whenever the
7
+ * slot's container changes size (including splitter drags and window
8
+ * resize). `remountOnMove` is the opt-out from the re-parenting-survival
9
+ * contract (rare, GL-edge-case shards); phase 4 does not exercise it.
10
+ */
11
+ export interface ViewHandle {
12
+ /** Called by the framework when the slot is removed from the layout. Release all resources here. */
13
+ unmount(): void;
14
+ /** Optional callback invoked when the slot's container dimensions change. */
15
+ onResize?(width: number, height: number): void;
16
+ /**
17
+ * When true the framework will unmount and remount this view if its slot
18
+ * moves to a different DOM parent (e.g. a splitter drag). Set to false
19
+ * (or omit) if the view can survive DOM re-parenting without reinitializing.
20
+ */
21
+ remountOnMove?: boolean;
22
+ /**
23
+ * Closability mode for this view instance.
24
+ * - undefined / false: non-closable (no close button rendered).
25
+ * - true: pure close — instant removal, no confirmation.
26
+ * - { canClose() }: guarded close — layout engine awaits the promise;
27
+ * resolve true to allow, false to cancel.
28
+ */
29
+ closable?: boolean | {
30
+ canClose(): Promise<boolean>;
31
+ };
32
+ }
33
+ /**
34
+ * Context passed to `ViewFactory.mount` so the view knows which layout
35
+ * instance it is and can push reactive metadata back to the tab strip.
36
+ */
37
+ export interface MountContext {
38
+ /** Stable identifier for the slot this view is mounted into. */
39
+ slotId: string;
40
+ /** The view id that was used to look up this factory. */
41
+ viewId: string;
42
+ /** Initial label for the tab; may be updated by the view via future API. */
43
+ label: string;
44
+ /**
45
+ * Push dirty-state to the tab strip. The framework renders a dirty
46
+ * indicator (filled dot) on the tab when true, clears it when false.
47
+ * Call this whenever the view's save-state changes.
48
+ */
49
+ setDirty(dirty: boolean): void;
50
+ }
51
+ /**
52
+ * The shard-side adapter that knows how to bring a view to life inside a
53
+ * given HTMLElement. The container is owned by the framework (the slot);
54
+ * the factory writes into it and returns a ViewHandle.
55
+ */
56
+ export interface ViewFactory {
57
+ /**
58
+ * Mount the view into `container`. The container element is owned and sized
59
+ * by the framework; the factory should not modify its dimensions or position.
60
+ * Returns a `ViewHandle` the framework uses to communicate with the view.
61
+ */
62
+ mount(container: HTMLElement, context: MountContext): ViewHandle;
63
+ }
64
+ /**
65
+ * Static description of a view a shard provides. Lives inside
66
+ * `ShardManifest.views` so apps and tooling can enumerate a shard's views
67
+ * without running `activate()`. Phase 8 only uses `id` and `label`; `icon`
68
+ * is reserved and not rendered yet.
69
+ */
70
+ export interface ViewDeclaration {
71
+ /** Unique view id within this shard. Used to look up the factory at mount time. */
72
+ id: string;
73
+ /** Human-readable label used in tab strips and launchers. */
74
+ label: string;
75
+ /** Optional icon hint (reserved; not yet rendered in phase 8). */
76
+ icon?: string;
77
+ }
78
+ /**
79
+ * Static description of a shard. Declared once and read by the framework
80
+ * before `activate` runs, so the shell can enumerate a shard's capabilities
81
+ * without executing any shard code.
82
+ */
83
+ export interface ShardManifest {
84
+ /** Unique shard identifier. Used as the state namespace and view id prefix. */
85
+ id: string;
86
+ /** Human-readable display name. */
87
+ label: string;
88
+ /** Semver version string for the shard package. */
89
+ version: string;
90
+ /**
91
+ * Static list of the view ids this shard provides. Every id listed here
92
+ * must be backed by a `ctx.registerView(id, factory)` call from within
93
+ * `activate()`; the framework verifies this after `activate` returns and
94
+ * throws on any missing factory. Phase 8 adds this field; shards that
95
+ * predate phase 8 must be updated to declare their views here.
96
+ */
97
+ views: ViewDeclaration[];
98
+ }
99
+ /**
100
+ * Handed to `shard.activate`. The shard uses it to declare state and
101
+ * register contributions. `state` is pre-bound to the shard's id so the
102
+ * shard never has to pass its own id through — the framework guarantees
103
+ * isolation by construction.
104
+ */
105
+ export interface ShardContext {
106
+ /**
107
+ * Declare the state zones this shard uses and receive a live reactive
108
+ * object. The shard id is baked in — shards never pass their own id.
109
+ * Persistent zones (`workspace`, `user`) are hydrated before the call
110
+ * returns.
111
+ *
112
+ * @param schema - Zone names mapped to their default values.
113
+ */
114
+ state<T extends ZoneSchema>(schema: T): StateZones<T>;
115
+ /**
116
+ * Register a view factory for a view id declared in the shard manifest.
117
+ * Must be called for every id listed in `manifest.views` during `activate`.
118
+ *
119
+ * @param viewId - Must match an entry in `manifest.views`.
120
+ * @param factory - The adapter that mounts the view into a container element.
121
+ */
122
+ registerView(viewId: string, factory: ViewFactory): void;
123
+ /** Obtain a file-oriented document handle scoped to this shard. */
124
+ documents(options: DocumentHandleOptions): DocumentHandle;
125
+ }
126
+ /**
127
+ * A shard module. Shards are the fundamental unit of contribution in SH3.
128
+ * Each shard activates once, receives a `ShardContext`, and registers views
129
+ * and other capabilities that the shell and apps can use. Shard lifecycle is
130
+ * separate from view lifecycle — a shard may be active with no views visible.
131
+ */
132
+ export interface Shard {
133
+ /** Static description of this shard's identity and declared contributions. */
134
+ manifest: ShardManifest;
135
+ /**
136
+ * Run once when the shard is activated. Use `ctx` to declare state zones,
137
+ * register view factories, and obtain document handles. Must register a
138
+ * factory for every view id declared in `manifest.views`.
139
+ */
140
+ activate(ctx: ShardContext): void | Promise<void>;
141
+ /**
142
+ * Optional self-starting hook. A shard that defines `autostart` is
143
+ * eagerly activated by the framework at boot (right after the register
144
+ * pass finishes) instead of waiting for an app to require it. `activate`
145
+ * runs first; `autostart` runs immediately after and may take imperative
146
+ * action — docking its own views into the active layout, opening a
147
+ * modal, subscribing to framework state, etc. The `__shell__` pseudo-
148
+ * shard uses this with a no-op body so its activation path is uniform
149
+ * with other self-starting shards. Diagnostic-style shards use it to
150
+ * do real work.
151
+ */
152
+ autostart?(ctx: ShardContext): void | Promise<void>;
153
+ /** Optional cleanup hook called when the shard is deactivated. Release timers, subscriptions, and external resources here. */
154
+ deactivate?(): void | Promise<void>;
155
+ }
@@ -0,0 +1,20 @@
1
+ /*
2
+ * Shard contract — minimum viable draft for phase 4.
3
+ *
4
+ * A shard is a self-contained module that contributes capabilities (views,
5
+ * commands, services, …) to the shell. See docs/design/shards.md for the
6
+ * full design; phase 4 implements only the pieces needed to mount a view
7
+ * into a layout slot:
8
+ *
9
+ * - A shard declares a manifest.
10
+ * - `activate(ctx)` runs once, receives a ShardContext, and registers
11
+ * whichever contributions it wants the shell to know about.
12
+ * - A ViewFactory knows how to mount a view into a raw HTMLElement and
13
+ * return a handle the framework uses to unmount / notify of resizes.
14
+ *
15
+ * Deferred to later phases: bus scoping, command/toolbar/menu/hotkey
16
+ * registration, modal provider contributions, background services, lazy
17
+ * activation events. They'll slot into `ShardContext` as new `register*`
18
+ * methods without disturbing the phase-4 shape.
19
+ */
20
+ export {};