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.
- package/dist/Shell.svelte +185 -0
- package/dist/Shell.svelte.d.ts +4 -0
- package/dist/api.d.ts +22 -0
- package/dist/api.js +45 -0
- package/dist/apps/lifecycle.d.ts +37 -0
- package/dist/apps/lifecycle.js +153 -0
- package/dist/apps/registry.svelte.d.ts +37 -0
- package/dist/apps/registry.svelte.js +60 -0
- package/dist/apps/types.d.ts +61 -0
- package/dist/apps/types.js +10 -0
- package/dist/assets/icons.svg +1119 -0
- package/dist/auth/auth.svelte.d.ts +44 -0
- package/dist/auth/auth.svelte.js +119 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.js +1 -0
- package/dist/build.d.ts +29 -0
- package/dist/build.js +85 -0
- package/dist/contract.d.ts +20 -0
- package/dist/contract.js +28 -0
- package/dist/documents/backends.d.ts +17 -0
- package/dist/documents/backends.js +156 -0
- package/dist/documents/config.d.ts +7 -0
- package/dist/documents/config.js +27 -0
- package/dist/documents/handle.d.ts +6 -0
- package/dist/documents/handle.js +154 -0
- package/dist/documents/http-backend.d.ts +22 -0
- package/dist/documents/http-backend.js +78 -0
- package/dist/documents/index.d.ts +6 -0
- package/dist/documents/index.js +8 -0
- package/dist/documents/notifications.d.ts +9 -0
- package/dist/documents/notifications.js +39 -0
- package/dist/documents/types.d.ts +97 -0
- package/dist/documents/types.js +12 -0
- package/dist/host-entry.d.ts +9 -0
- package/dist/host-entry.js +15 -0
- package/dist/host.d.ts +13 -0
- package/dist/host.js +73 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +13 -0
- package/dist/layout/DragPreview.svelte +63 -0
- package/dist/layout/DragPreview.svelte.d.ts +3 -0
- package/dist/layout/LayoutRenderer.svelte +260 -0
- package/dist/layout/LayoutRenderer.svelte.d.ts +6 -0
- package/dist/layout/SlotContainer.svelte +140 -0
- package/dist/layout/SlotContainer.svelte.d.ts +8 -0
- package/dist/layout/SlotDropZone.svelte +122 -0
- package/dist/layout/SlotDropZone.svelte.d.ts +8 -0
- package/dist/layout/drag.svelte.d.ts +45 -0
- package/dist/layout/drag.svelte.js +191 -0
- package/dist/layout/inspection.d.ts +52 -0
- package/dist/layout/inspection.js +157 -0
- package/dist/layout/ops.d.ts +78 -0
- package/dist/layout/ops.js +281 -0
- package/dist/layout/slotHostPool.svelte.d.ts +36 -0
- package/dist/layout/slotHostPool.svelte.js +229 -0
- package/dist/layout/store.svelte.d.ts +39 -0
- package/dist/layout/store.svelte.js +150 -0
- package/dist/layout/tree-walk.d.ts +15 -0
- package/dist/layout/tree-walk.js +33 -0
- package/dist/layout/types.d.ts +108 -0
- package/dist/layout/types.js +25 -0
- package/dist/overlays/ModalFrame.svelte +87 -0
- package/dist/overlays/ModalFrame.svelte.d.ts +10 -0
- package/dist/overlays/PopupFrame.svelte +85 -0
- package/dist/overlays/PopupFrame.svelte.d.ts +10 -0
- package/dist/overlays/ToastItem.svelte +77 -0
- package/dist/overlays/ToastItem.svelte.d.ts +9 -0
- package/dist/overlays/focusTrap.d.ts +1 -0
- package/dist/overlays/focusTrap.js +64 -0
- package/dist/overlays/modal.d.ts +9 -0
- package/dist/overlays/modal.js +141 -0
- package/dist/overlays/popup.d.ts +9 -0
- package/dist/overlays/popup.js +108 -0
- package/dist/overlays/roots.d.ts +4 -0
- package/dist/overlays/roots.js +31 -0
- package/dist/overlays/toast.d.ts +6 -0
- package/dist/overlays/toast.js +93 -0
- package/dist/overlays/types.d.ts +31 -0
- package/dist/overlays/types.js +15 -0
- package/dist/primitives/.gitkeep +0 -0
- package/dist/primitives/ResizableSplitter.svelte +333 -0
- package/dist/primitives/ResizableSplitter.svelte.d.ts +35 -0
- package/dist/primitives/TabbedPanel.svelte +305 -0
- package/dist/primitives/TabbedPanel.svelte.d.ts +50 -0
- package/dist/registry/client.d.ts +74 -0
- package/dist/registry/client.js +118 -0
- package/dist/registry/index.d.ts +13 -0
- package/dist/registry/index.js +14 -0
- package/dist/registry/installer.d.ts +53 -0
- package/dist/registry/installer.js +170 -0
- package/dist/registry/integrity.d.ts +32 -0
- package/dist/registry/integrity.js +92 -0
- package/dist/registry/loader.d.ts +50 -0
- package/dist/registry/loader.js +145 -0
- package/dist/registry/schema.d.ts +47 -0
- package/dist/registry/schema.js +180 -0
- package/dist/registry/storage.d.ts +37 -0
- package/dist/registry/storage.js +101 -0
- package/dist/registry/types.d.ts +245 -0
- package/dist/registry/types.js +14 -0
- package/dist/registry-shard/RegistryView.svelte +561 -0
- package/dist/registry-shard/RegistryView.svelte.d.ts +3 -0
- package/dist/registry-shard/registryApp.d.ts +10 -0
- package/dist/registry-shard/registryApp.js +24 -0
- package/dist/registry-shard/registryShard.svelte.d.ts +45 -0
- package/dist/registry-shard/registryShard.svelte.js +125 -0
- package/dist/shards/activate.svelte.d.ts +45 -0
- package/dist/shards/activate.svelte.js +124 -0
- package/dist/shards/registry.d.ts +4 -0
- package/dist/shards/registry.js +28 -0
- package/dist/shards/types.d.ts +155 -0
- package/dist/shards/types.js +20 -0
- package/dist/shell-shard/ShellHome.svelte +285 -0
- package/dist/shell-shard/ShellHome.svelte.d.ts +3 -0
- package/dist/shell-shard/shellShard.svelte.d.ts +2 -0
- package/dist/shell-shard/shellShard.svelte.js +47 -0
- package/dist/shellRuntime.svelte.d.ts +27 -0
- package/dist/shellRuntime.svelte.js +27 -0
- package/dist/state/backends.d.ts +26 -0
- package/dist/state/backends.js +99 -0
- package/dist/state/types.d.ts +38 -0
- package/dist/state/types.js +15 -0
- package/dist/state/zones.svelte.d.ts +52 -0
- package/dist/state/zones.svelte.js +141 -0
- package/dist/store/InstalledView.svelte +201 -0
- package/dist/store/InstalledView.svelte.d.ts +3 -0
- package/dist/store/StoreView.svelte +470 -0
- package/dist/store/StoreView.svelte.d.ts +3 -0
- package/dist/store/storeApp.d.ts +11 -0
- package/dist/store/storeApp.js +26 -0
- package/dist/store/storeShard.svelte.d.ts +29 -0
- package/dist/store/storeShard.svelte.js +99 -0
- package/dist/tokens.css +79 -0
- 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,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 {};
|