@cmodjs/core 3.1.0 → 4.0.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/docs/commands.md +38 -21
- package/docs/dependencies.md +27 -20
- package/docs/index.md +21 -17
- package/docs/install-steps.md +37 -29
- package/docs/jobs.md +25 -52
- package/docs/mod.md +93 -65
- package/docs/options.md +2 -2
- package/docs/permissions.md +4 -4
- package/docs/session.md +14 -12
- package/docs/testing.md +55 -32
- package/docs/ui.md +255 -263
- package/hotkeys.d.ts +22 -0
- package/hotkeys.d.ts.map +1 -0
- package/hotkeys.js +26 -0
- package/installer.d.ts +1 -1
- package/installer.d.ts.map +1 -1
- package/installer.js +11 -6
- package/jobs/check.d.ts +2 -1
- package/jobs/check.d.ts.map +1 -1
- package/jobs/check.js +2 -2
- package/jobs/context.d.ts +2 -1
- package/jobs/context.d.ts.map +1 -1
- package/jobs/context.js +1 -1
- package/jobs/permissions.d.ts +2 -1
- package/jobs/permissions.d.ts.map +1 -1
- package/jobs/permissions.js +2 -2
- package/jobs/program.js +1 -1
- package/jobs/prompt.d.ts +2 -1
- package/jobs/prompt.d.ts.map +1 -1
- package/jobs/prompt.js +20 -13
- package/jobs/schedule.d.ts +2 -1
- package/jobs/schedule.d.ts.map +1 -1
- package/jobs/schedule.js +15 -1
- package/jobs/slash-command.d.ts +6 -2
- package/jobs/slash-command.d.ts.map +1 -1
- package/jobs/slash-command.js +6 -5
- package/jobs/tool.d.ts +6 -4
- package/jobs/tool.d.ts.map +1 -1
- package/jobs/tool.js +3 -3
- package/mod.d.ts +63 -42
- package/mod.d.ts.map +1 -1
- package/mod.js +11 -1
- package/package.json +1 -1
- package/records.d.ts +13 -8
- package/records.d.ts.map +1 -1
- package/records.js +41 -31
- package/register.d.ts.map +1 -1
- package/register.js +1 -2
- package/runtime/announce.d.ts +1 -1
- package/runtime/announce.d.ts.map +1 -1
- package/runtime/announce.js +6 -7
- package/runtime/before.d.ts +2 -2
- package/runtime/before.d.ts.map +1 -1
- package/runtime/before.js +1 -1
- package/runtime/claude.d.ts +0 -1
- package/runtime/claude.d.ts.map +1 -1
- package/runtime/conversations.d.ts +4 -1
- package/runtime/conversations.d.ts.map +1 -1
- package/runtime/conversations.js +42 -14
- package/runtime/dependencies.d.ts.map +1 -1
- package/runtime/dependencies.js +2 -1
- package/runtime/engine.d.ts +5 -0
- package/runtime/engine.d.ts.map +1 -1
- package/runtime/grants.js +2 -2
- package/runtime/hooks.d.ts +1 -1
- package/runtime/hooks.d.ts.map +1 -1
- package/runtime/hotkeys.d.ts +15 -0
- package/runtime/hotkeys.d.ts.map +1 -0
- package/runtime/hotkeys.js +58 -0
- package/runtime/installer.d.ts +5 -2
- package/runtime/installer.d.ts.map +1 -1
- package/runtime/installer.js +23 -12
- package/runtime/lifecycle.d.ts.map +1 -1
- package/runtime/lifecycle.js +190 -141
- package/runtime/options.js +2 -2
- package/runtime/pages.d.ts +28 -0
- package/runtime/pages.d.ts.map +1 -0
- package/runtime/pages.js +98 -0
- package/runtime/parts.d.ts +14 -0
- package/runtime/parts.d.ts.map +1 -0
- package/runtime/parts.js +59 -0
- package/runtime/permission-check.d.ts +2 -2
- package/runtime/permission-check.d.ts.map +1 -1
- package/runtime/programs.js +1 -1
- package/runtime/router.d.ts +2 -2
- package/runtime/router.d.ts.map +1 -1
- package/runtime/router.js +1 -1
- package/runtime/sessions.d.ts +10 -9
- package/runtime/sessions.d.ts.map +1 -1
- package/runtime/sessions.js +14 -5
- package/runtime/state.d.ts +2 -1
- package/runtime/state.d.ts.map +1 -1
- package/runtime/state.js +1 -0
- package/runtime/toaster.d.ts +1 -1
- package/runtime/toaster.d.ts.map +1 -1
- package/runtime/toaster.js +10 -4
- package/runtime/ui.d.ts +87 -5
- package/runtime/ui.d.ts.map +1 -1
- package/runtime/ui.js +374 -109
- package/runtime/watches.d.ts +9 -0
- package/runtime/watches.d.ts.map +1 -0
- package/runtime/watches.js +73 -0
- package/runtime/widgets.d.ts +48 -0
- package/runtime/widgets.d.ts.map +1 -0
- package/runtime/widgets.js +219 -0
- package/session/context/turn/message/message.d.ts.map +1 -1
- package/session/context/turn/message/message.js +7 -2
- package/session/session.d.ts +11 -5
- package/session/session.d.ts.map +1 -1
- package/src/hotkeys.ts +51 -0
- package/src/installer.ts +8 -4
- package/src/jobs/check.ts +4 -3
- package/src/jobs/context.ts +3 -2
- package/src/jobs/permissions.ts +4 -3
- package/src/jobs/program.ts +1 -1
- package/src/jobs/prompt.ts +23 -14
- package/src/jobs/schedule.ts +16 -2
- package/src/jobs/slash-command.ts +10 -7
- package/src/jobs/tool.ts +7 -5
- package/src/mod.ts +59 -47
- package/src/records.ts +46 -31
- package/src/register.ts +2 -3
- package/src/runtime/announce.ts +6 -7
- package/src/runtime/before.ts +4 -4
- package/src/runtime/claude.ts +0 -1
- package/src/runtime/conversations.ts +66 -20
- package/src/runtime/dependencies.ts +2 -1
- package/src/runtime/engine.ts +5 -0
- package/src/runtime/grants.ts +2 -2
- package/src/runtime/hooks.ts +1 -0
- package/src/runtime/hotkeys.ts +80 -0
- package/src/runtime/installer.ts +24 -12
- package/src/runtime/lifecycle.ts +189 -136
- package/src/runtime/options.ts +2 -2
- package/src/runtime/pages.ts +127 -0
- package/src/runtime/parts.ts +70 -0
- package/src/runtime/permission-check.ts +2 -2
- package/src/runtime/programs.ts +1 -1
- package/src/runtime/router.ts +3 -3
- package/src/runtime/sessions.ts +24 -15
- package/src/runtime/state.ts +3 -1
- package/src/runtime/toaster.ts +10 -4
- package/src/runtime/ui.ts +407 -115
- package/src/runtime/watches.ts +75 -0
- package/src/runtime/widgets.ts +287 -0
- package/src/session/context/turn/message/message.ts +7 -2
- package/src/session/session.ts +10 -6
- package/src/testing/fake-claude.ts +9 -6
- package/src/testing/fake-elements.ts +7 -4
- package/src/testing/fake-sessions.ts +6 -4
- package/src/testing.ts +203 -46
- package/src/ui/areas.ts +27 -0
- package/src/ui/elements.ts +35 -0
- package/src/ui/markdown.ts +18 -18
- package/src/ui/panes.ts +73 -0
- package/src/ui/styles.ts +53 -0
- package/src/utils/subscription.ts +1 -0
- package/testing/fake-claude.d.ts +0 -1
- package/testing/fake-claude.d.ts.map +1 -1
- package/testing/fake-claude.js +9 -5
- package/testing/fake-elements.js +7 -4
- package/testing/fake-sessions.d.ts +2 -1
- package/testing/fake-sessions.d.ts.map +1 -1
- package/testing/fake-sessions.js +1 -0
- package/testing.d.ts +21 -6
- package/testing.d.ts.map +1 -1
- package/testing.js +206 -45
- package/ui/areas.d.ts +29 -0
- package/ui/areas.d.ts.map +1 -0
- package/ui/areas.js +6 -0
- package/ui/elements.d.ts +3 -0
- package/ui/elements.d.ts.map +1 -1
- package/ui/elements.js +36 -0
- package/ui/markdown.d.ts +14 -14
- package/ui/markdown.d.ts.map +1 -1
- package/ui/markdown.js +11 -11
- package/ui/panes.d.ts +71 -0
- package/ui/panes.d.ts.map +1 -0
- package/ui/panes.js +16 -0
- package/ui/styles.d.ts +44 -0
- package/ui/styles.d.ts.map +1 -0
- package/ui/styles.js +24 -0
- package/utils/subscription.d.ts +4 -0
- package/utils/subscription.d.ts.map +1 -0
- package/jobs/status-line.d.ts +0 -6
- package/jobs/status-line.d.ts.map +0 -1
- package/jobs/status-line.js +0 -39
- package/src/jobs/status-line.ts +0 -44
- package/src/ui/define-pane.ts +0 -27
- package/src/ui/places.ts +0 -49
- package/src/utils/disposable.ts +0 -3
- package/ui/define-pane.d.ts +0 -15
- package/ui/define-pane.d.ts.map +0 -1
- package/ui/define-pane.js +0 -12
- package/ui/places.d.ts +0 -43
- package/ui/places.d.ts.map +0 -1
- package/ui/places.js +0 -31
- package/utils/disposable.d.ts +0 -4
- package/utils/disposable.d.ts.map +0 -1
- /package/utils/{disposable.js → subscription.js} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fake-claude.d.ts","sourceRoot":"","sources":["../src/testing/fake-claude.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAS,MAAM,aAAa,CAAA;AAErD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"fake-claude.d.ts","sourceRoot":"","sources":["../src/testing/fake-claude.ts"],"names":[],"mappings":"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"}
|
package/testing/fake-claude.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { isObject } from '../records.js';
|
|
2
|
+
import { cmodPluginName } from '../runtime/dependencies.js';
|
|
3
|
+
import { partMethods } from '../runtime/ui.js';
|
|
2
4
|
import { untagged } from '../session/context/turn/message/note.js';
|
|
3
5
|
import { elements } from './fake-elements.js';
|
|
4
6
|
import { fakeFiles } from './fake-files.js';
|
|
@@ -11,7 +13,7 @@ export function fakeClaude(plugin) {
|
|
|
11
13
|
const calls = [];
|
|
12
14
|
const fakes = { process: {}, fs: {}, http: {}, settings: {}, config: {}, command: {}, ui: {}, session: {}, prompt: {}, model: {}, agent: {}, clock: {}, cmod: {}, tool: {} };
|
|
13
15
|
const emptyDisk = fakeFiles({});
|
|
14
|
-
const shown = { notes: [], prompts: [], logs: [], debug: [],
|
|
16
|
+
const shown = { notes: [], prompts: [], logs: [], debug: [], openPanes: new Set(), commands: [], tools: [], schedules: [] };
|
|
15
17
|
const unplacedPanes = new Set();
|
|
16
18
|
let focusedPane;
|
|
17
19
|
const store = new Map();
|
|
@@ -29,6 +31,11 @@ export function fakeClaude(plugin) {
|
|
|
29
31
|
const run = faked(call, answer);
|
|
30
32
|
return async (...args) => run(...args);
|
|
31
33
|
};
|
|
34
|
+
const cmodTakesParts = async (input) => {
|
|
35
|
+
if (input.to === cmodPluginName && input.method === partMethods.changed)
|
|
36
|
+
return null;
|
|
37
|
+
throw new Error(`cmod.call(${JSON.stringify(input)}) has no fake answer. Set fakes.cmod.call on the tested mod.`);
|
|
38
|
+
};
|
|
32
39
|
const appendNote = async ({ message }) => {
|
|
33
40
|
shown.notes.push(untagged(message.content.map((block) => (typeof block['text'] === 'string' ? block['text'] : '')).join('')));
|
|
34
41
|
return { message, uuid: `note-${shown.notes.length}` };
|
|
@@ -58,9 +65,6 @@ export function fakeClaude(plugin) {
|
|
|
58
65
|
const claude = {
|
|
59
66
|
plugin,
|
|
60
67
|
ui: {
|
|
61
|
-
status: (text) => {
|
|
62
|
-
shown.statuses.push(text);
|
|
63
|
-
},
|
|
64
68
|
log: (text, options) => {
|
|
65
69
|
const lines = options?.to === 'debug' ? shown.debug : shown.logs;
|
|
66
70
|
lines.push(text);
|
|
@@ -168,7 +172,7 @@ export function fakeClaude(plugin) {
|
|
|
168
172
|
dataHome: async () => undefined,
|
|
169
173
|
configHome: async () => undefined,
|
|
170
174
|
},
|
|
171
|
-
cmod: { call: rejected('cmod.call', () => fakes.cmod.call) },
|
|
175
|
+
cmod: { call: rejected('cmod.call', () => fakes.cmod.call ?? cmodTakesParts) },
|
|
172
176
|
};
|
|
173
177
|
const settle = async () => {
|
|
174
178
|
let seen;
|
package/testing/fake-elements.js
CHANGED
|
@@ -96,15 +96,15 @@ function boxRows({ props, children = [] }, width) {
|
|
|
96
96
|
const left = space('Left', 'X');
|
|
97
97
|
const direction = String(props['flexDirection'] ?? 'row');
|
|
98
98
|
const ordered = direction.endsWith('-reverse') ? [...children].reverse() : children;
|
|
99
|
-
const inner = width - left - space('Right', 'X');
|
|
100
|
-
const rows = direction.startsWith('column') ? stacked(ordered, inner, size('rowGap') ?? size('gap') ?? 0) : sideBySide(ordered, inner, size('columnGap') ?? size('gap') ?? 0);
|
|
99
|
+
const inner = Math.min(width, size('width') ?? width) - left - space('Right', 'X');
|
|
100
|
+
const rows = direction.startsWith('column') ? stacked(ordered, inner, size('rowGap') ?? size('gap') ?? 0) : sideBySide(ordered, inner, size('columnGap') ?? size('gap') ?? 0, props['justifyContent'] === 'space-between');
|
|
101
101
|
return [...blankRows(space('Top', 'Y')), ...rows.map((row) => paddedTo(`${' '.repeat(left)}${row}`, size('minWidth') ?? 0)), ...blankRows(space('Bottom', 'Y'))];
|
|
102
102
|
}
|
|
103
103
|
function stacked(children, width, gap) {
|
|
104
104
|
const blocks = children.map((child) => rowsOf(child, width)).filter((block) => block.length > 0);
|
|
105
105
|
return blocks.flatMap((block, index) => [...blankRows(index === 0 ? 0 : gap), ...block]);
|
|
106
106
|
}
|
|
107
|
-
function sideBySide(children, width, gap) {
|
|
107
|
+
function sideBySide(children, width, gap, isSpread = false) {
|
|
108
108
|
const laid = children
|
|
109
109
|
.map((child) => ({ child, natural: rowsOf(child, Number.POSITIVE_INFINITY) }))
|
|
110
110
|
.filter(({ natural }) => natural.length > 0)
|
|
@@ -114,7 +114,10 @@ function sideBySide(children, width, gap) {
|
|
|
114
114
|
const blocks = laid.map(({ child, natural, canShrink }) => (overflow <= 0 || !canShrink || total === 0 ? natural : rowsOf(child, widthOf(natural) - Math.ceil((overflow * widthOf(natural)) / total))));
|
|
115
115
|
const height = Math.max(0, ...blocks.map((block) => block.length));
|
|
116
116
|
const columns = blocks.map(widthOf);
|
|
117
|
-
|
|
117
|
+
const spaces = blocks.length - 1;
|
|
118
|
+
const free = isSpread && Number.isFinite(width) ? Math.max(width - columns.reduce((sum, each) => sum + each, 0) - gap * spaces, 0) : 0;
|
|
119
|
+
const gapAfter = (index) => gap + Math.floor(free / spaces) + (index < free % spaces ? 1 : 0);
|
|
120
|
+
return Array.from({ length: height }, (_, row) => blocks.map((block, index) => `${paddedTo(block[row] ?? '', index === spaces ? 0 : (columns[index] ?? 0))}${index === spaces ? '' : ' '.repeat(gapAfter(index))}`).join(''));
|
|
118
121
|
}
|
|
119
122
|
function minWidthOf(node) {
|
|
120
123
|
if (typeof node === 'string')
|
|
@@ -8,7 +8,7 @@ import { type Sender } from '../session/context/turn/message/sender.js';
|
|
|
8
8
|
import { Skill } from '../session/context/turn/message/skill.js';
|
|
9
9
|
import { ToolCall } from '../session/context/turn/message/tool-call.js';
|
|
10
10
|
import { ToolResult } from '../session/context/turn/message/tool-result.js';
|
|
11
|
-
import { type SessionChange, type SessionData, type TurnEnding } from '../session/conversation.js';
|
|
11
|
+
import { type SessionChange, type SessionData, type TurnData, type TurnEnding } from '../session/conversation.js';
|
|
12
12
|
import type { Place } from '../runtime/before.js';
|
|
13
13
|
import type { Claude } from '../runtime/claude.js';
|
|
14
14
|
export type MessageDraft = {
|
|
@@ -65,6 +65,7 @@ export type FakeSessions = {
|
|
|
65
65
|
readonly parent?: string;
|
|
66
66
|
}): string;
|
|
67
67
|
start(id: string): void;
|
|
68
|
+
lastTurn(): TurnData | undefined;
|
|
68
69
|
readonly sent: {
|
|
69
70
|
readonly session: string;
|
|
70
71
|
readonly task?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fake-sessions.d.ts","sourceRoot":"","sources":["../src/testing/fake-sessions.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,qDAAqD,CAAA;AACrF,OAAO,EAAE,OAAO,EAA0D,MAAM,4CAA4C,CAAA;AAC5H,OAAO,EAAE,IAAI,EAAE,MAAM,yCAAyC,CAAA;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AAClE,OAAO,EAAE,KAAK,EAAE,MAAM,0CAA0C,CAAA;AAChE,OAAO,EAAyB,KAAK,MAAM,EAAE,MAAM,2CAA2C,CAAA;AAG9F,OAAO,EAAE,KAAK,EAAE,MAAM,0CAA0C,CAAA;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,8CAA8C,CAAA;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,gDAAgD,CAAA;AAC3E,OAAO,EAA2F,KAAK,aAAa,EAAE,KAAK,WAAW,
|
|
1
|
+
{"version":3,"file":"fake-sessions.d.ts","sourceRoot":"","sources":["../src/testing/fake-sessions.ts"],"names":[],"mappings":"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"}
|
package/testing/fake-sessions.js
CHANGED
|
@@ -239,6 +239,7 @@ export function fakeSessions({ project, cwd, given, push, callTool }) {
|
|
|
239
239
|
if (session.conversation.state.context === 0)
|
|
240
240
|
record(session, session.conversation.startContext(now()));
|
|
241
241
|
},
|
|
242
|
+
lastTurn: () => [...currentSession().turns.values()].filter((turn) => turn.prompt.task === undefined).at(-1),
|
|
242
243
|
sent,
|
|
243
244
|
};
|
|
244
245
|
}
|
package/testing.d.ts
CHANGED
|
@@ -8,14 +8,27 @@ import { type Turn } from './session/context/turn/turn.js';
|
|
|
8
8
|
import { sessionEvents, type AnswerOf, type BeforeName, type CommandRun, type CommandRunAnswer, type Placement, type ProcessAnswer, type ProcessAsked, type ProcessName, type ProcessPayload } from './session/session.js';
|
|
9
9
|
import { type Fakes, type Shown as ShownByClaude, type TestCall } from './testing/fake-claude.js';
|
|
10
10
|
import { type FakeSession, type FakeSessions, type MessageDraft } from './testing/fake-sessions.js';
|
|
11
|
-
import { type
|
|
11
|
+
import { type Row, type StyleProps } from './ui/styles.js';
|
|
12
|
+
import { type Area } from './ui/areas.js';
|
|
12
13
|
export type { Fakes, TestCall } from './testing/fake-claude.js';
|
|
13
14
|
export type { FakeSession, MessageDraft } from './testing/fake-sessions.js';
|
|
14
15
|
export type { RoutedEvent } from './runtime/hooks.js';
|
|
15
16
|
export type { ShownToast } from './runtime/toaster.js';
|
|
17
|
+
export type ShownPart = {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly params: unknown;
|
|
20
|
+
lines(options?: {
|
|
21
|
+
readonly columns?: number;
|
|
22
|
+
}): Promise<string[]>;
|
|
23
|
+
press(key: string, link?: string): Promise<void>;
|
|
24
|
+
input(key: string, text: string, type?: UiInputArgument['kind']): Promise<void>;
|
|
25
|
+
select(key: string, value: string): Promise<void>;
|
|
26
|
+
};
|
|
16
27
|
export type Shown = ShownByClaude & {
|
|
17
28
|
readonly toasts: readonly ShownToast[];
|
|
18
29
|
readonly sent: FakeSessions['sent'];
|
|
30
|
+
readonly pane: ShownPart;
|
|
31
|
+
readonly page: ShownPart;
|
|
19
32
|
};
|
|
20
33
|
export type TestOptions<State extends object, Declared extends Options = Options> = {
|
|
21
34
|
readonly state?: {
|
|
@@ -85,14 +98,16 @@ export type TestedMod<State extends object> = {
|
|
|
85
98
|
readonly openedBy?: string;
|
|
86
99
|
readonly parent?: string;
|
|
87
100
|
}): Promise<string>;
|
|
88
|
-
lines(
|
|
89
|
-
|
|
101
|
+
lines(area: Area, props?: {
|
|
102
|
+
readonly columns?: number;
|
|
103
|
+
}): Promise<string[]>;
|
|
104
|
+
lines<K extends Row>(kind: K, props: Omit<StyleProps<K>, 'Default'>, requestId?: string): Promise<string[]>;
|
|
90
105
|
type(line: string): Promise<CommandReply>;
|
|
106
|
+
hotkey(name: string): Promise<void>;
|
|
91
107
|
callTool(name: string, input: Record<string, unknown>): Promise<EventResult<'tool.call'>>;
|
|
92
|
-
press(
|
|
93
|
-
input(paneId: string, key: string, text: string, type?: UiInputArgument['kind']): Promise<void>;
|
|
94
|
-
select(paneId: string, key: string, value: string): Promise<void>;
|
|
108
|
+
press(area: Area, key: string): Promise<void>;
|
|
95
109
|
moveTo(projectRoot: string, cwd?: string): Promise<void>;
|
|
110
|
+
readonly api: Readonly<Record<string, (input?: unknown) => Promise<unknown>>>;
|
|
96
111
|
readonly state: Readonly<State>;
|
|
97
112
|
readonly calls: readonly TestCall[];
|
|
98
113
|
readonly fakes: Fakes;
|
package/testing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,IAAI,EAEJ,gBAAgB,EAGhB,WAAW,EASX,eAAe,EAGhB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAazD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,2CAA2C,CAAA;AAKnE,OAAO,EAAW,KAAK,IAAI,EAAE,MAAM,gCAAgC,CAAA;AACnE,OAAO,EAA+B,aAAa,EAAE,KAAK,QAAQ,EAAoB,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,KAAK,gBAAgB,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAGzQ,OAAO,EAA4B,KAAK,KAAK,EAAE,KAAK,KAAK,IAAI,aAAa,EAAE,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AAG3H,OAAO,EAAgB,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAEjH,OAAO,EAAe,KAAK,
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["src/testing.ts"],"names":[],"mappings":"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"}
|
package/testing.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readSteps, storeFolder } from './records.js';
|
|
1
|
+
import { hotkeyCommand, readSteps, storeFolder } from './records.js';
|
|
2
2
|
import { answerCall, cmodPluginName, notInstalled } from './runtime/dependencies.js';
|
|
3
3
|
import { closingContext, foldBefore, installBefore, withAnswer } from './runtime/before.js';
|
|
4
4
|
import { permissionRoutes } from './runtime/permission-check.js';
|
|
@@ -21,7 +21,14 @@ import { elements, findElement, rowsOf } from './testing/fake-elements.js';
|
|
|
21
21
|
import { fakeFiles } from './testing/fake-files.js';
|
|
22
22
|
import { fakeSessions } from './testing/fake-sessions.js';
|
|
23
23
|
import { listed, messageOf } from './utils/text.js';
|
|
24
|
-
import { componentOf } from './ui/
|
|
24
|
+
import { componentOf } from './ui/styles.js';
|
|
25
|
+
import { areas, TurnEnd as TurnEndArea } from './ui/areas.js';
|
|
26
|
+
import { drawWith, elementOf } from './ui/elements.js';
|
|
27
|
+
import { drawnParts } from './runtime/parts.js';
|
|
28
|
+
import { serveWidgets } from './runtime/widgets.js';
|
|
29
|
+
import { engineOf } from './runtime/engine.js';
|
|
30
|
+
import { pagesOf } from './runtime/pages.js';
|
|
31
|
+
import { partMethods } from './runtime/ui.js';
|
|
25
32
|
const defaultColumns = 80;
|
|
26
33
|
const defaultRows = 24;
|
|
27
34
|
const presentation = { isFullscreen: false, columns: defaultColumns };
|
|
@@ -54,7 +61,65 @@ export function testMod(definition, options = {}) {
|
|
|
54
61
|
},
|
|
55
62
|
});
|
|
56
63
|
conversations.start(sessionId);
|
|
57
|
-
|
|
64
|
+
const callMod = async (method, input, caller = cmodPluginName) => calledAnswer(caller, await lifecycle.route('cmod.call', { to: name, method, input, caller }, async () => ({ deny: notInstalled(name) })));
|
|
65
|
+
let declaredPages = [];
|
|
66
|
+
let openPages = [];
|
|
67
|
+
let shownPage;
|
|
68
|
+
const tellShown = () => callMod(partMethods.shown, { open: openPages, shown: shownPage?.id ?? null, isFocused: shownPage?.isFocused ?? false });
|
|
69
|
+
const applyRequests = async (requests) => {
|
|
70
|
+
for (const request of requests) {
|
|
71
|
+
if ('open' in request) {
|
|
72
|
+
const isSettings = declaredPages.some((page) => page.id === request.open && page.place === 'settings');
|
|
73
|
+
if (!isSettings)
|
|
74
|
+
openPages = [...openPages.filter((page) => page.id !== request.open), { id: request.open, params: request.params ?? null }];
|
|
75
|
+
shownPage = { id: request.open, params: request.params ?? null, isFocused: request.focus === true };
|
|
76
|
+
}
|
|
77
|
+
else if ('close' in request) {
|
|
78
|
+
openPages = openPages.filter((page) => page.id !== request.close);
|
|
79
|
+
if (shownPage?.id === request.close)
|
|
80
|
+
shownPage = undefined;
|
|
81
|
+
}
|
|
82
|
+
else if ('settings' in request) {
|
|
83
|
+
const first = declaredPages.find((page) => page.place === 'settings');
|
|
84
|
+
shownPage = first === undefined ? undefined : { id: first.id, params: null, isFocused: true };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
await tellShown();
|
|
88
|
+
};
|
|
89
|
+
let widgets;
|
|
90
|
+
const updated = async ({ parts, redraw, requests }) => {
|
|
91
|
+
if (parts !== undefined)
|
|
92
|
+
declaredPages = pagesOf(parts);
|
|
93
|
+
if (parts !== undefined || redraw !== undefined)
|
|
94
|
+
(await widgetsOf()).update(name, { ...(parts === undefined ? {} : { parts }), ...(redraw === undefined ? {} : { redraw }) });
|
|
95
|
+
if (requests !== undefined && requests.length > 0)
|
|
96
|
+
await applyRequests(requests);
|
|
97
|
+
};
|
|
98
|
+
const pressPart = (input) => callMod(partMethods.press, input).then((update) => updated((update ?? {})));
|
|
99
|
+
const servedWidgets = async () => {
|
|
100
|
+
const own = name === cmodPluginName && lifecycle.mod !== undefined ? engineOf(lifecycle.mod).widgets : undefined;
|
|
101
|
+
if (own !== undefined)
|
|
102
|
+
return own;
|
|
103
|
+
const claude = { ...fake.claude, cmod: { call: async (input) => (input.to === name ? callMod(input.method, input.input) : fake.claude.cmod.call(input)) } };
|
|
104
|
+
const report = (_id, message, error) => fake.claude.ui.log(`cmod: ${message}: ${messageOf(error)}`, { to: 'debug' });
|
|
105
|
+
const press = (_mod, input) => void pressPart(input).catch((error) => fake.claude.ui.log(`cmod: ${name} did not take the press: ${messageOf(error)}`, { to: 'debug' }));
|
|
106
|
+
return serveWidgets({
|
|
107
|
+
claude,
|
|
108
|
+
router: cmodPlugin,
|
|
109
|
+
store: storeFolder({ HOME: await fake.claude.env.home(), XDG_DATA_HOME: await fake.claude.env.dataHome() }),
|
|
110
|
+
report,
|
|
111
|
+
turnClosedBy: () => conversations.lastTurn(),
|
|
112
|
+
parts: drawnParts({ claude, report, press }),
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
const widgetsOf = () => (widgets ??= servedWidgets());
|
|
116
|
+
const dependencies = fakeDependencies(name, options.dependencies ?? {}, conversations);
|
|
117
|
+
fake.fakes.cmod.call = async (call) => {
|
|
118
|
+
if (call.to !== cmodPluginName || call.method !== partMethods.changed)
|
|
119
|
+
return dependencies(call);
|
|
120
|
+
await updated(call.input);
|
|
121
|
+
return { isHeld: call.input.isAsked === true };
|
|
122
|
+
};
|
|
58
123
|
const appendRow = fake.claude.session.append;
|
|
59
124
|
fake.claude.session.append = async (args) => {
|
|
60
125
|
const added = await appendRow(args);
|
|
@@ -122,14 +187,25 @@ export function testMod(definition, options = {}) {
|
|
|
122
187
|
});
|
|
123
188
|
const route = async (event, input, below) => {
|
|
124
189
|
await start();
|
|
190
|
+
if (event === 'ui.render') {
|
|
191
|
+
await widgetsOf();
|
|
192
|
+
await fake.settle();
|
|
193
|
+
}
|
|
125
194
|
return cmodPlugin.dispatch(event, input, (passed) => lifecycle.route(event, passed, async (e) => (below ?? claudeCodeAnswer(event, e))));
|
|
126
195
|
};
|
|
196
|
+
const runCommand = async (command, args) => {
|
|
197
|
+
await start();
|
|
198
|
+
const reached = new Set();
|
|
199
|
+
const ownRun = async () => (reached.add(command), {});
|
|
200
|
+
const answer = await cmodPlugin.dispatch('command.run', { command, args, origin: { kind: 'composer' }, presentation }, (passed) => lifecycle.route('command.run', passed, ownRun));
|
|
201
|
+
if (reached.has(command) && answer.text === undefined)
|
|
202
|
+
throw new Error(!fake.shown.commands.includes(command) ? `${name} has no slash command /${command}. Add it in setup with mod.use(slashCommand({ name: '${command}', … })).` : `${name}: /${command} reached Claude Code's own run of the command, so Claude Code shows "${name} registered /${command} but no command.run hook answered it". Answer it without calling next(e).`);
|
|
203
|
+
return answer;
|
|
204
|
+
};
|
|
127
205
|
const drawPane = async (paneId) => {
|
|
128
206
|
await start();
|
|
129
|
-
if (!fake.shown.openPanes.has(paneId))
|
|
130
|
-
|
|
131
|
-
throw new Error(`The pane "${paneId}" of ${name} is not open, and a user sees a pane only while it is open. Open it first with ${opening}.`);
|
|
132
|
-
}
|
|
207
|
+
if (!fake.shown.openPanes.has(paneId))
|
|
208
|
+
throw new Error(`The pane "${paneId}" of ${name} closed, and a user sees a pane only while it is open.`);
|
|
133
209
|
const opened = fake.calls.findLast((call) => call.call === 'ui.open' && call.args[0].id === paneId)?.args[0];
|
|
134
210
|
const columns = opened?.columns ?? defaultColumns;
|
|
135
211
|
const isFocused = (await fake.claude.ui.panes()).some((open) => open.id === paneId && open.isFocused);
|
|
@@ -137,13 +213,99 @@ export function testMod(definition, options = {}) {
|
|
|
137
213
|
const drawing = await route('ui.render', { surface: 'terminal', component: 'Pane', requestId: paneId, props }, elements.Box({}));
|
|
138
214
|
return { drawing, columns };
|
|
139
215
|
};
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
216
|
+
const drawPage = async (pageId, columns = defaultColumns) => {
|
|
217
|
+
await start();
|
|
218
|
+
const page = shownPage;
|
|
219
|
+
if (page?.id !== pageId)
|
|
220
|
+
throw new Error(`The page "${pageId}" of ${name} no longer shows, and a user sees a page only while it shows.`);
|
|
221
|
+
const drawn = (await callMod(partMethods.draw, { page: pageId, params: page.params, columns, isFocused: page.isFocused }));
|
|
222
|
+
await fake.settle();
|
|
223
|
+
const tree = drawn?.tree ?? null;
|
|
224
|
+
const drawing = tree === null || drawn === null ? elements.Box({}) : drawWith(elements, () => elementOf(tree, '', (handler, args) => void pressPart({ draw: drawn.draw, handler, args })));
|
|
225
|
+
return { drawing, columns };
|
|
226
|
+
};
|
|
227
|
+
const shownNow = (isPane) => {
|
|
228
|
+
if (isPane) {
|
|
229
|
+
const id = [...fake.shown.openPanes].at(-1);
|
|
230
|
+
if (id === undefined)
|
|
231
|
+
throw new Error(`No pane of ${name} shows. Open one with pane.open(), from a slash command, a hotkey, or setup.`);
|
|
232
|
+
return { id, params: undefined };
|
|
233
|
+
}
|
|
234
|
+
if (shownPage === undefined)
|
|
235
|
+
throw new Error(`No page of ${name} shows in the Sidebar. Open one with page.open(), or Settings with mod.settings.open().`);
|
|
236
|
+
return shownPage;
|
|
237
|
+
};
|
|
238
|
+
const shownPart = (isPane) => {
|
|
239
|
+
const kind = isPane ? 'pane' : 'page';
|
|
240
|
+
const draw = async (columns) => {
|
|
241
|
+
await start();
|
|
242
|
+
const { id } = shownNow(isPane);
|
|
243
|
+
return { id, ...(isPane ? await drawPane(id) : await drawPage(id, columns)) };
|
|
244
|
+
};
|
|
245
|
+
const drawnElement = async (type, key) => {
|
|
246
|
+
const { id, drawing } = await draw();
|
|
247
|
+
const props = findElement(drawing, type, key);
|
|
248
|
+
if (props === undefined)
|
|
249
|
+
throw new Error(`The ${kind} "${id}" of ${name} draws no ${type} with the ${type === 'Button' ? 'key or label' : 'key'} "${key}".`);
|
|
250
|
+
return { id, props };
|
|
251
|
+
};
|
|
252
|
+
const eventIn = (id, key) => ({ plugin: name, element: key, component: 'Pane', requestId: id, surface: 'terminal' });
|
|
253
|
+
return {
|
|
254
|
+
get id() {
|
|
255
|
+
return shownNow(isPane).id;
|
|
256
|
+
},
|
|
257
|
+
get params() {
|
|
258
|
+
return shownNow(isPane).params;
|
|
259
|
+
},
|
|
260
|
+
lines: async (lineOptions) => {
|
|
261
|
+
const { drawing, columns } = await draw(lineOptions?.columns);
|
|
262
|
+
return rowsOf(drawing, columns).map((row) => row.trimEnd());
|
|
263
|
+
},
|
|
264
|
+
async press(key, link) {
|
|
265
|
+
const { id, props } = await drawnElement(link === undefined ? 'Button' : 'Markdown', key);
|
|
266
|
+
if (link !== undefined && props['onLinkPress'] === undefined)
|
|
267
|
+
throw new Error(`The Markdown "${key}" in the ${kind} "${id}" of ${name} has no onLinkPress, so Claude Code opens its links itself.`);
|
|
268
|
+
const pressed = { ...eventIn(id, String(props['key'])), ...(link === undefined ? {} : { link: { href: link } }) };
|
|
269
|
+
const run = async (e) => {
|
|
270
|
+
if (e.link === undefined)
|
|
271
|
+
await props['onPress'](e);
|
|
272
|
+
else
|
|
273
|
+
await props['onLinkPress'](e.link, e);
|
|
274
|
+
return { element: e.element };
|
|
275
|
+
};
|
|
276
|
+
if (isPane)
|
|
277
|
+
await lifecycle.route('ui.press', pressed, run);
|
|
278
|
+
else
|
|
279
|
+
await run(pressed);
|
|
280
|
+
await fake.settle();
|
|
281
|
+
},
|
|
282
|
+
async input(key, text, type = 'submit') {
|
|
283
|
+
const { id, props } = await drawnElement('Input', key);
|
|
284
|
+
const typed = { ...eventIn(id, key), kind: type, value: text };
|
|
285
|
+
await props[type === 'submit' ? 'onSubmit' : 'onInput']?.(text, typed);
|
|
286
|
+
await fake.settle();
|
|
287
|
+
},
|
|
288
|
+
async select(key, value) {
|
|
289
|
+
const { id, props } = await drawnElement('Select', key);
|
|
290
|
+
const choices = props['options'].map((option) => option.value);
|
|
291
|
+
if (!choices.includes(value))
|
|
292
|
+
throw new Error(`The Select "${key}" in the ${kind} "${id}" of ${name} has no option "${value}". Its options are ${choices.map((option) => `"${option}"`).join(', ')}.`);
|
|
293
|
+
const picked = { ...eventIn(id, key), value };
|
|
294
|
+
await props['onSelect'](value, picked);
|
|
295
|
+
await fake.settle();
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
};
|
|
299
|
+
const isArea = (target) => areas.includes(target);
|
|
300
|
+
const drawWidgets = async (area, columns = defaultColumns) => {
|
|
301
|
+
await start();
|
|
302
|
+
const served = await widgetsOf();
|
|
303
|
+
await fake.settle();
|
|
304
|
+
const turn = area === TurnEndArea ? conversations.lastTurn() : undefined;
|
|
305
|
+
if (area === TurnEndArea && turn?.closedAt === undefined)
|
|
306
|
+
throw new Error(`${name} has no ended turn, so TurnEnd draws nothing. End one with tested.add({ type: Prompt, text: '…' }), then tested.endTurn().`);
|
|
307
|
+
return { drawing: elements.Box({ flexDirection: 'column', children: [...(await served.draw(area, elements, columns, turn))] }), columns };
|
|
145
308
|
};
|
|
146
|
-
const eventIn = (paneId, key) => ({ plugin: name, element: key, component: 'Pane', requestId: paneId, surface: 'terminal' });
|
|
147
309
|
const drawSlot = async (component, props, requestId = `${component}_${(slotDraws += 1)}`) => {
|
|
148
310
|
await start();
|
|
149
311
|
const input = { surface: 'terminal', component, requestId, props };
|
|
@@ -262,10 +424,14 @@ export function testMod(definition, options = {}) {
|
|
|
262
424
|
return settled(conversations.startTask(task));
|
|
263
425
|
},
|
|
264
426
|
lines: async (target, props, requestId) => {
|
|
427
|
+
if (isArea(target) && (props === undefined || Object.keys(props).every((key) => key === 'columns'))) {
|
|
428
|
+
const { drawing, columns } = await drawWidgets(target, props?.columns);
|
|
429
|
+
return rowsOf(drawing, columns).map((row) => row.trimEnd());
|
|
430
|
+
}
|
|
265
431
|
const component = componentOf(target);
|
|
266
|
-
if (props
|
|
267
|
-
throw new Error(`lines takes
|
|
268
|
-
const { drawing, columns } =
|
|
432
|
+
if (props === undefined || component === undefined)
|
|
433
|
+
throw new Error(`lines takes an area, such as AbovePrompt, or a row kind, such as ToolCall, with its props, and ${target} is neither. Read a pane with tested.shown.pane.lines() and a page with tested.shown.page.lines().`);
|
|
434
|
+
const { drawing, columns } = await drawSlot(component, props, requestId);
|
|
269
435
|
return rowsOf(drawing, columns).map((row) => row.trimEnd());
|
|
270
436
|
},
|
|
271
437
|
async type(line) {
|
|
@@ -273,11 +439,7 @@ export function testMod(definition, options = {}) {
|
|
|
273
439
|
if (typed === null)
|
|
274
440
|
throw new Error(`type takes a slash command the way the user types it, such as tested.type('/help'), and "${line}" does not start with /.`);
|
|
275
441
|
const [, command = '', args = ''] = typed;
|
|
276
|
-
|
|
277
|
-
const answer = await route('command.run', { command, args, origin: { kind: 'composer' }, presentation }, unanswered);
|
|
278
|
-
if (answer === unanswered)
|
|
279
|
-
throw new Error(`${name} has no slash command /${command}. Add it in setup with mod.use(slashCommand({ name: '${command}', … })).`);
|
|
280
|
-
return replyOf(answer);
|
|
442
|
+
return replyOf(await runCommand(command, args));
|
|
281
443
|
},
|
|
282
444
|
async callTool(toolName, input) {
|
|
283
445
|
const tool = `mcp__${name}__${toolName}`;
|
|
@@ -293,32 +455,29 @@ export function testMod(definition, options = {}) {
|
|
|
293
455
|
throw new Error(`${name} has no tool ${toolName}. Add it in setup with mod.use(tool({ name: '${toolName}', … })).`);
|
|
294
456
|
return answer;
|
|
295
457
|
},
|
|
296
|
-
async
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
await lifecycle.route('ui.press', pressed, async (e) => {
|
|
302
|
-
if (e.link === undefined)
|
|
303
|
-
await props['onPress'](e);
|
|
304
|
-
else
|
|
305
|
-
await props['onLinkPress'](e.link, e);
|
|
306
|
-
return { element: e.element };
|
|
307
|
-
});
|
|
308
|
-
},
|
|
309
|
-
async input(paneId, key, text, type = 'submit') {
|
|
310
|
-
const props = await drawnElement(paneId, 'Input', key);
|
|
311
|
-
const typed = { ...eventIn(paneId, key), kind: type, value: text };
|
|
312
|
-
await props[type === 'submit' ? 'onSubmit' : 'onInput']?.(text, typed);
|
|
458
|
+
async hotkey(hotkeyName) {
|
|
459
|
+
if (definition.hotkeys?.[hotkeyName] === undefined)
|
|
460
|
+
throw new Error(`${name} has no hotkey ${hotkeyName}. Declare it in defineMod: hotkeys: { ${hotkeyName}: { title: '…', default: 'ctrl+…' } }.`);
|
|
461
|
+
await runCommand(hotkeyCommand(name, hotkeyName), '');
|
|
462
|
+
await fake.settle();
|
|
313
463
|
},
|
|
314
|
-
async
|
|
315
|
-
const
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
await
|
|
464
|
+
async press(area, key) {
|
|
465
|
+
const { drawing } = await drawWidgets(area);
|
|
466
|
+
const keys = [key, ...(await widgetsOf()).widgets().filter((widget) => widget.mod === name).map((widget) => `${widget.key}:${key}`)];
|
|
467
|
+
const button = keys.map((each) => findElement(drawing, 'Button', each)).find((found) => found !== undefined);
|
|
468
|
+
if (button === undefined)
|
|
469
|
+
throw new Error(`The widgets of ${name} in ${area} draw no Button with the key or label "${key}".`);
|
|
470
|
+
await button['onPress']();
|
|
471
|
+
await fake.settle();
|
|
321
472
|
},
|
|
473
|
+
api: new Proxy({}, {
|
|
474
|
+
get: (_target, method) => method === 'then'
|
|
475
|
+
? undefined
|
|
476
|
+
: async (input) => {
|
|
477
|
+
await start();
|
|
478
|
+
return callMod(String(method), input ?? null, 'tester');
|
|
479
|
+
},
|
|
480
|
+
}),
|
|
322
481
|
async moveTo(nextRoot, nextCwd = nextRoot) {
|
|
323
482
|
await start();
|
|
324
483
|
if (nextRoot !== projectRoot) {
|
|
@@ -348,6 +507,8 @@ export function testMod(definition, options = {}) {
|
|
|
348
507
|
return lifecycle.toasts;
|
|
349
508
|
},
|
|
350
509
|
sent: conversations.sent,
|
|
510
|
+
pane: shownPart(true),
|
|
511
|
+
page: shownPart(false),
|
|
351
512
|
},
|
|
352
513
|
};
|
|
353
514
|
keepClaudeCode(tested, { fire });
|
package/ui/areas.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { RenderElement } from 'claude-code';
|
|
2
|
+
import type { Mod, Turn } from '../mod.js';
|
|
3
|
+
export declare const AbovePrompt = "abovePrompt";
|
|
4
|
+
export declare const BelowPrompt = "belowPrompt";
|
|
5
|
+
export declare const Working = "working";
|
|
6
|
+
export declare const TurnEnd = "turnEnd";
|
|
7
|
+
export declare const Dashboard = "dashboard";
|
|
8
|
+
export declare const areas: readonly ["abovePrompt", "belowPrompt", "working", "turnEnd", "dashboard"];
|
|
9
|
+
export type Area = (typeof areas)[number];
|
|
10
|
+
export type WidgetContext = {
|
|
11
|
+
readonly area: typeof TurnEnd;
|
|
12
|
+
readonly columns: number;
|
|
13
|
+
readonly turn: Turn;
|
|
14
|
+
} | {
|
|
15
|
+
readonly area: Exclude<Area, typeof TurnEnd>;
|
|
16
|
+
readonly columns: number;
|
|
17
|
+
};
|
|
18
|
+
export type WidgetInit<State extends object = Record<never, never>> = {
|
|
19
|
+
readonly id: string;
|
|
20
|
+
readonly title: string;
|
|
21
|
+
readonly areas: readonly [Area, ...Area[]];
|
|
22
|
+
render(mod: Mod<State>, context: WidgetContext): RenderElement | undefined;
|
|
23
|
+
};
|
|
24
|
+
export type Widget = {
|
|
25
|
+
readonly id: string;
|
|
26
|
+
readonly area: Area | undefined;
|
|
27
|
+
remove(): void;
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=areas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"areas.d.ts","sourceRoot":"","sources":["../src/ui/areas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAE1C,eAAO,MAAM,WAAW,gBAAgB,CAAA;AACxC,eAAO,MAAM,WAAW,gBAAgB,CAAA;AACxC,eAAO,MAAM,OAAO,YAAY,CAAA;AAChC,eAAO,MAAM,OAAO,YAAY,CAAA;AAChC,eAAO,MAAM,SAAS,cAAc,CAAA;AAEpC,eAAO,MAAM,KAAK,4EAAmE,CAAA;AAErF,MAAM,MAAM,IAAI,GAAG,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,CAAA;AAEzC,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,OAAO,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAEzL,MAAM,MAAM,UAAU,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI;IACpE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC,CAAA;IAC1C,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,aAAa,GAAG,aAAa,GAAG,SAAS,CAAA;CAC3E,CAAA;AAED,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAAA;IAC/B,MAAM,IAAI,IAAI,CAAA;CACf,CAAA"}
|
package/ui/areas.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const AbovePrompt = 'abovePrompt';
|
|
2
|
+
export const BelowPrompt = 'belowPrompt';
|
|
3
|
+
export const Working = 'working';
|
|
4
|
+
export const TurnEnd = 'turnEnd';
|
|
5
|
+
export const Dashboard = 'dashboard';
|
|
6
|
+
export const areas = [AbovePrompt, BelowPrompt, Working, TurnEnd, Dashboard];
|
package/ui/elements.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { ElementTable, MarkdownProps, RenderElement } from 'claude-code';
|
|
2
2
|
export declare function drawWith<Drawn>(table: ElementTable, draw: () => Drawn, markdown?: (props: MarkdownProps) => RenderElement): Drawn;
|
|
3
|
+
export type Handler = (...args: unknown[]) => unknown;
|
|
4
|
+
export declare function elementData(value: unknown, handlers: Handler[]): unknown;
|
|
5
|
+
export declare function elementOf(data: unknown, keyPrefix: string, press: (handler: number, args: readonly unknown[]) => void): unknown;
|
|
3
6
|
export declare const Box: import("claude-code").ElementConstructor<import("claude-code").BoxProps>;
|
|
4
7
|
export declare const Text: import("claude-code").ElementConstructor<import("claude-code").TextProps>;
|
|
5
8
|
export declare const Button: import("claude-code").ElementConstructor<import("claude-code").ButtonProps>;
|
package/ui/elements.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"elements.d.ts","sourceRoot":"","sources":["../src/ui/elements.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,YAAY,EAAY,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAcpG,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,aAAa,GAAG,KAAK,CASjI;
|
|
1
|
+
{"version":3,"file":"elements.d.ts","sourceRoot":"","sources":["../src/ui/elements.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,YAAY,EAAY,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAcpG,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,aAAa,GAAG,KAAK,CASjI;AAwBD,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAA;AAErD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAUxE;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,OAAO,EAAE,KAAK,IAAI,GAAG,OAAO,CAa/H;AAED,eAAO,MAAM,GAAG,0EAAiB,CAAA;AACjC,eAAO,MAAM,IAAI,2EAAkB,CAAA;AACnC,eAAO,MAAM,MAAM,6EAAoB,CAAA;AACvC,eAAO,MAAM,IAAI,2EAAkB,CAAA;AACnC,eAAO,MAAM,IAAI,2EAAkB,CAAA;AACnC,eAAO,MAAM,QAAQ,yDAAsB,CAAA;AAC3C,eAAO,MAAM,KAAK,4EAAmB,CAAA;AACrC,eAAO,MAAM,MAAM,6EAAoB,CAAA;AACvC,eAAO,MAAM,KAAK,4EAAmB,CAAA"}
|