@cmodjs/core 0.2.3 → 1.0.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/docs/commands.md +58 -10
- package/docs/dependencies.md +12 -12
- package/docs/hooks.md +41 -28
- package/docs/index.md +29 -25
- package/docs/install-steps.md +9 -7
- package/docs/jobs.md +59 -111
- package/docs/mod.md +200 -174
- package/docs/options.md +2 -2
- package/docs/permissions.md +27 -16
- package/docs/session.md +248 -0
- package/docs/state.md +11 -7
- package/docs/testing.md +66 -23
- package/docs/ui.md +108 -76
- package/jobs/check.d.ts.map +1 -1
- package/jobs/check.js +5 -2
- package/jobs/context.d.ts +7 -8
- package/jobs/context.d.ts.map +1 -1
- package/jobs/context.js +23 -73
- package/jobs/permissions/decide-permission.d.ts +5 -3
- package/jobs/permissions/decide-permission.d.ts.map +1 -1
- package/jobs/permissions/decide-permission.js +6 -4
- package/jobs/permissions/find-project-scope.d.ts +1 -1
- package/jobs/permissions/find-project-scope.d.ts.map +1 -1
- package/jobs/permissions/find-project-scope.js +8 -11
- package/jobs/permissions/match-target.d.ts +2 -2
- package/jobs/permissions/match-target.d.ts.map +1 -1
- package/jobs/permissions.d.ts +1 -1
- package/jobs/permissions.d.ts.map +1 -1
- package/jobs/permissions.js +6 -4
- package/jobs/program.d.ts.map +1 -1
- package/jobs/program.js +8 -3
- package/jobs/prompt.d.ts +4 -2
- package/jobs/prompt.d.ts.map +1 -1
- package/jobs/prompt.js +16 -15
- package/jobs/schedule.d.ts +2 -2
- package/jobs/schedule.d.ts.map +1 -1
- package/jobs/schedule.js +9 -9
- package/jobs/slash-command.d.ts +4 -4
- package/jobs/slash-command.d.ts.map +1 -1
- package/jobs/slash-command.js +14 -9
- package/jobs/status-line.d.ts +1 -11
- package/jobs/status-line.d.ts.map +1 -1
- package/jobs/status-line.js +8 -12
- package/jobs/tool.d.ts +2 -2
- package/jobs/tool.d.ts.map +1 -1
- package/jobs/tool.js +15 -9
- package/mod.d.ts +154 -75
- package/mod.d.ts.map +1 -1
- package/mod.js +94 -3
- package/options.d.ts +2 -2
- package/options.js +19 -19
- package/package.json +3 -3
- package/records.d.ts +10 -8
- package/records.d.ts.map +1 -1
- package/records.js +28 -17
- package/register.d.ts.map +1 -1
- package/register.js +18 -6
- package/runtime/announce.d.ts +18 -0
- package/runtime/announce.d.ts.map +1 -0
- package/runtime/announce.js +66 -0
- package/runtime/bound.d.ts +4 -0
- package/runtime/bound.d.ts.map +1 -0
- package/runtime/bound.js +9 -0
- package/runtime/claude.d.ts +3 -3
- package/runtime/claude.d.ts.map +1 -1
- package/runtime/conversations.d.ts +16 -0
- package/runtime/conversations.d.ts.map +1 -0
- package/runtime/conversations.js +291 -0
- package/runtime/deadline.d.ts +0 -1
- package/runtime/deadline.d.ts.map +1 -1
- package/runtime/deadline.js +1 -1
- package/runtime/dependencies.d.ts +2 -0
- package/runtime/dependencies.d.ts.map +1 -1
- package/runtime/dependencies.js +9 -0
- package/runtime/engine.d.ts +17 -0
- package/runtime/engine.d.ts.map +1 -0
- package/runtime/engine.js +10 -0
- package/runtime/grants.d.ts +4 -0
- package/runtime/grants.d.ts.map +1 -1
- package/runtime/grants.js +63 -33
- package/runtime/hooks.d.ts +5 -4
- package/runtime/hooks.d.ts.map +1 -1
- package/runtime/hooks.js +10 -5
- package/runtime/installer.d.ts.map +1 -1
- package/runtime/installer.js +11 -9
- package/runtime/lifecycle.d.ts +3 -1
- package/runtime/lifecycle.d.ts.map +1 -1
- package/runtime/lifecycle.js +317 -186
- package/runtime/metadata.d.ts +3 -1
- package/runtime/metadata.d.ts.map +1 -1
- package/runtime/metadata.js +21 -11
- package/runtime/options.d.ts +3 -1
- package/runtime/options.d.ts.map +1 -1
- package/runtime/options.js +4 -4
- package/runtime/programs.d.ts +8 -0
- package/runtime/programs.d.ts.map +1 -1
- package/runtime/programs.js +39 -1
- package/runtime/router.d.ts +2 -1
- package/runtime/router.d.ts.map +1 -1
- package/runtime/router.js +11 -3
- package/runtime/sessions.d.ts +36 -0
- package/runtime/sessions.d.ts.map +1 -0
- package/runtime/sessions.js +235 -0
- package/runtime/state.d.ts +6 -2
- package/runtime/state.d.ts.map +1 -1
- package/runtime/state.js +46 -13
- package/runtime/toaster.d.ts +31 -0
- package/runtime/toaster.d.ts.map +1 -0
- package/runtime/toaster.js +176 -0
- package/runtime/ui.d.ts +6 -17
- package/runtime/ui.d.ts.map +1 -1
- package/runtime/ui.js +36 -112
- package/session/context/context.d.ts +10 -0
- package/session/context/context.d.ts.map +1 -0
- package/session/context/context.js +2 -0
- package/session/context/turn/message/document.d.ts +7 -0
- package/session/context/turn/message/document.d.ts.map +1 -0
- package/session/context/turn/message/document.js +1 -0
- package/session/context/turn/message/message.d.ts +54 -0
- package/session/context/turn/message/message.d.ts.map +1 -0
- package/session/context/turn/message/message.js +137 -0
- package/session/context/turn/message/note.d.ts +8 -0
- package/session/context/turn/message/note.d.ts.map +1 -0
- package/session/context/turn/message/note.js +1 -0
- package/session/context/turn/message/notice.d.ts +6 -0
- package/session/context/turn/message/notice.d.ts.map +1 -0
- package/session/context/turn/message/notice.js +1 -0
- package/session/context/turn/message/prompt.d.ts +8 -0
- package/session/context/turn/message/prompt.d.ts.map +1 -0
- package/session/context/turn/message/prompt.js +1 -0
- package/session/context/turn/message/reply.d.ts +6 -0
- package/session/context/turn/message/reply.d.ts.map +1 -0
- package/session/context/turn/message/reply.js +1 -0
- package/session/context/turn/message/sender.d.ts +14 -0
- package/session/context/turn/message/sender.d.ts.map +1 -0
- package/session/context/turn/message/sender.js +30 -0
- package/session/context/turn/message/skill.d.ts +7 -0
- package/session/context/turn/message/skill.d.ts.map +1 -0
- package/session/context/turn/message/skill.js +1 -0
- package/session/context/turn/message/tool-call.d.ts +9 -0
- package/session/context/turn/message/tool-call.d.ts.map +1 -0
- package/session/context/turn/message/tool-call.js +1 -0
- package/session/context/turn/message/tool-result.d.ts +8 -0
- package/session/context/turn/message/tool-result.d.ts.map +1 -0
- package/session/context/turn/message/tool-result.js +1 -0
- package/session/context/turn/turn.d.ts +12 -0
- package/session/context/turn/turn.d.ts.map +1 -0
- package/session/context/turn/turn.js +2 -0
- package/session/conversation.d.ts +64 -0
- package/session/conversation.d.ts.map +1 -0
- package/session/conversation.js +123 -0
- package/session/goal/goal.d.ts +10 -0
- package/session/goal/goal.d.ts.map +1 -0
- package/session/goal/goal.js +1 -0
- package/session/goal/task.d.ts +16 -0
- package/session/goal/task.d.ts.map +1 -0
- package/session/goal/task.js +3 -0
- package/session/session.d.ts +99 -0
- package/session/session.d.ts.map +1 -0
- package/session/session.js +1 -0
- package/src/jobs/check.ts +7 -4
- package/src/jobs/context.ts +24 -82
- package/src/jobs/permissions/decide-permission.ts +10 -8
- package/src/jobs/permissions/find-project-scope.ts +8 -14
- package/src/jobs/permissions/match-target.ts +3 -3
- package/src/jobs/permissions.ts +8 -6
- package/src/jobs/program.ts +7 -3
- package/src/jobs/prompt.ts +21 -24
- package/src/jobs/schedule.ts +14 -14
- package/src/jobs/slash-command.ts +20 -14
- package/src/jobs/status-line.ts +9 -21
- package/src/jobs/tool.ts +17 -11
- package/src/mod.ts +209 -97
- package/src/options.ts +22 -22
- package/src/records.ts +35 -23
- package/src/register.ts +18 -6
- package/src/runtime/announce.ts +74 -0
- package/src/runtime/bound.ts +12 -0
- package/src/runtime/claude.ts +5 -3
- package/src/runtime/conversations.ts +302 -0
- package/src/runtime/deadline.ts +2 -2
- package/src/runtime/dependencies.ts +12 -0
- package/src/runtime/engine.ts +22 -0
- package/src/runtime/grants.ts +66 -31
- package/src/runtime/hooks.ts +19 -10
- package/src/runtime/installer.ts +10 -9
- package/src/runtime/lifecycle.ts +313 -181
- package/src/runtime/metadata.ts +33 -11
- package/src/runtime/options.ts +19 -4
- package/src/runtime/programs.ts +40 -1
- package/src/runtime/router.ts +11 -4
- package/src/runtime/sessions.ts +242 -0
- package/src/runtime/state.ts +50 -14
- package/src/runtime/toaster.ts +218 -0
- package/src/runtime/ui.ts +42 -139
- package/src/session/context/context.ts +12 -0
- package/src/session/context/turn/message/document.ts +8 -0
- package/src/session/context/turn/message/message.ts +165 -0
- package/src/session/context/turn/message/note.ts +9 -0
- package/src/session/context/turn/message/notice.ts +7 -0
- package/src/session/context/turn/message/prompt.ts +9 -0
- package/src/session/context/turn/message/reply.ts +7 -0
- package/src/session/context/turn/message/sender.ts +38 -0
- package/src/session/context/turn/message/skill.ts +8 -0
- package/src/session/context/turn/message/tool-call.ts +10 -0
- package/src/session/context/turn/message/tool-result.ts +9 -0
- package/src/session/context/turn/turn.ts +14 -0
- package/src/session/conversation.ts +169 -0
- package/src/session/goal/goal.ts +9 -0
- package/src/session/goal/task.ts +19 -0
- package/src/session/session.ts +94 -0
- package/src/testing/fake-claude.ts +23 -18
- package/src/testing/fake-elements.ts +3 -2
- package/src/testing/fake-files.ts +1 -1
- package/src/testing/fake-sessions.ts +203 -0
- package/src/testing.ts +112 -33
- package/src/ui/components.ts +3 -3
- package/src/ui/elements.ts +22 -8
- package/src/ui/markdown.ts +22 -26
- package/src/ui/places.ts +49 -0
- package/src/utils/call-effects.ts +3 -3
- package/src/utils/changelog.ts +32 -0
- package/src/utils/matcher.ts +37 -0
- package/src/utils/metadata.ts +17 -15
- package/src/utils/parse-shell.ts +24 -16
- package/src/utils/paths.ts +15 -1
- package/src/utils/text.ts +4 -0
- package/testing/fake-claude.d.ts +4 -2
- package/testing/fake-claude.d.ts.map +1 -1
- package/testing/fake-claude.js +18 -12
- package/testing/fake-elements.js +4 -2
- package/testing/fake-files.js +1 -1
- package/testing/fake-sessions.d.ts +74 -0
- package/testing/fake-sessions.d.ts.map +1 -0
- package/testing/fake-sessions.js +170 -0
- package/testing.d.ts +33 -13
- package/testing.d.ts.map +1 -1
- package/testing.js +92 -21
- package/ui/components.d.ts +2 -3
- package/ui/components.d.ts.map +1 -1
- package/ui/components.js +3 -3
- package/ui/elements.d.ts.map +1 -1
- package/ui/elements.js +19 -9
- package/ui/markdown.d.ts +35 -33
- package/ui/markdown.d.ts.map +1 -1
- package/ui/markdown.js +14 -16
- package/ui/places.d.ts +43 -0
- package/ui/places.d.ts.map +1 -0
- package/ui/places.js +31 -0
- package/utils/call-effects.d.ts +1 -1
- package/utils/call-effects.d.ts.map +1 -1
- package/utils/call-effects.js +2 -2
- package/utils/changelog.d.ts +9 -0
- package/utils/changelog.d.ts.map +1 -0
- package/utils/changelog.js +29 -0
- package/utils/matcher.d.ts +13 -0
- package/utils/matcher.d.ts.map +1 -0
- package/utils/matcher.js +32 -0
- package/utils/metadata.d.ts +3 -3
- package/utils/metadata.js +18 -14
- package/utils/parse-shell.d.ts +1 -0
- package/utils/parse-shell.d.ts.map +1 -1
- package/utils/parse-shell.js +22 -13
- package/utils/paths.d.ts +2 -0
- package/utils/paths.d.ts.map +1 -1
- package/utils/paths.js +13 -1
- package/utils/text.d.ts +1 -0
- package/utils/text.d.ts.map +1 -1
- package/utils/text.js +3 -0
- package/src/ui/slots.ts +0 -36
- package/ui/slots.d.ts +0 -208
- package/ui/slots.d.ts.map +0 -1
- package/ui/slots.js +0 -19
package/runtime/state.js
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
|
-
import { messageOf } from '../utils/text.js';
|
|
2
1
|
const lifetimes = ['memory', 'session', 'project', 'global'];
|
|
3
|
-
const
|
|
2
|
+
const keptProjects = 20;
|
|
4
3
|
const listeners = new WeakMap();
|
|
5
4
|
export function onStateChange(state, listener) {
|
|
6
5
|
const own = listeners.get(state) ?? new Set();
|
|
7
6
|
own.add(listener);
|
|
8
7
|
listeners.set(state, own);
|
|
9
8
|
}
|
|
10
|
-
export function
|
|
9
|
+
export async function sessionsWith(claude, name, key, value) {
|
|
10
|
+
const prefix = `${name}.${key}.sessions.`;
|
|
11
|
+
const found = [];
|
|
12
|
+
for (const stored of (await claude.store.keys()).filter((each) => each.startsWith(prefix))) {
|
|
13
|
+
if ((await claude.store.get(stored)) === value)
|
|
14
|
+
found.push(stored.slice(prefix.length));
|
|
15
|
+
}
|
|
16
|
+
return found;
|
|
17
|
+
}
|
|
18
|
+
export function createState({ name, initial, session, root, claude, report, changed: redraw, forkParent }) {
|
|
11
19
|
const changed = () => {
|
|
12
20
|
redraw();
|
|
13
21
|
for (const listener of listeners.get(state) ?? [])
|
|
@@ -20,21 +28,36 @@ export function createState({ name, initial, session, root, claude, changed: red
|
|
|
20
28
|
let turn = Promise.resolve();
|
|
21
29
|
const inTurn = (task) => {
|
|
22
30
|
const run = turn.then(task);
|
|
23
|
-
turn =
|
|
31
|
+
turn = Promise.allSettled([run]);
|
|
24
32
|
return run;
|
|
25
33
|
};
|
|
26
34
|
const ownersKey = (lifetime, key) => `${name}.${key}.${lifetime === 'project' ? 'projects' : 'sessions'}.`;
|
|
27
35
|
const storeKey = (lifetime, key, owner) => (lifetime === 'global' ? `${name}.${key}` : `${ownersKey(lifetime, key)}${owner}`);
|
|
28
36
|
const read = (lifetime, key, owner) => claude.store.get(storeKey(lifetime, key, owner));
|
|
29
37
|
const write = async (lifetime, key, value, owner) => {
|
|
30
|
-
if (lifetime
|
|
38
|
+
if (lifetime !== 'project')
|
|
31
39
|
return claude.store.set(storeKey(lifetime, key, owner), value);
|
|
32
40
|
await claude.store.delete(storeKey(lifetime, key, owner));
|
|
33
41
|
await claude.store.set(storeKey(lifetime, key, owner), value);
|
|
34
42
|
const owners = (await claude.store.keys()).filter((stored) => stored.startsWith(ownersKey(lifetime, key)));
|
|
35
|
-
for (const oldest of owners.slice(0, -
|
|
43
|
+
for (const oldest of owners.slice(0, -keptProjects))
|
|
36
44
|
await claude.store.delete(oldest);
|
|
37
45
|
};
|
|
46
|
+
const transcriptsKey = `${name}.$transcripts.`;
|
|
47
|
+
const forgetEndedSessions = async (current) => {
|
|
48
|
+
const stored = await claude.store.keys();
|
|
49
|
+
for (const recorded of stored.filter((key) => key.startsWith(transcriptsKey))) {
|
|
50
|
+
const owner = recorded.slice(transcriptsKey.length);
|
|
51
|
+
if (owner === current)
|
|
52
|
+
continue;
|
|
53
|
+
const transcript = await claude.store.get(recorded);
|
|
54
|
+
if (typeof transcript === 'string' && (await claude.fs.exists(transcript)))
|
|
55
|
+
continue;
|
|
56
|
+
for (const value of stored.filter((key) => key.startsWith(`${name}.`) && key.endsWith(`.sessions.${owner}`)))
|
|
57
|
+
await claude.store.delete(value);
|
|
58
|
+
await claude.store.delete(recorded);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
38
61
|
const loadGroups = async (only, next) => {
|
|
39
62
|
const loaded = [];
|
|
40
63
|
for (const lifetime of only) {
|
|
@@ -47,13 +70,14 @@ export function createState({ name, initial, session, root, claude, changed: red
|
|
|
47
70
|
current[lifetime][key] = saved === undefined ? declared[lifetime][key] : frozen(name, `${lifetime}.${key}`, saved);
|
|
48
71
|
changed();
|
|
49
72
|
};
|
|
50
|
-
const copySession = async (
|
|
73
|
+
const copySession = async (from, to) => {
|
|
51
74
|
for (const key of Object.keys(declared.session)) {
|
|
52
|
-
const saved = await read('session', key,
|
|
75
|
+
const saved = await read('session', key, from);
|
|
53
76
|
if (saved !== undefined)
|
|
54
|
-
await write('session', key, saved,
|
|
77
|
+
await write('session', key, saved, to);
|
|
55
78
|
}
|
|
56
79
|
};
|
|
80
|
+
const hasSaved = async (owner) => (await claude.store.keys()).some((key) => key.startsWith(`${name}.`) && key.endsWith(`.sessions.${owner}`));
|
|
57
81
|
const group = (lifetime) => new Proxy(current[lifetime], {
|
|
58
82
|
set(target, key, value) {
|
|
59
83
|
if (typeof key !== 'string')
|
|
@@ -66,7 +90,7 @@ export function createState({ name, initial, session, root, claude, changed: red
|
|
|
66
90
|
if (lifetime !== 'memory') {
|
|
67
91
|
const owner = lifetime === 'project' ? projectRoot : sessionId;
|
|
68
92
|
inTurn(() => write(lifetime, key, value, owner)).catch((error) => {
|
|
69
|
-
|
|
93
|
+
report({ id: `state:${lifetime}.${key}`, message: `Could not keep state.${lifetime}.${key}. Keep only JSON data in mod.state`, error });
|
|
70
94
|
});
|
|
71
95
|
}
|
|
72
96
|
changed();
|
|
@@ -80,13 +104,22 @@ export function createState({ name, initial, session, root, claude, changed: red
|
|
|
80
104
|
return projectRoot;
|
|
81
105
|
},
|
|
82
106
|
changed,
|
|
83
|
-
load: () => inTurn(() =>
|
|
107
|
+
load: () => inTurn(async () => {
|
|
108
|
+
if (Object.keys(declared.session).length > 0 && !(await hasSaved(sessionId))) {
|
|
109
|
+
const parent = await forkParent();
|
|
110
|
+
if (parent !== undefined)
|
|
111
|
+
await copySession(parent, sessionId);
|
|
112
|
+
}
|
|
113
|
+
return loadGroups(lifetimes, { root: projectRoot, session: sessionId });
|
|
114
|
+
}),
|
|
84
115
|
moveTo: (nextRoot) => inTurn(async () => (nextRoot === projectRoot ? undefined : loadGroups(['project'], { root: nextRoot, session: sessionId }))),
|
|
85
|
-
switchSession: (nextSession, source) => inTurn(async () => {
|
|
116
|
+
switchSession: (nextSession, source, transcript) => inTurn(async () => {
|
|
117
|
+
await claude.store.set(`${transcriptsKey}${nextSession}`, transcript);
|
|
118
|
+
await forgetEndedSessions(nextSession);
|
|
86
119
|
if (nextSession === sessionId)
|
|
87
120
|
return;
|
|
88
121
|
if (source === 'fork')
|
|
89
|
-
await copySession(nextSession);
|
|
122
|
+
await copySession(sessionId, nextSession);
|
|
90
123
|
return loadGroups(['memory', 'session'], { root: projectRoot, session: nextSession });
|
|
91
124
|
}),
|
|
92
125
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ElementTable, RenderElement, RenderPropsOf } from 'claude-code';
|
|
2
|
+
import type { Mod, Toast, ToastId, ToastType } from '../mod.js';
|
|
3
|
+
import type { Claude } from './claude.js';
|
|
4
|
+
export type ShownToast = {
|
|
5
|
+
readonly type: ToastType;
|
|
6
|
+
readonly message: string;
|
|
7
|
+
readonly description?: string;
|
|
8
|
+
};
|
|
9
|
+
export type Failure = {
|
|
10
|
+
readonly id: string;
|
|
11
|
+
readonly message: string;
|
|
12
|
+
readonly error?: unknown;
|
|
13
|
+
readonly description?: string;
|
|
14
|
+
};
|
|
15
|
+
export type Toaster = {
|
|
16
|
+
readonly toast: Toast;
|
|
17
|
+
copy(text: string): Promise<void>;
|
|
18
|
+
readonly shown: readonly ShownToast[];
|
|
19
|
+
draw(drawn: RenderElement, table: ElementTable, band: RenderPropsOf['AbovePrompt']): RenderElement;
|
|
20
|
+
};
|
|
21
|
+
export declare function createToaster(claude: Claude, name: string): Toaster;
|
|
22
|
+
export type FailureTarget = {
|
|
23
|
+
readonly name: string;
|
|
24
|
+
readonly ui: Pick<Mod['ui'], 'toast' | 'copy'>;
|
|
25
|
+
readonly claude: {
|
|
26
|
+
readonly plugin: Claude['plugin'];
|
|
27
|
+
readonly fs: Pick<Claude['fs'], 'read'>;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
export declare function showFailure({ name, ui, claude }: FailureTarget, { id, message, error, description }: Failure): ToastId;
|
|
31
|
+
//# sourceMappingURL=toaster.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toaster.d.ts","sourceRoot":"","sources":["../src/runtime/toaster.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAS,MAAM,aAAa,CAAA;AACpF,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAgB,SAAS,EAAE,MAAM,WAAW,CAAA;AAI7E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9G,MAAM,MAAM,OAAO,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhI,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAA;IACrC,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,CAAC,aAAa,CAAC,GAAG,aAAa,CAAA;CACnG,CAAA;AAuBD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAmJnE;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,CAAA;IAC9C,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAA;KAAE,CAAA;CAChG,CAAA;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,WAA6B,EAAE,EAAE,OAAO,GAAG,OAAO,CAcxI"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { Box, Button, drawWith, Text } from '../ui/elements.js';
|
|
2
|
+
import { coreVersion } from '../records.js';
|
|
3
|
+
import { firstLine, messageOf } from '../utils/text.js';
|
|
4
|
+
const defaultDuration = 4000;
|
|
5
|
+
const visibleToasts = 3;
|
|
6
|
+
const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
7
|
+
const spinnerMs = 100;
|
|
8
|
+
const icons = {
|
|
9
|
+
default: { glyph: ' ' },
|
|
10
|
+
success: { glyph: '✓', color: 'success' },
|
|
11
|
+
info: { glyph: 'ℹ', color: 'suggestion' },
|
|
12
|
+
warning: { glyph: '⚠', color: 'warning' },
|
|
13
|
+
error: { glyph: '✗', color: 'error' },
|
|
14
|
+
};
|
|
15
|
+
export function createToaster(claude, name) {
|
|
16
|
+
const entries = new Map();
|
|
17
|
+
let lastId = 0;
|
|
18
|
+
let frame = 0;
|
|
19
|
+
let spinning;
|
|
20
|
+
let isHeld = false;
|
|
21
|
+
let showsAll = false;
|
|
22
|
+
const changed = () => {
|
|
23
|
+
const isLoading = [...entries.values()].some((entry) => entry.type === 'loading');
|
|
24
|
+
if (isLoading && spinning === undefined) {
|
|
25
|
+
spinning = claude.clock.every(spinnerMs, () => {
|
|
26
|
+
frame += 1;
|
|
27
|
+
claude.ui.invalidate('ui.render');
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (!isLoading && spinning !== undefined) {
|
|
31
|
+
spinning.cancel();
|
|
32
|
+
spinning = undefined;
|
|
33
|
+
}
|
|
34
|
+
if (entries.size <= visibleToasts)
|
|
35
|
+
showsAll = false;
|
|
36
|
+
claude.ui.invalidate('ui.render');
|
|
37
|
+
};
|
|
38
|
+
const disarm = (entry) => {
|
|
39
|
+
entry.timer?.cancel();
|
|
40
|
+
entry.timer = undefined;
|
|
41
|
+
};
|
|
42
|
+
const arm = (entry) => {
|
|
43
|
+
const duration = entry.options.duration ?? defaultDuration;
|
|
44
|
+
if (entry.timer !== undefined || entry.type === 'loading' || entry.isOpen || !Number.isFinite(duration))
|
|
45
|
+
return;
|
|
46
|
+
entry.timer = claude.clock.after(duration, () => {
|
|
47
|
+
entry.timer = undefined;
|
|
48
|
+
if (isHeld)
|
|
49
|
+
arm(entry);
|
|
50
|
+
else
|
|
51
|
+
dismiss(entry.id);
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
const dismiss = (id) => {
|
|
55
|
+
for (const entry of entries.values()) {
|
|
56
|
+
if (id !== undefined && entry.id !== id)
|
|
57
|
+
continue;
|
|
58
|
+
disarm(entry);
|
|
59
|
+
entries.delete(entry.id);
|
|
60
|
+
}
|
|
61
|
+
changed();
|
|
62
|
+
};
|
|
63
|
+
const show = (type) => (message, options = {}) => {
|
|
64
|
+
const id = options.id ?? (lastId += 1);
|
|
65
|
+
const previous = entries.get(id);
|
|
66
|
+
if (previous !== undefined)
|
|
67
|
+
disarm(previous);
|
|
68
|
+
const entry = { id, type, message, options, isOpen: previous?.isOpen ?? false, timer: undefined };
|
|
69
|
+
if (type === 'error' && previous?.message !== message)
|
|
70
|
+
claude.ui.log(`${name}: ${message}${typeof options.description === 'string' ? `: ${options.description}` : ''}`, { to: 'debug' });
|
|
71
|
+
entries.set(id, entry);
|
|
72
|
+
changed();
|
|
73
|
+
return id;
|
|
74
|
+
};
|
|
75
|
+
const toast = Object.assign(show('default'), {
|
|
76
|
+
success: show('success'),
|
|
77
|
+
info: show('info'),
|
|
78
|
+
warning: show('warning'),
|
|
79
|
+
error: show('error'),
|
|
80
|
+
loading: show('loading'),
|
|
81
|
+
dismiss,
|
|
82
|
+
promise(task, messages) {
|
|
83
|
+
const id = show('loading')(messages.loading);
|
|
84
|
+
const settled = Promise.resolve()
|
|
85
|
+
.then(() => (typeof task === 'function' ? task() : task))
|
|
86
|
+
.then((value) => {
|
|
87
|
+
show('success')(typeof messages.success === 'function' ? messages.success(value) : messages.success, { id });
|
|
88
|
+
return value;
|
|
89
|
+
}, (error) => {
|
|
90
|
+
show('error')(typeof messages.error === 'function' ? messages.error(error) : messages.error, { id });
|
|
91
|
+
throw error;
|
|
92
|
+
});
|
|
93
|
+
settled.catch(() => undefined);
|
|
94
|
+
return { unwrap: () => settled };
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
const copy = async (text) => {
|
|
98
|
+
const copied = await claude.ui.copy({ text });
|
|
99
|
+
if (!copied.isCopied)
|
|
100
|
+
throw new Error(copied.reason);
|
|
101
|
+
};
|
|
102
|
+
const press = (entry, run) => () => void Promise.resolve()
|
|
103
|
+
.then(run)
|
|
104
|
+
.catch((error) => showFailure({ name, claude, ui: { toast, copy } }, { id: `press:${entry.id}`, message: `${entry.message}: the press failed`, error }));
|
|
105
|
+
const toggle = (entry) => () => {
|
|
106
|
+
entry.isOpen = !entry.isOpen;
|
|
107
|
+
disarm(entry);
|
|
108
|
+
changed();
|
|
109
|
+
};
|
|
110
|
+
const row = (entry) => {
|
|
111
|
+
const { description, action } = entry.options;
|
|
112
|
+
const isText = typeof description === 'string';
|
|
113
|
+
const icon = entry.type === 'loading' ? { glyph: spinner[frame % spinner.length], color: 'claude' } : icons[entry.type];
|
|
114
|
+
const message = isText ? Button({ plain: true, key: `toast:${entry.id}:open`, label: entry.message, onPress: toggle(entry) }) : Text({ children: entry.message });
|
|
115
|
+
const details = !isText ? description : Text({ dimColor: true, wrap: entry.isOpen ? 'wrap' : 'truncate-end', children: entry.isOpen ? description : firstLine(description) });
|
|
116
|
+
const fixed = (child) => Box({ flexShrink: 0, children: child });
|
|
117
|
+
return Box({
|
|
118
|
+
gap: 2,
|
|
119
|
+
children: [
|
|
120
|
+
fixed(Text({ ...(icon.color === undefined ? {} : { color: icon.color }), children: icon.glyph })),
|
|
121
|
+
fixed(Text({ dimColor: true, children: name })),
|
|
122
|
+
Box({ flexDirection: 'column', flexGrow: 1, flexShrink: 1, children: details === undefined ? [message] : [message, details] }),
|
|
123
|
+
...(action === undefined ? [] : [fixed(Button({ plain: true, key: `toast:${entry.id}:action`, label: action.label, onPress: press(entry, action.onClick) }))]),
|
|
124
|
+
fixed(Button({ plain: true, dimColor: true, key: `toast:${entry.id}:close`, label: '✕', onPress: () => dismiss(entry.id) })),
|
|
125
|
+
],
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
return {
|
|
129
|
+
toast,
|
|
130
|
+
copy,
|
|
131
|
+
get shown() {
|
|
132
|
+
return [...entries.values()].map(({ type, message, options }) => ({ type, message, ...(typeof options.description === 'string' ? { description: options.description } : {}) }));
|
|
133
|
+
},
|
|
134
|
+
draw(drawn, table, band) {
|
|
135
|
+
isHeld = band.hasSurvey === true;
|
|
136
|
+
if (isHeld || entries.size === 0)
|
|
137
|
+
return drawn;
|
|
138
|
+
const all = [...entries.values()];
|
|
139
|
+
const hidden = showsAll ? 0 : Math.max(all.length - visibleToasts, 0);
|
|
140
|
+
for (const entry of all.slice(hidden))
|
|
141
|
+
arm(entry);
|
|
142
|
+
const more = Button({
|
|
143
|
+
plain: true,
|
|
144
|
+
dimColor: true,
|
|
145
|
+
key: 'toasts:more',
|
|
146
|
+
label: `+${hidden} more`,
|
|
147
|
+
onPress: () => {
|
|
148
|
+
showsAll = true;
|
|
149
|
+
changed();
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
return drawWith(table, () => Box({ flexDirection: 'column', children: [drawn, ...(hidden > 0 ? [more] : []), ...all.slice(hidden).map(row)] }));
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
export function showFailure({ name, ui, claude }, { id, message, error, description = describe(error) }) {
|
|
157
|
+
const stack = error instanceof Error && error.stack !== undefined ? `\n\n${error.stack}` : '';
|
|
158
|
+
const show = (label) => ui.toast.error(message, { id, description, duration: Infinity, action: { label, onClick: copy } });
|
|
159
|
+
const copy = async () => {
|
|
160
|
+
const modVersion = await claude.fs
|
|
161
|
+
.read(`${claude.plugin.root}/.claude-plugin/plugin.json`)
|
|
162
|
+
.then((text) => String(JSON.parse(text).version))
|
|
163
|
+
.catch((failure) => `unknown (${messageOf(failure)})`);
|
|
164
|
+
return ui.copy(`${name} ${modVersion}, @cmodjs/core ${coreVersion}\n${message}\n${description}${stack}`).then(() => show('Copied'), (failure) => show(`Copy failed: ${messageOf(failure)}`));
|
|
165
|
+
};
|
|
166
|
+
return show('Copy');
|
|
167
|
+
}
|
|
168
|
+
function describe(error) {
|
|
169
|
+
const messages = [];
|
|
170
|
+
for (let cause = error; cause !== undefined; cause = cause instanceof Error ? cause.cause : undefined) {
|
|
171
|
+
const text = messageOf(cause);
|
|
172
|
+
if (!messages.some((earlier) => earlier.includes(text) || text.includes(earlier)))
|
|
173
|
+
messages.push(text);
|
|
174
|
+
}
|
|
175
|
+
return messages.join('\n');
|
|
176
|
+
}
|
package/runtime/ui.d.ts
CHANGED
|
@@ -1,31 +1,20 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { Mod, ProgressStep } from '../mod.js';
|
|
1
|
+
import type { Mod } from '../mod.js';
|
|
3
2
|
import type { Claude } from './claude.js';
|
|
4
3
|
import type { Router } from './router.js';
|
|
5
|
-
|
|
6
|
-
report(step: ProgressStep): void;
|
|
7
|
-
wait(text: string): void;
|
|
8
|
-
fail(reason: string, fix: string): void;
|
|
9
|
-
end(): void;
|
|
10
|
-
};
|
|
11
|
-
export type Progress = {
|
|
12
|
-
readonly isShown: boolean;
|
|
13
|
-
start(title: string): ProgressLine;
|
|
14
|
-
draw(drawn: RenderElement, table: ElementTable, columns: number | undefined): RenderElement;
|
|
15
|
-
};
|
|
16
|
-
export declare function createProgress(claude: Claude): Progress;
|
|
4
|
+
import type { Failure, Toaster } from './toaster.js';
|
|
17
5
|
export type UiOptions<State extends object> = {
|
|
18
6
|
readonly name: string;
|
|
19
7
|
readonly claude: Claude;
|
|
20
8
|
readonly router: Router;
|
|
21
|
-
readonly
|
|
9
|
+
readonly toaster: Toaster;
|
|
10
|
+
readonly report: (failure: Failure) => void;
|
|
22
11
|
readonly announce: (feature: string) => void;
|
|
23
12
|
readonly mod: () => Mod<State>;
|
|
24
13
|
};
|
|
25
14
|
export type UiArea<State extends object> = {
|
|
26
|
-
readonly ui: Mod<State>['ui']
|
|
15
|
+
readonly ui: Omit<Mod<State>['ui'], 'notice'>;
|
|
27
16
|
changed(): void;
|
|
28
17
|
restorePanes(): Promise<void>;
|
|
29
18
|
};
|
|
30
|
-
export declare function createUi<State extends object>({ name, claude, router,
|
|
19
|
+
export declare function createUi<State extends object>({ name, claude, router, toaster, report, announce, mod }: UiOptions<State>): UiArea<State>;
|
|
31
20
|
//# sourceMappingURL=ui.d.ts.map
|
package/runtime/ui.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/runtime/ui.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/runtime/ui.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAc,GAAG,EAAc,MAAM,WAAW,CAAA;AAM5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAgFpD,MAAM,MAAM,SAAS,CAAC,KAAK,SAAS,MAAM,IAAI;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAC3C,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5C,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,KAAK,CAAC,CAAA;CAC/B,CAAA;AAED,MAAM,MAAM,MAAM,CAAC,KAAK,SAAS,MAAM,IAAI;IACzC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAA;IAC7C,OAAO,IAAI,IAAI,CAAA;IACf,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9B,CAAA;AAED,wBAAgB,QAAQ,CAAC,KAAK,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,SAAS,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CA2NxI"}
|
package/runtime/ui.js
CHANGED
|
@@ -1,92 +1,8 @@
|
|
|
1
|
-
import { ProgressBar } from '../ui/components.js';
|
|
2
1
|
import { Box, drawWith, Text } from '../ui/elements.js';
|
|
2
|
+
import { componentOf, nameOf } from '../ui/places.js';
|
|
3
3
|
import { messageOf } from '../utils/text.js';
|
|
4
|
-
const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
5
|
-
const spinnerMs = 100;
|
|
6
4
|
const bullet = '⏺';
|
|
7
|
-
const widestBar = 30;
|
|
8
|
-
const narrowestBar = 10;
|
|
9
5
|
const sizeKeys = ['columns', 'rows'];
|
|
10
|
-
export function createProgress(claude) {
|
|
11
|
-
const lines = [];
|
|
12
|
-
let frame = 0;
|
|
13
|
-
let timer;
|
|
14
|
-
const changed = () => {
|
|
15
|
-
const isSpinning = lines.some((line) => line.failure === undefined && line.waiting === undefined);
|
|
16
|
-
if (isSpinning && timer === undefined) {
|
|
17
|
-
timer = claude.clock.every(spinnerMs, () => {
|
|
18
|
-
frame += 1;
|
|
19
|
-
claude.ui.invalidate('ui.render');
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
if (!isSpinning && timer !== undefined) {
|
|
23
|
-
timer.cancel();
|
|
24
|
-
timer = undefined;
|
|
25
|
-
}
|
|
26
|
-
claude.ui.invalidate('ui.render');
|
|
27
|
-
};
|
|
28
|
-
return {
|
|
29
|
-
get isShown() {
|
|
30
|
-
return lines.length > 0;
|
|
31
|
-
},
|
|
32
|
-
start(title) {
|
|
33
|
-
const line = { title, step: undefined, waiting: undefined, failure: undefined };
|
|
34
|
-
lines.push(line);
|
|
35
|
-
changed();
|
|
36
|
-
return {
|
|
37
|
-
report(step) {
|
|
38
|
-
line.step = step;
|
|
39
|
-
line.waiting = undefined;
|
|
40
|
-
changed();
|
|
41
|
-
},
|
|
42
|
-
wait(text) {
|
|
43
|
-
line.waiting = text;
|
|
44
|
-
changed();
|
|
45
|
-
},
|
|
46
|
-
fail(reason, fix) {
|
|
47
|
-
line.failure = { reason, fix };
|
|
48
|
-
changed();
|
|
49
|
-
},
|
|
50
|
-
end() {
|
|
51
|
-
const index = lines.indexOf(line);
|
|
52
|
-
if (index >= 0)
|
|
53
|
-
lines.splice(index, 1);
|
|
54
|
-
changed();
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
},
|
|
58
|
-
draw(drawn, table, columns) {
|
|
59
|
-
return drawWith(table, () => Box({ flexDirection: 'column', children: [drawn, ...lines.map((line) => drawLine(line, frame, columns))] }));
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
function drawLine(line, frame, columns) {
|
|
64
|
-
const title = Text({ bold: true, children: line.title });
|
|
65
|
-
if (line.failure !== undefined) {
|
|
66
|
-
return Box({
|
|
67
|
-
flexDirection: 'column',
|
|
68
|
-
children: [
|
|
69
|
-
Box({ children: [Box({ minWidth: 2, children: Text({ color: 'error', children: '✗' }) }), Text({ children: [title, ' ', Text({ color: 'error', children: line.failure.reason })] })] }),
|
|
70
|
-
Box({ paddingLeft: 2, children: Text({ dimColor: true, children: line.failure.fix }) }),
|
|
71
|
-
],
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
if (line.waiting !== undefined) {
|
|
75
|
-
return Text({ wrap: 'truncate-end', children: [Text({ color: 'warning', children: '◌' }), ' ', title, ' ', Text({ dimColor: true, children: line.waiting })] });
|
|
76
|
-
}
|
|
77
|
-
const glyph = Text({ color: 'claude', children: spinner[frame % spinner.length] });
|
|
78
|
-
if (line.step === undefined)
|
|
79
|
-
return Text({ wrap: 'truncate-end', children: [glyph, ' ', title, Text({ dimColor: true, children: '…' })] });
|
|
80
|
-
const { done, total, label = '' } = line.step;
|
|
81
|
-
const width = barWidth(columns, `${line.title}${done}/${total}${label}`.length);
|
|
82
|
-
return Text({ wrap: 'truncate-end', children: [glyph, ' ', title, ' ', ProgressBar({ done, total, width }), ...(label === '' ? [] : [' ', label])] });
|
|
83
|
-
}
|
|
84
|
-
function barWidth(columns, textLength) {
|
|
85
|
-
if (columns === undefined)
|
|
86
|
-
return widestBar;
|
|
87
|
-
const spacing = 2 + 2 + 2 + 2;
|
|
88
|
-
return Math.max(narrowestBar, Math.min(widestBar, columns - textLength - spacing));
|
|
89
|
-
}
|
|
90
6
|
function paneSize(pane, state) {
|
|
91
7
|
const size = {};
|
|
92
8
|
for (const key of sizeKeys) {
|
|
@@ -149,7 +65,7 @@ function drawPieces(pieces, place) {
|
|
|
149
65
|
return Box({ children: [Box({ minWidth: 2, children: Text({ color: 'text', children: bullet }) }), column(spaced)] });
|
|
150
66
|
return column([opening, Box({ paddingLeft: 2, children: column(rest) })]);
|
|
151
67
|
}
|
|
152
|
-
export function createUi({ name, claude, router,
|
|
68
|
+
export function createUi({ name, claude, router, toaster, report, announce, mod }) {
|
|
153
69
|
const panes = new Map();
|
|
154
70
|
const paneLogs = new Map();
|
|
155
71
|
const openPanes = new Set();
|
|
@@ -163,7 +79,7 @@ export function createUi({ name, claude, router, progress, announce, mod }) {
|
|
|
163
79
|
let hasLogged = false;
|
|
164
80
|
return (failure) => {
|
|
165
81
|
if (!hasLogged)
|
|
166
|
-
|
|
82
|
+
report({ id: `render:${what}`, message: `The ${what} render failed`, description: failure });
|
|
167
83
|
hasLogged = true;
|
|
168
84
|
};
|
|
169
85
|
};
|
|
@@ -192,10 +108,10 @@ export function createUi({ name, claude, router, progress, announce, mod }) {
|
|
|
192
108
|
return drawPieces(drawings.map((drawing) => ({ drawing, isClaudesRow: false })), 'pane');
|
|
193
109
|
};
|
|
194
110
|
};
|
|
195
|
-
const routeMarkdown = (
|
|
111
|
+
const routeMarkdown = (createReader) => {
|
|
196
112
|
if (read !== undefined)
|
|
197
113
|
return;
|
|
198
|
-
const reader =
|
|
114
|
+
const reader = createReader();
|
|
199
115
|
read = reader;
|
|
200
116
|
router.add('ui.render', async (e, next) => {
|
|
201
117
|
if (e.component !== 'AssistantMessage')
|
|
@@ -275,7 +191,7 @@ export function createUi({ name, claude, router, progress, announce, mod }) {
|
|
|
275
191
|
await run();
|
|
276
192
|
}
|
|
277
193
|
catch (error) {
|
|
278
|
-
|
|
194
|
+
report({ id: `pane:${pane.id}:${handler}`, message: `The ${pane.title} pane's ${handler} threw`, error });
|
|
279
195
|
}
|
|
280
196
|
};
|
|
281
197
|
return {
|
|
@@ -304,22 +220,31 @@ export function createUi({ name, claude, router, progress, announce, mod }) {
|
|
|
304
220
|
};
|
|
305
221
|
return handle;
|
|
306
222
|
},
|
|
307
|
-
render(
|
|
308
|
-
const
|
|
309
|
-
const
|
|
223
|
+
render(place, Component) {
|
|
224
|
+
const target = place;
|
|
225
|
+
const markdown = typeof target === 'string' ? undefined : target;
|
|
226
|
+
const component = componentOf(place);
|
|
227
|
+
if (markdown === undefined && component === undefined)
|
|
228
|
+
throw new Error(`${name}: ${String(place)} is not a place render draws, such as ToolCall or CodeBlock.`);
|
|
229
|
+
const feature = `a render of ${nameOf(place)}`;
|
|
310
230
|
if (renders.has(feature))
|
|
311
|
-
throw new Error(`${name}: ${feature} is already added. Render each
|
|
231
|
+
throw new Error(`${name}: ${feature} is already added. Render each place once.`);
|
|
312
232
|
renders.add(feature);
|
|
313
233
|
announce(feature);
|
|
314
|
-
if (
|
|
315
|
-
const
|
|
316
|
-
markdownRenders.set(
|
|
317
|
-
routeMarkdown(
|
|
318
|
-
return
|
|
234
|
+
if (markdown !== undefined) {
|
|
235
|
+
const markdownType = markdown.markdown;
|
|
236
|
+
markdownRenders.set(markdownType, { Component: Component, log: logOnce(nameOf(place)) });
|
|
237
|
+
routeMarkdown(markdown.createReader);
|
|
238
|
+
return {
|
|
239
|
+
dispose() {
|
|
240
|
+
markdownRenders.delete(markdownType);
|
|
241
|
+
renders.delete(feature);
|
|
242
|
+
claude.ui.invalidate('ui.render');
|
|
243
|
+
},
|
|
244
|
+
};
|
|
319
245
|
}
|
|
320
|
-
const
|
|
321
|
-
const
|
|
322
|
-
router.add('ui.render', async (e, next) => {
|
|
246
|
+
const log = logOnce(nameOf(place));
|
|
247
|
+
const drawing = router.add('ui.render', async (e, next) => {
|
|
323
248
|
if (e.component !== component)
|
|
324
249
|
return next(e);
|
|
325
250
|
const table = claude.ui.resolve(e);
|
|
@@ -332,17 +257,16 @@ export function createUi({ name, claude, router, progress, announce, mod }) {
|
|
|
332
257
|
return drawn.drawing;
|
|
333
258
|
return drawWith(table, () => drawPieces([drawn], e.props.isFirstOfReply ? 'opensReply' : 'inReply'));
|
|
334
259
|
});
|
|
260
|
+
return {
|
|
261
|
+
dispose() {
|
|
262
|
+
drawing.dispose();
|
|
263
|
+
renders.delete(feature);
|
|
264
|
+
claude.ui.invalidate('ui.render');
|
|
265
|
+
},
|
|
266
|
+
};
|
|
335
267
|
},
|
|
336
|
-
toast:
|
|
337
|
-
|
|
338
|
-
const line = progress.start(title);
|
|
339
|
-
try {
|
|
340
|
-
return await task((step) => line.report(step));
|
|
341
|
-
}
|
|
342
|
-
finally {
|
|
343
|
-
line.end();
|
|
344
|
-
}
|
|
345
|
-
},
|
|
268
|
+
toast: toaster.toast,
|
|
269
|
+
copy: toaster.copy,
|
|
346
270
|
ask: (question, options) => claude.ui.ask(question, options),
|
|
347
271
|
scroll: (args) => claude.ui.scroll(args),
|
|
348
272
|
},
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Turn } from './turn/turn.js';
|
|
2
|
+
export declare const ContextStart = "contextStart";
|
|
3
|
+
export declare const ContextEnd = "contextEnd";
|
|
4
|
+
export type Context = {
|
|
5
|
+
readonly number: number;
|
|
6
|
+
readonly openedAt: string;
|
|
7
|
+
readonly closedAt?: string;
|
|
8
|
+
turns(): Promise<Turn[]>;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/session/context/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAA;AAE1C,eAAO,MAAM,YAAY,iBAAiB,CAAA;AAE1C,eAAO,MAAM,UAAU,eAAe,CAAA;AAEtC,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,KAAK,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAAA;CACzB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../../../../src/session/context/turn/message/document.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C,eAAO,MAAM,QAAQ,aAAa,CAAA;AAElC,MAAM,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,QAAQ,CAAC,GAAG;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const Document = 'document';
|