sh3-core 0.21.2 → 0.22.1
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/__test__/fixtures.js +1 -1
- package/dist/__test__/reset.js +1 -1
- package/dist/__test__/smoke.test.js +2 -2
- package/dist/actions/contextMenuModel.test.js +6 -3
- package/dist/actions/ctx-actions.svelte.test.js +9 -9
- package/dist/actions/dispatcher-v3.test.js +8 -0
- package/dist/actions/dispatcher.svelte.d.ts +1 -2
- package/dist/actions/dispatcher.svelte.js +6 -7
- package/dist/actions/dispatcher.test.js +9 -12
- package/dist/actions/listActionsFromEntries.test.js +1 -2
- package/dist/actions/listActive.test.js +2 -3
- package/dist/actions/menuBarModel.test.js +1 -7
- package/dist/actions/paletteModel.test.js +1 -3
- package/dist/actions/scope-helpers.test.js +4 -4
- package/dist/actions/shardContext.test.js +2 -2
- package/dist/actions/state.svelte.d.ts +12 -2
- package/dist/actions/state.svelte.js +15 -12
- package/dist/actions/state.test.js +4 -4
- package/dist/api.d.ts +3 -3
- package/dist/api.js +1 -1
- package/dist/app/admin/adminShard.svelte.js +1 -1
- package/dist/app/store/storeShard.svelte.js +10 -5
- package/dist/app-appearance/appearanceShard.svelte.js +1 -5
- package/dist/apps/lifecycle.js +65 -33
- package/dist/apps/lifecycle.test.js +198 -10
- package/dist/artifact.d.ts +2 -0
- package/dist/build.js +1 -1
- package/dist/conflicts/adapter-documents.js +1 -2
- package/dist/createShell.js +1 -1
- package/dist/documents/handle.d.ts +9 -4
- package/dist/documents/handle.js +69 -45
- package/dist/documents/handle.test.js +99 -27
- package/dist/documents/index.d.ts +1 -1
- package/dist/documents/types.d.ts +16 -20
- package/dist/host.d.ts +1 -1
- package/dist/host.js +9 -56
- package/dist/host.svelte.test.js +31 -63
- package/dist/layout/LayoutRenderer.svelte +1 -1
- package/dist/layout/SlotContainer.svelte +1 -0
- package/dist/layout/inspection.js +19 -14
- package/dist/layout/inspection.svelte.test.js +136 -1
- package/dist/layout/slotHostPool.svelte.d.ts +2 -1
- package/dist/layout/slotHostPool.svelte.js +6 -3
- package/dist/layout/slotHostPool.test.js +17 -0
- package/dist/layout/store.projectScope.test.js +76 -0
- package/dist/layout/store.svelte.d.ts +6 -0
- package/dist/layout/store.svelte.js +43 -13
- package/dist/layout/tree-walk.d.ts +8 -1
- package/dist/layout/tree-walk.js +11 -1
- package/dist/layout/tree-walk.test.js +53 -1
- package/dist/layout/types.d.ts +27 -0
- package/dist/layout/types.test.js +28 -0
- package/dist/layouts-shard/LayoutsSection.svelte +1 -1
- package/dist/layouts-shard/layoutsShard.svelte.js +2 -5
- package/dist/layouts-shard/layoutsShard.svelte.test.js +2 -2
- package/dist/overlays/FloatFrame.svelte +4 -1
- package/dist/overlays/float.d.ts +7 -1
- package/dist/overlays/float.js +4 -0
- package/dist/projects-shard/ProjectsSection.svelte +1 -5
- package/dist/projects-shard/projectsShard.svelte.js +1 -5
- package/dist/registry/installer.js +1 -1
- package/dist/registry/loader.d.ts +1 -1
- package/dist/registry/loader.js +3 -3
- package/dist/registry/permission-descriptions.test.js +2 -2
- package/dist/registry/register.js +1 -1
- package/dist/registry/register.test.js +1 -1
- package/dist/runtime/runVerb-shell.test.js +1 -1
- package/dist/runtime/runVerb.js +2 -2
- package/dist/runtime/runVerb.test.js +9 -9
- package/dist/sh3Api/headless.js +1 -1
- package/dist/sh3core-shard/sh3coreShard.svelte.js +1 -6
- package/dist/shards/ctx-fetch.test.js +9 -9
- package/dist/shards/lifecycle.svelte.d.ts +108 -0
- package/dist/shards/lifecycle.svelte.js +551 -0
- package/dist/shards/lifecycle.test.js +139 -0
- package/dist/shards/types.d.ts +56 -22
- package/dist/shell-shard/shellShard.svelte.js +11 -5
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/dist/shards/activate-browse.test.js +0 -120
- package/dist/shards/activate-contributions.test.js +0 -141
- package/dist/shards/activate-error-isolation.test.js +0 -98
- package/dist/shards/activate-fields.svelte.test.d.ts +0 -1
- package/dist/shards/activate-fields.svelte.test.js +0 -121
- package/dist/shards/activate-on-key-revoked.test.d.ts +0 -1
- package/dist/shards/activate-on-key-revoked.test.js +0 -60
- package/dist/shards/activate-runtime.test.d.ts +0 -1
- package/dist/shards/activate-runtime.test.js +0 -299
- package/dist/shards/activate-scopeid.test.d.ts +0 -1
- package/dist/shards/activate-scopeid.test.js +0 -21
- package/dist/shards/activate.svelte.d.ts +0 -102
- package/dist/shards/activate.svelte.js +0 -403
- /package/dist/{shards/activate-browse.test.d.ts → actions/dispatcher-v3.test.d.ts} +0 -0
- /package/dist/{shards/activate-contributions.test.d.ts → layout/store.projectScope.test.d.ts} +0 -0
- /package/dist/shards/{activate-error-isolation.test.d.ts → lifecycle.test.d.ts} +0 -0
package/dist/layout/tree-walk.js
CHANGED
|
@@ -20,12 +20,13 @@ export function collectSlotRefs(tree) {
|
|
|
20
20
|
viewId: node.viewId,
|
|
21
21
|
label: node.viewId || node.slotId,
|
|
22
22
|
meta: node.meta,
|
|
23
|
+
props: node.props,
|
|
23
24
|
});
|
|
24
25
|
return;
|
|
25
26
|
}
|
|
26
27
|
if (node.type === 'tabs') {
|
|
27
28
|
for (const t of node.tabs) {
|
|
28
|
-
out.push({ slotId: t.slotId, viewId: t.viewId, label: t.label, meta: t.meta });
|
|
29
|
+
out.push({ slotId: t.slotId, viewId: t.viewId, label: t.label, meta: t.meta, props: t.props });
|
|
29
30
|
}
|
|
30
31
|
return;
|
|
31
32
|
}
|
|
@@ -49,3 +50,12 @@ export function collectTreeSlotRefs(tree) {
|
|
|
49
50
|
}
|
|
50
51
|
return out;
|
|
51
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Collect every slot that has a non-null viewId, for use by shard layout
|
|
55
|
+
* hooks (`onLayoutWillRestore`, `onLayoutRestored`). Excludes empty slots.
|
|
56
|
+
*/
|
|
57
|
+
export function collectRestoredSlots(tree) {
|
|
58
|
+
return collectTreeSlotRefs(tree)
|
|
59
|
+
.filter((r) => r.viewId !== null)
|
|
60
|
+
.map(({ slotId, viewId, props }) => ({ slotId, viewId, props }));
|
|
61
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { collectSlotRefs, collectTreeSlotRefs } from './tree-walk';
|
|
2
|
+
import { collectSlotRefs, collectTreeSlotRefs, collectRestoredSlots } from './tree-walk';
|
|
3
3
|
const slot = (slotId, viewId) => ({
|
|
4
4
|
type: 'slot',
|
|
5
5
|
slotId,
|
|
@@ -12,6 +12,58 @@ describe('collectSlotRefs (single LayoutNode — existing behavior)', () => {
|
|
|
12
12
|
]);
|
|
13
13
|
});
|
|
14
14
|
});
|
|
15
|
+
describe('collectSlotRefs — props', () => {
|
|
16
|
+
it('includes props from a slot node', () => {
|
|
17
|
+
const tree = {
|
|
18
|
+
type: 'slot',
|
|
19
|
+
slotId: 's1',
|
|
20
|
+
viewId: 'v:a',
|
|
21
|
+
props: { filePath: '/a.guml' },
|
|
22
|
+
};
|
|
23
|
+
const refs = collectSlotRefs(tree);
|
|
24
|
+
expect(refs[0].props).toEqual({ filePath: '/a.guml' });
|
|
25
|
+
});
|
|
26
|
+
it('includes props from a tab entry', () => {
|
|
27
|
+
const tree = {
|
|
28
|
+
type: 'tabs',
|
|
29
|
+
activeTab: 0,
|
|
30
|
+
tabs: [{ slotId: 's2', viewId: 'v:b', label: 'B', props: { x: 42 } }],
|
|
31
|
+
};
|
|
32
|
+
const refs = collectSlotRefs(tree);
|
|
33
|
+
expect(refs[0].props).toEqual({ x: 42 });
|
|
34
|
+
});
|
|
35
|
+
it('props is undefined when not set', () => {
|
|
36
|
+
const tree = { type: 'slot', slotId: 's3', viewId: 'v:c' };
|
|
37
|
+
const refs = collectSlotRefs(tree);
|
|
38
|
+
expect(refs[0].props).toBeUndefined();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe('collectRestoredSlots', () => {
|
|
42
|
+
it('collects slots from docked tree and floats, excluding nulls', () => {
|
|
43
|
+
const tree = {
|
|
44
|
+
docked: { type: 'slot', slotId: 'd1', viewId: 'v:d', props: { a: 1 } },
|
|
45
|
+
floats: [
|
|
46
|
+
{
|
|
47
|
+
id: 'f1',
|
|
48
|
+
content: { type: 'slot', slotId: 'f-s1', viewId: 'v:e' },
|
|
49
|
+
position: { x: 0, y: 0 },
|
|
50
|
+
size: { w: 100, h: 100 },
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
const slots = collectRestoredSlots(tree);
|
|
55
|
+
expect(slots).toHaveLength(2);
|
|
56
|
+
expect(slots[0]).toEqual({ slotId: 'd1', viewId: 'v:d', props: { a: 1 } });
|
|
57
|
+
expect(slots[1]).toEqual({ slotId: 'f-s1', viewId: 'v:e', props: undefined });
|
|
58
|
+
});
|
|
59
|
+
it('excludes slots with null viewId', () => {
|
|
60
|
+
const tree = {
|
|
61
|
+
docked: { type: 'slot', slotId: 'e1', viewId: null },
|
|
62
|
+
floats: [],
|
|
63
|
+
};
|
|
64
|
+
expect(collectRestoredSlots(tree)).toHaveLength(0);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
15
67
|
describe('collectTreeSlotRefs (LayoutTree — new)', () => {
|
|
16
68
|
it('returns docked slots followed by float content slots in order', () => {
|
|
17
69
|
const tree = {
|
package/dist/layout/types.d.ts
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/** JSON-safe value — safe for serialization into the layout tree. */
|
|
2
|
+
export type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
3
|
+
[key: string]: JsonValue;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* A slot as seen by shard layout hooks (`onLayoutWillRestore`,
|
|
7
|
+
* `onLayoutRestored`). Carries the slot's persisted props so shards
|
|
8
|
+
* can re-run ceremony without querying the layout tree directly.
|
|
9
|
+
*/
|
|
10
|
+
export interface RestoredSlot {
|
|
11
|
+
slotId: string;
|
|
12
|
+
viewId: string;
|
|
13
|
+
props?: Record<string, JsonValue>;
|
|
14
|
+
}
|
|
1
15
|
/** Axis along which a split node divides its children. */
|
|
2
16
|
export type SplitDirection = 'horizontal' | 'vertical';
|
|
3
17
|
/**
|
|
@@ -62,6 +76,13 @@ export interface TabEntry {
|
|
|
62
76
|
* Ephemeral — not serialized with the layout tree.
|
|
63
77
|
*/
|
|
64
78
|
meta?: Record<string, unknown>;
|
|
79
|
+
/**
|
|
80
|
+
* Persistent view-level parameters. Serialized with the layout tree and
|
|
81
|
+
* passed to `ViewFactory.mount` on every mount, including layout restore.
|
|
82
|
+
* Values must be JSON-serializable. Use for data the view needs to
|
|
83
|
+
* initialize itself (e.g. `{ filePath: '/foo.guml' }`).
|
|
84
|
+
*/
|
|
85
|
+
props?: Record<string, JsonValue>;
|
|
65
86
|
}
|
|
66
87
|
/**
|
|
67
88
|
* A layout node that groups one or more slots as tabs, showing one at a time.
|
|
@@ -124,6 +145,12 @@ export interface SlotNode {
|
|
|
124
145
|
* content is a single slot rather than a TabsNode).
|
|
125
146
|
*/
|
|
126
147
|
meta?: Record<string, unknown>;
|
|
148
|
+
/**
|
|
149
|
+
* Persistent view-level parameters. Serialized with the layout tree and
|
|
150
|
+
* passed to `ViewFactory.mount` on every mount, including layout restore.
|
|
151
|
+
* Values must be JSON-serializable.
|
|
152
|
+
*/
|
|
153
|
+
props?: Record<string, JsonValue>;
|
|
127
154
|
}
|
|
128
155
|
/**
|
|
129
156
|
* Union of all layout node kinds. The recursive tree is composed entirely of
|
|
@@ -24,3 +24,31 @@ describe('LAYOUT_SCHEMA_VERSION', () => {
|
|
|
24
24
|
expect(LAYOUT_SCHEMA_VERSION).toBe(7);
|
|
25
25
|
});
|
|
26
26
|
});
|
|
27
|
+
describe('SlotNode props', () => {
|
|
28
|
+
it('accepts optional props', () => {
|
|
29
|
+
var _a;
|
|
30
|
+
const node = {
|
|
31
|
+
type: 'slot',
|
|
32
|
+
slotId: 'slot-1',
|
|
33
|
+
viewId: 'my:view',
|
|
34
|
+
props: { filePath: '/foo.guml', count: 3, flag: true, nul: null },
|
|
35
|
+
};
|
|
36
|
+
expect((_a = node.props) === null || _a === void 0 ? void 0 : _a.filePath).toBe('/foo.guml');
|
|
37
|
+
});
|
|
38
|
+
it('omitting props is valid', () => {
|
|
39
|
+
const node = { type: 'slot', slotId: 's', viewId: null };
|
|
40
|
+
expect(node.props).toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe('TabEntry props', () => {
|
|
44
|
+
it('accepts optional props', () => {
|
|
45
|
+
var _a;
|
|
46
|
+
const entry = {
|
|
47
|
+
slotId: 'slot-1',
|
|
48
|
+
viewId: 'my:view',
|
|
49
|
+
label: 'My View',
|
|
50
|
+
props: { x: 1 },
|
|
51
|
+
};
|
|
52
|
+
expect((_a = entry.props) === null || _a === void 0 ? void 0 : _a.x).toBe(1);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { getLayouts } from './layoutsState.svelte';
|
|
9
9
|
import { restoreToFloat } from './layoutsApi';
|
|
10
|
-
import { listStandaloneViews } from '../shards/
|
|
10
|
+
import { listStandaloneViews } from '../shards/lifecycle.svelte';
|
|
11
11
|
import { toastManager } from '../overlays/toast';
|
|
12
12
|
import { sh3 } from '../sh3Runtime.svelte';
|
|
13
13
|
import { makeSelectionApi } from '../actions/selection.svelte';
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* submenu and the home-page card grid.
|
|
9
9
|
*/
|
|
10
10
|
import { VERSION } from '../version';
|
|
11
|
-
import { listStandaloneViews } from '../shards/
|
|
11
|
+
import { listStandaloneViews } from '../shards/lifecycle.svelte';
|
|
12
12
|
import { getSelection } from '../actions/selection.svelte';
|
|
13
13
|
import { modalManager } from '../overlays/modal';
|
|
14
14
|
import { toastManager } from '../overlays/toast';
|
|
@@ -137,7 +137,7 @@ export const layoutsShard = {
|
|
|
137
137
|
version: VERSION,
|
|
138
138
|
views: [],
|
|
139
139
|
},
|
|
140
|
-
|
|
140
|
+
register(ctx) {
|
|
141
141
|
const zone = ctx.state({
|
|
142
142
|
user: { layouts: [] },
|
|
143
143
|
});
|
|
@@ -222,9 +222,6 @@ export const layoutsShard = {
|
|
|
222
222
|
});
|
|
223
223
|
});
|
|
224
224
|
},
|
|
225
|
-
autostart() {
|
|
226
|
-
/* self-start so the action is available before any app launches. */
|
|
227
|
-
},
|
|
228
225
|
deactivate() {
|
|
229
226
|
__unbindZone();
|
|
230
227
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
-
import { registerShard, activateShard } from '../shards/
|
|
2
|
+
import { registerShard, activateShard } from '../shards/lifecycle.svelte';
|
|
3
3
|
import { addAutostartShard } from '../actions/state.svelte';
|
|
4
4
|
import { floatManager } from '../overlays/float';
|
|
5
5
|
import { layoutsShard } from './layoutsShard.svelte';
|
|
@@ -15,7 +15,7 @@ const stubShard = {
|
|
|
15
15
|
version: '0.0.0',
|
|
16
16
|
views: [{ id: 'shell:terminal', label: 'Sh3', standalone: true }],
|
|
17
17
|
},
|
|
18
|
-
|
|
18
|
+
register(ctx) {
|
|
19
19
|
ctx.registerView('shell:terminal', {
|
|
20
20
|
mount: () => ({ unmount() { } }),
|
|
21
21
|
});
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
import { makeSelectionApi } from '../actions/selection.svelte';
|
|
36
36
|
import { spawnSatellite } from '../sh3Api/window';
|
|
37
37
|
import { walkShardsForContent } from '../satellite/walkShards';
|
|
38
|
+
import { getActiveScopeId, getPersonalScopeId } from '../documents/config';
|
|
38
39
|
import { logGesture } from '../gestures';
|
|
39
40
|
|
|
40
41
|
const isTauri =
|
|
@@ -256,13 +257,15 @@
|
|
|
256
257
|
async function onPopOut(e: MouseEvent): Promise<void> {
|
|
257
258
|
e.stopPropagation();
|
|
258
259
|
try {
|
|
260
|
+
const scopeId = getActiveScopeId();
|
|
261
|
+
const personalId = getPersonalScopeId();
|
|
259
262
|
await spawnSatellite({
|
|
260
263
|
kind: 'float',
|
|
261
264
|
content: entry.content,
|
|
262
265
|
title: entry.title,
|
|
263
266
|
size: { w: entry.size.w, h: entry.size.h },
|
|
264
267
|
activateShards: walkShardsForContent(entry.content),
|
|
265
|
-
projectId:
|
|
268
|
+
projectId: scopeId !== personalId ? scopeId : undefined,
|
|
266
269
|
});
|
|
267
270
|
floatManager.close(entry.id);
|
|
268
271
|
} catch (err) {
|
package/dist/overlays/float.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LayoutNode, FloatEntry } from '../layout/types';
|
|
1
|
+
import type { LayoutNode, FloatEntry, JsonValue } from '../layout/types';
|
|
2
2
|
import type { Size } from '../layout/floats';
|
|
3
3
|
export interface FloatOptions {
|
|
4
4
|
title?: string;
|
|
@@ -9,6 +9,12 @@ export interface FloatOptions {
|
|
|
9
9
|
size?: Size;
|
|
10
10
|
/** Instance data threaded to the view factory via `MountContext.meta`. */
|
|
11
11
|
meta?: Record<string, unknown>;
|
|
12
|
+
/**
|
|
13
|
+
* Serializable view props threaded to the factory via `MountContext.props`
|
|
14
|
+
* and persisted with the float entry. Survives `popoutView` → `dockFloat`
|
|
15
|
+
* round-trip. JSON-safe values only.
|
|
16
|
+
*/
|
|
17
|
+
props?: Record<string, JsonValue>;
|
|
12
18
|
/**
|
|
13
19
|
* When true, the float dismisses on any pointerdown outside its frame,
|
|
14
20
|
* is not dockable, and renders without a header when `title` is unset.
|
package/dist/overlays/float.js
CHANGED
|
@@ -150,6 +150,8 @@ function openFloat(viewId, options = {}) {
|
|
|
150
150
|
};
|
|
151
151
|
if (options.meta)
|
|
152
152
|
slot.meta = options.meta;
|
|
153
|
+
if (options.props)
|
|
154
|
+
slot.props = options.props;
|
|
153
155
|
content = slot;
|
|
154
156
|
}
|
|
155
157
|
else {
|
|
@@ -157,6 +159,8 @@ function openFloat(viewId, options = {}) {
|
|
|
157
159
|
const tab = { slotId, viewId, label };
|
|
158
160
|
if (options.meta)
|
|
159
161
|
tab.meta = options.meta;
|
|
162
|
+
if (options.props)
|
|
163
|
+
tab.props = options.props;
|
|
160
164
|
content = {
|
|
161
165
|
type: 'tabs',
|
|
162
166
|
tabs: [tab],
|
|
@@ -90,11 +90,7 @@
|
|
|
90
90
|
border-color: var(--sh3-accent);
|
|
91
91
|
transform: translateY(-1px);
|
|
92
92
|
}
|
|
93
|
-
|
|
94
|
-
border-color: var(--sh3-accent);
|
|
95
|
-
box-shadow: 0 0 0 2px color-mix(in srgb, var(--sh3-accent) 40%, transparent);
|
|
96
|
-
}
|
|
97
|
-
.project-name { font-weight: 600; font-size: 13px; }
|
|
93
|
+
.project-name { font-weight: 600; font-size: 13px; }
|
|
98
94
|
.project-meta { font-size: 11px; color: var(--sh3-fg-muted); }
|
|
99
95
|
.leave-project {
|
|
100
96
|
display: flex;
|
|
@@ -56,7 +56,7 @@ export const projectsShard = {
|
|
|
56
56
|
version: VERSION,
|
|
57
57
|
views: [{ id: PROJECTS_MANAGE_VIEW, label: 'Project Manager' }],
|
|
58
58
|
},
|
|
59
|
-
|
|
59
|
+
register(ctx) {
|
|
60
60
|
void refreshProjects();
|
|
61
61
|
if (typeof document !== 'undefined') {
|
|
62
62
|
document.addEventListener('visibilitychange', () => {
|
|
@@ -141,8 +141,4 @@ export const projectsShard = {
|
|
|
141
141
|
});
|
|
142
142
|
});
|
|
143
143
|
},
|
|
144
|
-
autostart() {
|
|
145
|
-
/* register on the self-starting path so the project list is available
|
|
146
|
-
on the home screen without an app launch. */
|
|
147
|
-
},
|
|
148
144
|
};
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { loadBundleModule } from './loader';
|
|
18
18
|
import { savePackage, loadBundle, listInstalled, removePackage } from './storage';
|
|
19
|
-
import { deactivateShard } from '../shards/
|
|
19
|
+
import { deactivateShard } from '../shards/lifecycle.svelte';
|
|
20
20
|
import { unregisterApp } from '../apps/lifecycle';
|
|
21
21
|
import { registerLoadedBundle } from './register';
|
|
22
22
|
import { extractBundlePermissions } from './permission-descriptions';
|
|
@@ -37,7 +37,7 @@ export declare function loadBundleModule(bytes: ArrayBuffer): Promise<LoadedBund
|
|
|
37
37
|
/**
|
|
38
38
|
* Type guard: returns true if the loaded module is a shard.
|
|
39
39
|
*
|
|
40
|
-
* A shard has
|
|
40
|
+
* A shard has a `register` function and `manifest.views` array. An app
|
|
41
41
|
* has neither — it has `initialLayout` and `manifest.requiredShards`.
|
|
42
42
|
*/
|
|
43
43
|
export declare function isShard(mod: Shard | App): mod is Shard;
|
package/dist/registry/loader.js
CHANGED
|
@@ -125,12 +125,12 @@ export async function loadBundleModule(bytes) {
|
|
|
125
125
|
/**
|
|
126
126
|
* Type guard: returns true if the loaded module is a shard.
|
|
127
127
|
*
|
|
128
|
-
* A shard has
|
|
128
|
+
* A shard has a `register` function and `manifest.views` array. An app
|
|
129
129
|
* has neither — it has `initialLayout` and `manifest.requiredShards`.
|
|
130
130
|
*/
|
|
131
131
|
export function isShard(mod) {
|
|
132
|
-
return ('
|
|
133
|
-
typeof mod.
|
|
132
|
+
return ('register' in mod &&
|
|
133
|
+
typeof mod.register === 'function' &&
|
|
134
134
|
Array.isArray(mod.manifest.views));
|
|
135
135
|
}
|
|
136
136
|
/**
|
|
@@ -40,7 +40,7 @@ describe('describePermission — documents:read / documents:write', () => {
|
|
|
40
40
|
function shardWithPerms(id, perms) {
|
|
41
41
|
return {
|
|
42
42
|
manifest: { id, label: id, version: '0.0.0', views: [], permissions: perms },
|
|
43
|
-
|
|
43
|
+
register: () => { },
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
46
|
function appWithPerms(id, perms) {
|
|
@@ -54,7 +54,7 @@ function appWithPerms(id, perms) {
|
|
|
54
54
|
layoutVersion: 1,
|
|
55
55
|
permissions: perms,
|
|
56
56
|
},
|
|
57
|
-
|
|
57
|
+
register: () => { },
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
describe('extractBundlePermissions', () => {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* manifests; the authoritative value comes from the persisted/server
|
|
9
9
|
* metadata and must be stamped here before any consumer reads the manifest.
|
|
10
10
|
*/
|
|
11
|
-
import { registerShard } from '../shards/
|
|
11
|
+
import { registerShard } from '../shards/lifecycle.svelte';
|
|
12
12
|
import { registerApp } from '../apps/registry.svelte';
|
|
13
13
|
export function registerLoadedBundle(loaded, meta) {
|
|
14
14
|
for (const shard of loaded.shards) {
|
|
@@ -3,7 +3,7 @@ import { resetFramework } from '../__test__/reset';
|
|
|
3
3
|
import { makeApp, makeAppManifest, makeShard, makeShardManifest } from '../__test__/fixtures';
|
|
4
4
|
import { registerLoadedBundle } from './register';
|
|
5
5
|
import { registeredApps } from '../apps/registry.svelte';
|
|
6
|
-
import { registeredShards } from '../shards/
|
|
6
|
+
import { registeredShards } from '../shards/lifecycle.svelte';
|
|
7
7
|
describe('registerLoadedBundle', () => {
|
|
8
8
|
beforeEach(resetFramework);
|
|
9
9
|
it('stamps meta.version onto every shard manifest before registering', () => {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
13
13
|
import { MemoryDocumentBackend } from '../documents/backends';
|
|
14
14
|
import { __setDocumentBackend, __setActiveScope } from '../documents/config';
|
|
15
|
-
import { registerShard, activateShard, __resetShardRegistryForTest, } from '../shards/
|
|
15
|
+
import { registerShard, activateShard, __resetShardRegistryForTest, } from '../shards/lifecycle.svelte';
|
|
16
16
|
import { __resetViewRegistryForTest } from '../shards/registry';
|
|
17
17
|
import { __resetActionsRegistryForTest } from '../actions/registry';
|
|
18
18
|
import { runVerbProgrammatic } from './runVerb';
|
package/dist/runtime/runVerb.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* runVerbProgrammatic — programmatic verb dispatch with synthesized VerbContext.
|
|
3
3
|
*
|
|
4
|
-
* Used by `ctx.runVerb(...)` (see shards/
|
|
4
|
+
* Used by `ctx.runVerb(...)` (see shards/lifecycle.svelte.ts). Builds a
|
|
5
5
|
* sink scrollback that captures entries into an array, a headless Sh3Api
|
|
6
6
|
* (no terminal-bound state), a stub SessionClient, and a real TenantFsClient.
|
|
7
7
|
* Verbs that opt in via `programmatic: true` run against this synthesized
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* Resolution: prefixed names (`'sh3-store:install'`) look up directly;
|
|
16
16
|
* unprefixed sh3 names (`'apps'`) resolve against shardId 'sh3'.
|
|
17
17
|
*/
|
|
18
|
-
import { activeShards } from '../shards/
|
|
18
|
+
import { activeShards } from '../shards/lifecycle.svelte';
|
|
19
19
|
import { getVerb, listVerbsWithShard } from '../shards/registry';
|
|
20
20
|
import { makeSh3Api } from '../sh3Api/headless';
|
|
21
21
|
import { Scrollback } from '../shell-shard/scrollback.svelte';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
2
|
import { MemoryDocumentBackend } from '../documents/backends';
|
|
3
3
|
import { __setDocumentBackend, __setActiveScope } from '../documents/config';
|
|
4
|
-
import { registerShard, activateShard, __resetShardRegistryForTest, } from '../shards/
|
|
4
|
+
import { registerShard, activateShard, __resetShardRegistryForTest, } from '../shards/lifecycle.svelte';
|
|
5
5
|
import { __resetViewRegistryForTest } from '../shards/registry';
|
|
6
6
|
import { runVerbProgrammatic } from './runVerb';
|
|
7
7
|
function makeVerb(name, programmatic, body = async () => undefined) {
|
|
@@ -27,7 +27,7 @@ describe('runVerbProgrammatic', () => {
|
|
|
27
27
|
it('rejects on unknown verb', async () => {
|
|
28
28
|
registerShard({
|
|
29
29
|
manifest: { id: 'tester', label: 'T', version: '0.0.0', views: [] },
|
|
30
|
-
|
|
30
|
+
register(ctx) {
|
|
31
31
|
ctx.registerVerb(makeVerb('echo', true));
|
|
32
32
|
},
|
|
33
33
|
});
|
|
@@ -37,7 +37,7 @@ describe('runVerbProgrammatic', () => {
|
|
|
37
37
|
it('rejects when verb is not programmatic', async () => {
|
|
38
38
|
registerShard({
|
|
39
39
|
manifest: { id: 'tester', label: 'T', version: '0.0.0', views: [] },
|
|
40
|
-
|
|
40
|
+
register(ctx) {
|
|
41
41
|
ctx.registerVerb(makeVerb('plain', false));
|
|
42
42
|
},
|
|
43
43
|
});
|
|
@@ -47,7 +47,7 @@ describe('runVerbProgrammatic', () => {
|
|
|
47
47
|
it('invokes a programmatic verb and resolves with { result, scrollback }', async () => {
|
|
48
48
|
registerShard({
|
|
49
49
|
manifest: { id: 'tester', label: 'T', version: '0.0.0', views: [] },
|
|
50
|
-
|
|
50
|
+
register(ctx) {
|
|
51
51
|
ctx.registerVerb(makeVerb('echo', true, async (vctx, args) => {
|
|
52
52
|
vctx.scrollback.push({
|
|
53
53
|
kind: 'status',
|
|
@@ -71,7 +71,7 @@ describe('runVerbProgrammatic', () => {
|
|
|
71
71
|
let observed = undefined;
|
|
72
72
|
registerShard({
|
|
73
73
|
manifest: { id: 'tester', label: 'T', version: '0.0.0', views: [] },
|
|
74
|
-
|
|
74
|
+
register(ctx) {
|
|
75
75
|
ctx.registerVerb(makeVerb('capture', true, async (vctx) => {
|
|
76
76
|
observed = vctx.structuredArgs;
|
|
77
77
|
}));
|
|
@@ -85,7 +85,7 @@ describe('runVerbProgrammatic', () => {
|
|
|
85
85
|
let received;
|
|
86
86
|
registerShard({
|
|
87
87
|
manifest: { id: 'tester', label: 'T', version: '0.0.0', views: [] },
|
|
88
|
-
|
|
88
|
+
register(ctx) {
|
|
89
89
|
ctx.registerVerb(makeVerb('peek', true, async (vctx) => {
|
|
90
90
|
received = vctx.signal;
|
|
91
91
|
}));
|
|
@@ -99,7 +99,7 @@ describe('runVerbProgrammatic', () => {
|
|
|
99
99
|
it('inner dispatch re-enters runVerb and merges scrollback into the outer capture', async () => {
|
|
100
100
|
registerShard({
|
|
101
101
|
manifest: { id: 'tester', label: 'T', version: '0.0.0', views: [] },
|
|
102
|
-
|
|
102
|
+
register(ctx) {
|
|
103
103
|
ctx.registerVerb(makeVerb('inner', true, async (vctx) => {
|
|
104
104
|
vctx.scrollback.push({ kind: 'status', text: 'inner-fired', level: 'info', ts: 0 });
|
|
105
105
|
}));
|
|
@@ -120,7 +120,7 @@ describe('runVerbProgrammatic', () => {
|
|
|
120
120
|
it('propagates an error thrown by the verb', async () => {
|
|
121
121
|
registerShard({
|
|
122
122
|
manifest: { id: 'tester', label: 'T', version: '0.0.0', views: [] },
|
|
123
|
-
|
|
123
|
+
register(ctx) {
|
|
124
124
|
ctx.registerVerb(makeVerb('boom', true, async () => {
|
|
125
125
|
throw new Error('kaboom');
|
|
126
126
|
}));
|
|
@@ -133,7 +133,7 @@ describe('runVerbProgrammatic', () => {
|
|
|
133
133
|
let observed = undefined;
|
|
134
134
|
registerShard({
|
|
135
135
|
manifest: { id: 'tester', label: 'T', version: '0.0.0', views: [] },
|
|
136
|
-
|
|
136
|
+
register(ctx) {
|
|
137
137
|
ctx.registerVerb(makeVerb('peek-sh3', true, async (vctx) => {
|
|
138
138
|
observed = vctx.sh3.dispatchToTerminal('foo');
|
|
139
139
|
}));
|
package/dist/sh3Api/headless.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { listRegisteredApps, getActiveApp } from '../apps/registry.svelte';
|
|
17
17
|
import { launchApp } from '../apps/lifecycle';
|
|
18
|
-
import { registeredShards, listStandaloneViews } from '../shards/
|
|
18
|
+
import { registeredShards, listStandaloneViews } from '../shards/lifecycle.svelte';
|
|
19
19
|
import { inspectActiveLayout, focusView, closeTab, popoutView, dockFloat, dockIntoActiveLayout, locateSlot as locateSlotInActiveLayout, } from '../layout/inspection';
|
|
20
20
|
import { floatManager } from '../overlays/float';
|
|
21
21
|
import { getUser, isAdmin } from '../auth/index';
|
|
@@ -65,7 +65,7 @@ export const sh3coreShard = {
|
|
|
65
65
|
{ id: 'sh3:keys-and-peers', label: 'Keys & Peers' },
|
|
66
66
|
],
|
|
67
67
|
},
|
|
68
|
-
|
|
68
|
+
register(ctx) {
|
|
69
69
|
const zones = ctx.state({
|
|
70
70
|
user: {
|
|
71
71
|
bindings: {},
|
|
@@ -165,9 +165,4 @@ export const sh3coreShard = {
|
|
|
165
165
|
});
|
|
166
166
|
});
|
|
167
167
|
},
|
|
168
|
-
autostart() {
|
|
169
|
-
// Intentionally empty. Defining this field is what puts the sh3core
|
|
170
|
-
// pseudo-shard on the self-starting path at boot (see bootstrap),
|
|
171
|
-
// so `sh3core:home` is available before any app launches.
|
|
172
|
-
},
|
|
173
168
|
};
|
|
@@ -2,7 +2,7 @@ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
|
|
|
2
2
|
import { MemoryDocumentBackend } from '../documents/backends';
|
|
3
3
|
import { __setDocumentBackend, __setActiveScope } from '../documents/config';
|
|
4
4
|
import { __setEnvServerUrl } from '../env/index';
|
|
5
|
-
import { registerShard, activateShard, __resetShardRegistryForTest, } from './
|
|
5
|
+
import { registerShard, activateShard, __resetShardRegistryForTest, } from './lifecycle.svelte';
|
|
6
6
|
import { __resetViewRegistryForTest } from './registry';
|
|
7
7
|
describe('ctx.fetch', () => {
|
|
8
8
|
let originalFetch;
|
|
@@ -27,7 +27,7 @@ describe('ctx.fetch', () => {
|
|
|
27
27
|
let captured = null;
|
|
28
28
|
registerShard({
|
|
29
29
|
manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
|
|
30
|
-
|
|
30
|
+
register(ctx) { captured = ctx; },
|
|
31
31
|
});
|
|
32
32
|
await activateShard('test');
|
|
33
33
|
await captured.fetch('/api/foo');
|
|
@@ -42,7 +42,7 @@ describe('ctx.fetch', () => {
|
|
|
42
42
|
let captured = null;
|
|
43
43
|
registerShard({
|
|
44
44
|
manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
|
|
45
|
-
|
|
45
|
+
register(ctx) { captured = ctx; },
|
|
46
46
|
});
|
|
47
47
|
await activateShard('test');
|
|
48
48
|
await captured.fetch('https://other.example.com/api/bar');
|
|
@@ -57,7 +57,7 @@ describe('ctx.fetch', () => {
|
|
|
57
57
|
let captured = null;
|
|
58
58
|
registerShard({
|
|
59
59
|
manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
|
|
60
|
-
|
|
60
|
+
register(ctx) { captured = ctx; },
|
|
61
61
|
});
|
|
62
62
|
await activateShard('test');
|
|
63
63
|
await captured.fetch('api/baz');
|
|
@@ -79,7 +79,7 @@ describe('ctx.serverUrl', () => {
|
|
|
79
79
|
let captured = null;
|
|
80
80
|
registerShard({
|
|
81
81
|
manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
|
|
82
|
-
|
|
82
|
+
register(ctx) { captured = ctx; },
|
|
83
83
|
});
|
|
84
84
|
await activateShard('test');
|
|
85
85
|
expect(captured.serverUrl).toBe('https://example.com');
|
|
@@ -89,7 +89,7 @@ describe('ctx.serverUrl', () => {
|
|
|
89
89
|
let captured = null;
|
|
90
90
|
registerShard({
|
|
91
91
|
manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
|
|
92
|
-
|
|
92
|
+
register(ctx) { captured = ctx; },
|
|
93
93
|
});
|
|
94
94
|
await activateShard('test');
|
|
95
95
|
expect(captured.serverUrl).toBe('');
|
|
@@ -110,7 +110,7 @@ describe('ctx.resolveUrl', () => {
|
|
|
110
110
|
let captured = null;
|
|
111
111
|
registerShard({
|
|
112
112
|
manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
|
|
113
|
-
|
|
113
|
+
register(ctx) { captured = ctx; },
|
|
114
114
|
});
|
|
115
115
|
await activateShard('test');
|
|
116
116
|
expect(captured.resolveUrl('/api/foo')).toBe('https://example.com/api/foo');
|
|
@@ -119,7 +119,7 @@ describe('ctx.resolveUrl', () => {
|
|
|
119
119
|
let captured = null;
|
|
120
120
|
registerShard({
|
|
121
121
|
manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
|
|
122
|
-
|
|
122
|
+
register(ctx) { captured = ctx; },
|
|
123
123
|
});
|
|
124
124
|
await activateShard('test');
|
|
125
125
|
expect(captured.resolveUrl('https://other.example.com/ws')).toBe('https://other.example.com/ws');
|
|
@@ -128,7 +128,7 @@ describe('ctx.resolveUrl', () => {
|
|
|
128
128
|
let captured = null;
|
|
129
129
|
registerShard({
|
|
130
130
|
manifest: { id: 'test', label: 'test', version: '0.0.0', views: [] },
|
|
131
|
-
|
|
131
|
+
register(ctx) { captured = ctx; },
|
|
132
132
|
});
|
|
133
133
|
await activateShard('test');
|
|
134
134
|
expect(captured.resolveUrl('api/ws')).toBe('https://example.com/api/ws');
|