sh3-core 0.15.0 → 0.15.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/app/store/verbs.js +4 -0
- package/dist/overlays/FloatFrame.svelte +132 -8
- package/dist/overlays/FloatFrame.svelte.d.ts +1 -1
- package/dist/overlays/FloatLayer.svelte +2 -2
- package/dist/overlays/float.d.ts +21 -0
- package/dist/overlays/float.js +66 -0
- package/dist/overlays/float.test.js +359 -0
- package/dist/overlays/floatMaximized.svelte.d.ts +4 -0
- package/dist/overlays/floatMaximized.svelte.js +30 -0
- package/dist/runtime/runVerb-shell.test.d.ts +1 -0
- package/dist/runtime/runVerb-shell.test.js +231 -0
- package/dist/sh3core-shard/sh3coreShard.svelte.d.ts +7 -0
- package/dist/sh3core-shard/sh3coreShard.svelte.js +23 -0
- package/dist/shards/activate-runtime.test.js +24 -2
- package/dist/shards/activate.svelte.js +7 -2
- package/dist/shards/types.d.ts +11 -3
- package/dist/shell-shard/Terminal.svelte +1 -0
- package/dist/shell-shard/shellApi.js +1 -0
- package/dist/shell-shard/verbs/apps.js +2 -0
- package/dist/shell-shard/verbs/cat.js +1 -0
- package/dist/shell-shard/verbs/help.js +5 -1
- package/dist/shell-shard/verbs/help.svelte.test.d.ts +1 -0
- package/dist/shell-shard/verbs/help.svelte.test.js +53 -0
- package/dist/shell-shard/verbs/ls.js +1 -0
- package/dist/shell-shard/verbs/session.js +2 -0
- package/dist/shell-shard/verbs/shards.js +1 -0
- package/dist/shell-shard/verbs/views.js +5 -0
- package/dist/shell-shard/verbs/zones.js +2 -0
- package/dist/verbs/types.d.ts +9 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -31,7 +31,29 @@ import { registeredApps } from '../apps/registry.svelte';
|
|
|
31
31
|
import { launchApp } from '../apps/lifecycle';
|
|
32
32
|
import { resetActivePresetToDefault } from '../layout/store.svelte';
|
|
33
33
|
import { modalManager } from '../overlays/modal';
|
|
34
|
+
import { floatManager } from '../overlays/float';
|
|
34
35
|
import { registerAppActions } from './appActions';
|
|
36
|
+
/**
|
|
37
|
+
* Build the palette-only float-maximize toggle action. Targets the topmost
|
|
38
|
+
* float (last entry in `floatManager.list()`); disabled when no floats are
|
|
39
|
+
* open. See spec docs/superpowers/specs/2026-05-07-float-resize-maximize-design.md.
|
|
40
|
+
*/
|
|
41
|
+
export function buildToggleMaximizeAction() {
|
|
42
|
+
return {
|
|
43
|
+
id: 'sh3.float.toggleMaximize',
|
|
44
|
+
label: 'Toggle Float Maximize',
|
|
45
|
+
scope: ['home', 'app'],
|
|
46
|
+
paletteItem: true,
|
|
47
|
+
contextItem: false,
|
|
48
|
+
defaultShortcut: 'Ctrl+Shift+M',
|
|
49
|
+
disabled: () => floatManager.list().length === 0,
|
|
50
|
+
run() {
|
|
51
|
+
const top = floatManager.list().at(-1);
|
|
52
|
+
if (top)
|
|
53
|
+
floatManager.toggleMaximize(top.id);
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
35
57
|
export const sh3coreShard = {
|
|
36
58
|
manifest: {
|
|
37
59
|
id: '__sh3core__',
|
|
@@ -61,6 +83,7 @@ export const sh3coreShard = {
|
|
|
61
83
|
import('../actions/listeners').then(({ openPalette }) => openPalette());
|
|
62
84
|
},
|
|
63
85
|
});
|
|
86
|
+
ctx.actions.register(buildToggleMaximizeAction());
|
|
64
87
|
ctx.actions.register({
|
|
65
88
|
id: 'sh3.app.reset-layout',
|
|
66
89
|
label: 'Reset Current Layout',
|
|
@@ -44,8 +44,30 @@ describe('ctx.listVerbs / ctx.runVerb (integration)', () => {
|
|
|
44
44
|
const list = consumerCtx.listVerbs();
|
|
45
45
|
const a = list.find((v) => v.name === 'host:a');
|
|
46
46
|
const b = list.find((v) => v.name === 'host:b');
|
|
47
|
-
expect(a).toEqual({ shardId: 'host', name: 'host:a', summary: 'first verb', schema: undefined });
|
|
48
|
-
expect(b).toEqual({ shardId: 'host', name: 'host:b', summary: 'second verb', schema: undefined });
|
|
47
|
+
expect(a).toEqual({ shardId: 'host', name: 'host:a', summary: 'first verb', programmatic: true, schema: undefined });
|
|
48
|
+
expect(b).toEqual({ shardId: 'host', name: 'host:b', summary: 'second verb', programmatic: undefined, schema: undefined });
|
|
49
|
+
});
|
|
50
|
+
it('listVerbs({ programmaticOnly: true }) returns only verbs that opted in', async () => {
|
|
51
|
+
registerShard({
|
|
52
|
+
manifest: { id: 'host', label: 'Host', version: '0.0.0', views: [] },
|
|
53
|
+
activate(ctx) {
|
|
54
|
+
ctx.registerVerb(programmaticVerb('a', 'first verb'));
|
|
55
|
+
ctx.registerVerb(plainVerb('b', 'second verb'));
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
let consumerCtx = null;
|
|
59
|
+
registerShard({
|
|
60
|
+
manifest: { id: 'consumer', label: 'C', version: '0.0.0', views: [] },
|
|
61
|
+
activate(ctx) {
|
|
62
|
+
consumerCtx = ctx;
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
await activateShard('host');
|
|
66
|
+
await activateShard('consumer');
|
|
67
|
+
const list = consumerCtx.listVerbs({ programmaticOnly: true });
|
|
68
|
+
expect(list.find((v) => v.name === 'host:a')).toBeDefined();
|
|
69
|
+
expect(list.find((v) => v.name === 'host:b')).toBeUndefined();
|
|
70
|
+
expect(list.every((v) => v.programmatic === true)).toBe(true);
|
|
49
71
|
});
|
|
50
72
|
it('runVerb dispatches a programmatic verb and returns captured scrollback', async () => {
|
|
51
73
|
registerShard({
|
|
@@ -201,11 +201,16 @@ export async function activateShard(id, opts) {
|
|
|
201
201
|
openContextMenu(opts) { shellOpenContextMenu(opts); },
|
|
202
202
|
openPalette(opts) { shellOpenPalette(opts); },
|
|
203
203
|
},
|
|
204
|
-
listVerbs() {
|
|
205
|
-
|
|
204
|
+
listVerbs(opts) {
|
|
205
|
+
const all = listVerbsWithShard();
|
|
206
|
+
const filtered = (opts === null || opts === void 0 ? void 0 : opts.programmaticOnly)
|
|
207
|
+
? all.filter(({ verb }) => verb.programmatic === true)
|
|
208
|
+
: all;
|
|
209
|
+
return filtered.map(({ verb, shardId }) => ({
|
|
206
210
|
shardId,
|
|
207
211
|
name: verb.name,
|
|
208
212
|
summary: verb.summary,
|
|
213
|
+
programmatic: verb.programmatic,
|
|
209
214
|
schema: verb.schema,
|
|
210
215
|
}));
|
|
211
216
|
},
|
package/dist/shards/types.d.ts
CHANGED
|
@@ -255,17 +255,25 @@ export interface ShardContext {
|
|
|
255
255
|
/**
|
|
256
256
|
* Read-only snapshot of every verb registered across every active shard.
|
|
257
257
|
* Returned entries include the contributing `shardId`, the prefixed
|
|
258
|
-
* `name`, the verb's `summary`, and (when
|
|
259
|
-
* Order is undefined.
|
|
258
|
+
* `name`, the verb's `summary`, the `programmatic` flag, and (when
|
|
259
|
+
* present) its `schema`. Order is undefined.
|
|
260
|
+
*
|
|
261
|
+
* Pass `{ programmaticOnly: true }` to restrict the result to verbs that
|
|
262
|
+
* have opted in via `programmatic: true` — i.e. the verbs that are
|
|
263
|
+
* actually invocable through `ctx.runVerb(...)`. AI-class shards
|
|
264
|
+
* typically want this filter so they only surface what they can call.
|
|
260
265
|
*
|
|
261
266
|
* No permission gate — verb names + summaries are already visible via
|
|
262
267
|
* the `help` verb. Diagnostic and AI-class shards (sh3-ai, sh3-diagnostic)
|
|
263
268
|
* use this to enumerate the host's action surface.
|
|
264
269
|
*/
|
|
265
|
-
listVerbs(
|
|
270
|
+
listVerbs(opts?: {
|
|
271
|
+
programmaticOnly?: boolean;
|
|
272
|
+
}): Array<{
|
|
266
273
|
shardId: string;
|
|
267
274
|
name: string;
|
|
268
275
|
summary: string;
|
|
276
|
+
programmatic?: boolean;
|
|
269
277
|
schema?: VerbSchema;
|
|
270
278
|
}>;
|
|
271
279
|
/**
|
|
@@ -3,6 +3,7 @@ import AppCard from '../rich/AppCard.svelte';
|
|
|
3
3
|
export const appsVerb = {
|
|
4
4
|
name: 'apps',
|
|
5
5
|
summary: 'List installed apps. Click a row to launch.',
|
|
6
|
+
programmatic: true,
|
|
6
7
|
async run(ctx) {
|
|
7
8
|
const apps = ctx.shell.listApps();
|
|
8
9
|
ctx.scrollback.push({
|
|
@@ -29,6 +30,7 @@ export const appsVerb = {
|
|
|
29
30
|
export const appVerb = {
|
|
30
31
|
name: 'app',
|
|
31
32
|
summary: 'Show the currently active app.',
|
|
33
|
+
programmatic: true,
|
|
32
34
|
async run(ctx) {
|
|
33
35
|
const active = ctx.shell.getActiveApp();
|
|
34
36
|
if (!active) {
|
|
@@ -4,8 +4,12 @@ export function makeHelpVerb() {
|
|
|
4
4
|
return {
|
|
5
5
|
name: 'help',
|
|
6
6
|
summary: 'List verbs or show detail for one.',
|
|
7
|
+
globalVerb: true,
|
|
7
8
|
async run(ctx) {
|
|
8
|
-
const
|
|
9
|
+
const inSh3 = ctx.shell.getMode().id === 'sh3';
|
|
10
|
+
const rows = listVerbs()
|
|
11
|
+
.filter((v) => inSh3 || v.globalVerb === true)
|
|
12
|
+
.map((v) => ({ name: v.name, summary: v.summary }));
|
|
9
13
|
ctx.scrollback.push({
|
|
10
14
|
kind: 'rich',
|
|
11
15
|
component: HelpTable,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { makeHelpVerb } from './help';
|
|
3
|
+
import { registerVerb, __resetViewRegistryForTest } from '../../shards/registry';
|
|
4
|
+
function makeCtx(modeId) {
|
|
5
|
+
const pushed = [];
|
|
6
|
+
const ctx = {
|
|
7
|
+
shell: { getMode: () => ({ id: modeId, label: modeId }) },
|
|
8
|
+
scrollback: { push: (e) => pushed.push(e) },
|
|
9
|
+
session: {},
|
|
10
|
+
cwd: '/',
|
|
11
|
+
dispatch: async () => { },
|
|
12
|
+
fs: {},
|
|
13
|
+
};
|
|
14
|
+
return { ctx, pushed };
|
|
15
|
+
}
|
|
16
|
+
const sh3Verb = { name: 'apps', summary: 'list apps', async run() { } };
|
|
17
|
+
const globalA = { name: 'clear', summary: 'clear scrollback', globalVerb: true, async run() { } };
|
|
18
|
+
const globalB = { name: 'mode', summary: 'switch mode', globalVerb: true, async run() { } };
|
|
19
|
+
describe('help verb', () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
__resetViewRegistryForTest();
|
|
22
|
+
registerVerb('apps', sh3Verb, 'shell');
|
|
23
|
+
registerVerb('clear', globalA, 'shell');
|
|
24
|
+
registerVerb('mode', globalB, 'shell');
|
|
25
|
+
registerVerb('help', makeHelpVerb(), 'shell');
|
|
26
|
+
});
|
|
27
|
+
it('lists every registered verb in sh3 mode', async () => {
|
|
28
|
+
const help = makeHelpVerb();
|
|
29
|
+
const { ctx, pushed } = makeCtx('sh3');
|
|
30
|
+
await help.run(ctx, []);
|
|
31
|
+
const rich = pushed.find((e) => e.kind === 'rich');
|
|
32
|
+
const names = rich.props.data.rows.map((r) => r.name);
|
|
33
|
+
expect(names).toContain('apps');
|
|
34
|
+
expect(names).toContain('clear');
|
|
35
|
+
expect(names).toContain('mode');
|
|
36
|
+
expect(names).toContain('help');
|
|
37
|
+
});
|
|
38
|
+
it('lists only globalVerb-flagged verbs in a custom mode', async () => {
|
|
39
|
+
const help = makeHelpVerb();
|
|
40
|
+
const { ctx, pushed } = makeCtx('gemini');
|
|
41
|
+
await help.run(ctx, []);
|
|
42
|
+
const rich = pushed.find((e) => e.kind === 'rich');
|
|
43
|
+
const names = rich.props.data.rows.map((r) => r.name);
|
|
44
|
+
expect(names).toContain('clear');
|
|
45
|
+
expect(names).toContain('mode');
|
|
46
|
+
expect(names).toContain('help');
|
|
47
|
+
expect(names).not.toContain('apps');
|
|
48
|
+
});
|
|
49
|
+
it('is flagged globalVerb so it resolves in custom modes', () => {
|
|
50
|
+
const help = makeHelpVerb();
|
|
51
|
+
expect(help.globalVerb).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -35,6 +35,7 @@ function normalizeRel(s) {
|
|
|
35
35
|
export const pwdVerb = {
|
|
36
36
|
name: 'pwd',
|
|
37
37
|
summary: 'Show the current working directory.',
|
|
38
|
+
programmatic: true,
|
|
38
39
|
async run(ctx) {
|
|
39
40
|
const rel = ctx.session.cwd || '';
|
|
40
41
|
const display = rel.startsWith('/') ? rel : `/${rel}`;
|
|
@@ -85,6 +86,7 @@ export const cdVerb = {
|
|
|
85
86
|
export const whoamiVerb = {
|
|
86
87
|
name: 'whoami',
|
|
87
88
|
summary: 'Show the current admin user and session info.',
|
|
89
|
+
programmatic: true,
|
|
88
90
|
async run(ctx) {
|
|
89
91
|
const me = ctx.shell.whoAmI();
|
|
90
92
|
ctx.scrollback.push({
|
|
@@ -2,6 +2,7 @@ import ViewsTable from '../rich/ViewsTable.svelte';
|
|
|
2
2
|
export const viewsVerb = {
|
|
3
3
|
name: 'views',
|
|
4
4
|
summary: 'List views currently mounted. Pass --standalone to list summonable views instead.',
|
|
5
|
+
programmatic: true,
|
|
5
6
|
async run(ctx, args) {
|
|
6
7
|
if (args.includes('--standalone')) {
|
|
7
8
|
const standalones = ctx.shell.listStandaloneViews();
|
|
@@ -52,6 +53,7 @@ export const viewsVerb = {
|
|
|
52
53
|
export const openVerb = {
|
|
53
54
|
name: 'open',
|
|
54
55
|
summary: 'Open a view from any active shard into the current layout.',
|
|
56
|
+
programmatic: true,
|
|
55
57
|
async run(ctx, args) {
|
|
56
58
|
var _a;
|
|
57
59
|
const viewId = args[0];
|
|
@@ -86,6 +88,7 @@ export const openVerb = {
|
|
|
86
88
|
export const popoutVerb = {
|
|
87
89
|
name: 'popout',
|
|
88
90
|
summary: 'Pop a docked view out into a float by slot id.',
|
|
91
|
+
programmatic: true,
|
|
89
92
|
async run(ctx, args) {
|
|
90
93
|
var _a;
|
|
91
94
|
const slotId = args[0];
|
|
@@ -120,6 +123,7 @@ export const popoutVerb = {
|
|
|
120
123
|
export const dockVerb = {
|
|
121
124
|
name: 'dock',
|
|
122
125
|
summary: 'Dock a float back into the current layout by float id. Run with no args to list floats.',
|
|
126
|
+
programmatic: true,
|
|
123
127
|
async run(ctx, args) {
|
|
124
128
|
var _a;
|
|
125
129
|
const floatId = args[0];
|
|
@@ -166,6 +170,7 @@ export const dockVerb = {
|
|
|
166
170
|
export const closeVerb = {
|
|
167
171
|
name: 'close',
|
|
168
172
|
summary: 'Close a view by slot id.',
|
|
173
|
+
programmatic: true,
|
|
169
174
|
async run(ctx, args) {
|
|
170
175
|
var _a;
|
|
171
176
|
const slotId = args[0];
|
|
@@ -3,6 +3,7 @@ import ZoneTree from '../rich/ZoneTree.svelte';
|
|
|
3
3
|
export const zonesVerb = {
|
|
4
4
|
name: 'zones',
|
|
5
5
|
summary: 'List zones for the current user (optionally scoped to a shard).',
|
|
6
|
+
programmatic: true,
|
|
6
7
|
async run(ctx, args) {
|
|
7
8
|
const rows = ctx.shell.listZones(args[0]);
|
|
8
9
|
ctx.scrollback.push({
|
|
@@ -16,6 +17,7 @@ export const zonesVerb = {
|
|
|
16
17
|
export const zoneVerb = {
|
|
17
18
|
name: 'zone',
|
|
18
19
|
summary: 'Dump the contents of a zone as a collapsible JSON tree.',
|
|
20
|
+
programmatic: true,
|
|
19
21
|
async run(ctx, args) {
|
|
20
22
|
const [shardId, zoneName] = args;
|
|
21
23
|
if (!shardId || !zoneName) {
|
package/dist/verbs/types.d.ts
CHANGED
|
@@ -77,6 +77,15 @@ export interface ShellApi {
|
|
|
77
77
|
id: string;
|
|
78
78
|
label: string;
|
|
79
79
|
}[];
|
|
80
|
+
/**
|
|
81
|
+
* Active shell mode. Returns `{ id: 'sh3', label: 'sh3' }` from headless
|
|
82
|
+
* contexts (no terminal view mounted) so callers can rely on a stable
|
|
83
|
+
* shape — Terminal.svelte overrides this with the live mode.
|
|
84
|
+
*/
|
|
85
|
+
getMode(): {
|
|
86
|
+
id: string;
|
|
87
|
+
label: string;
|
|
88
|
+
};
|
|
80
89
|
}
|
|
81
90
|
export interface VerbContext {
|
|
82
91
|
shell: ShellApi;
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Auto-generated from package.json — do not edit manually. */
|
|
2
|
-
export declare const VERSION = "0.15.
|
|
2
|
+
export declare const VERSION = "0.15.1";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Auto-generated from package.json — do not edit manually. */
|
|
2
|
-
export const VERSION = '0.15.
|
|
2
|
+
export const VERSION = '0.15.1';
|