@cmodjs/core 0.3.0 → 1.5.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 +61 -10
- package/docs/dependencies.md +16 -14
- package/docs/hooks.md +64 -29
- package/docs/index.md +29 -25
- package/docs/install-steps.md +13 -9
- package/docs/jobs.md +59 -111
- package/docs/mod.md +227 -175
- package/docs/options.md +2 -2
- package/docs/permissions.md +27 -16
- package/docs/session.md +311 -0
- package/docs/state.md +11 -7
- package/docs/testing.md +68 -23
- package/docs/ui.md +116 -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 +14 -4
- 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 +178 -75
- package/mod.d.ts.map +1 -1
- package/mod.js +127 -3
- package/options.d.ts +2 -2
- package/options.js +19 -19
- package/package.json +3 -3
- package/records.d.ts +13 -8
- package/records.d.ts.map +1 -1
- package/records.js +33 -17
- package/register.d.ts.map +1 -1
- package/register.js +34 -5
- package/runtime/announce.d.ts +18 -0
- package/runtime/announce.d.ts.map +1 -0
- package/runtime/announce.js +68 -0
- package/runtime/before.d.ts +32 -0
- package/runtime/before.d.ts.map +1 -0
- package/runtime/before.js +214 -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 +4 -2
- package/runtime/claude.d.ts.map +1 -1
- package/runtime/conversations.d.ts +17 -0
- package/runtime/conversations.d.ts.map +1 -0
- package/runtime/conversations.js +563 -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 +10 -1
- package/runtime/engine.d.ts +13 -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 +68 -31
- package/runtime/hooks.d.ts +5 -4
- package/runtime/hooks.d.ts.map +1 -1
- package/runtime/hooks.js +29 -9
- package/runtime/installer.d.ts +1 -1
- package/runtime/installer.d.ts.map +1 -1
- package/runtime/installer.js +17 -14
- package/runtime/lifecycle.d.ts +3 -1
- package/runtime/lifecycle.d.ts.map +1 -1
- package/runtime/lifecycle.js +414 -212
- package/runtime/metadata.d.ts +3 -1
- package/runtime/metadata.d.ts.map +1 -1
- package/runtime/metadata.js +25 -14
- 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 +58 -0
- package/runtime/sessions.d.ts.map +1 -0
- package/runtime/sessions.js +433 -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 +24 -0
- package/runtime/toaster.d.ts.map +1 -0
- package/runtime/toaster.js +286 -0
- package/runtime/ui.d.ts +6 -17
- package/runtime/ui.d.ts.map +1 -1
- package/runtime/ui.js +62 -118
- package/session/context/context.d.ts +12 -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 +55 -0
- package/session/context/turn/message/message.d.ts.map +1 -0
- package/session/context/turn/message/message.js +178 -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 +13 -0
- package/session/context/turn/turn.d.ts.map +1 -0
- package/session/context/turn/turn.js +2 -0
- package/session/conversation.d.ts +73 -0
- package/session/conversation.d.ts.map +1 -0
- package/session/conversation.js +146 -0
- package/session/goal/goal.d.ts +11 -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 +158 -0
- package/session/session.d.ts.map +1 -0
- package/session/session.js +2 -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 +13 -4
- 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 +252 -97
- package/src/options.ts +22 -22
- package/src/records.ts +40 -23
- package/src/register.ts +34 -5
- package/src/runtime/announce.ts +76 -0
- package/src/runtime/before.ts +200 -0
- package/src/runtime/bound.ts +12 -0
- package/src/runtime/claude.ts +7 -2
- package/src/runtime/conversations.ts +579 -0
- package/src/runtime/deadline.ts +2 -2
- package/src/runtime/dependencies.ts +13 -1
- package/src/runtime/engine.ts +23 -0
- package/src/runtime/grants.ts +69 -29
- package/src/runtime/hooks.ts +39 -14
- package/src/runtime/installer.ts +16 -14
- package/src/runtime/lifecycle.ts +413 -201
- package/src/runtime/metadata.ts +37 -14
- 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 +447 -0
- package/src/runtime/state.ts +50 -14
- package/src/runtime/toaster.ts +334 -0
- package/src/runtime/ui.ts +65 -143
- package/src/session/context/context.ts +14 -0
- package/src/session/context/turn/message/document.ts +8 -0
- package/src/session/context/turn/message/message.ts +203 -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 +15 -0
- package/src/session/conversation.ts +194 -0
- package/src/session/goal/goal.ts +10 -0
- package/src/session/goal/task.ts +19 -0
- package/src/session/session.ts +141 -0
- package/src/testing/fake-claude.ts +25 -17
- package/src/testing/fake-elements.ts +3 -2
- package/src/testing/fake-files.ts +1 -1
- package/src/testing/fake-sessions.ts +277 -0
- package/src/testing.ts +138 -35
- 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 +5 -5
- package/src/utils/changelog.ts +32 -0
- package/src/utils/disposable.ts +3 -0
- package/src/utils/matcher.ts +37 -0
- package/src/utils/metadata.ts +17 -15
- package/src/utils/parse-shell.ts +9 -8
- package/src/utils/paths.ts +21 -4
- package/src/utils/text.ts +4 -0
- package/testing/fake-claude.d.ts +5 -1
- package/testing/fake-claude.d.ts.map +1 -1
- package/testing/fake-claude.js +21 -12
- package/testing/fake-elements.js +4 -2
- package/testing/fake-files.js +1 -1
- package/testing/fake-sessions.d.ts +81 -0
- package/testing/fake-sessions.d.ts.map +1 -0
- package/testing/fake-sessions.js +249 -0
- package/testing.d.ts +34 -13
- package/testing.d.ts.map +1 -1
- package/testing.js +115 -23
- 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 +2 -1
- package/utils/call-effects.d.ts.map +1 -1
- package/utils/call-effects.js +3 -3
- package/utils/changelog.d.ts +9 -0
- package/utils/changelog.d.ts.map +1 -0
- package/utils/changelog.js +29 -0
- package/utils/disposable.d.ts +4 -0
- package/utils/disposable.d.ts.map +1 -0
- package/utils/disposable.js +1 -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.js +9 -7
- package/utils/paths.d.ts +4 -1
- package/utils/paths.d.ts.map +1 -1
- package/utils/paths.js +19 -4
- 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
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { Box, Button, drawWith, Link, Text } from '../ui/elements.js';
|
|
2
|
+
import { coreVersion, isObject } from '../records.js';
|
|
3
|
+
import { firstLine, messageOf } from '../utils/text.js';
|
|
4
|
+
const githubRepository = /^https:\/\/github\.com\/([^/]+\/[^/#?]+?)(?:\.git)?(?:\/issues)?\/?$/;
|
|
5
|
+
const linkLimit = 2048;
|
|
6
|
+
const hiddenKey = 'cmod:hidden-failures';
|
|
7
|
+
const defaultDuration = 4000;
|
|
8
|
+
const visibleToasts = 3;
|
|
9
|
+
const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
10
|
+
const spinnerMs = 100;
|
|
11
|
+
const icons = {
|
|
12
|
+
default: { glyph: ' ' },
|
|
13
|
+
success: { glyph: '✓', color: 'success' },
|
|
14
|
+
info: { glyph: 'ℹ', color: 'suggestion' },
|
|
15
|
+
warning: { glyph: '⚠', color: 'warning' },
|
|
16
|
+
error: { glyph: '✗', color: 'error' },
|
|
17
|
+
};
|
|
18
|
+
export function createToaster(claude, name, version) {
|
|
19
|
+
const entries = new Map();
|
|
20
|
+
const hidden = new Map();
|
|
21
|
+
void claude.store.get(hiddenKey).then((saved) => {
|
|
22
|
+
if (typeof saved !== 'object' || saved === null)
|
|
23
|
+
return;
|
|
24
|
+
for (const [id, hides] of Object.entries(saved))
|
|
25
|
+
if (typeof hides === 'string' && !hidden.has(id))
|
|
26
|
+
hidden.set(id, hides);
|
|
27
|
+
}, (error) => claude.ui.log(`${name}: could not read the failures you hid: ${messageOf(error)}`, { to: 'debug' }));
|
|
28
|
+
let lastId = 0;
|
|
29
|
+
let frame = 0;
|
|
30
|
+
let spinning;
|
|
31
|
+
let isHeld = false;
|
|
32
|
+
let showsAll = false;
|
|
33
|
+
let bandId;
|
|
34
|
+
const changed = () => {
|
|
35
|
+
const isLoading = [...entries.values()].some((entry) => entry.type === 'loading');
|
|
36
|
+
if (isLoading && spinning === undefined) {
|
|
37
|
+
spinning = claude.clock.every(spinnerMs, () => {
|
|
38
|
+
frame += 1;
|
|
39
|
+
claude.ui.invalidate('ui.render');
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (!isLoading && spinning !== undefined) {
|
|
43
|
+
spinning.cancel();
|
|
44
|
+
spinning = undefined;
|
|
45
|
+
}
|
|
46
|
+
if (entries.size <= visibleToasts)
|
|
47
|
+
showsAll = false;
|
|
48
|
+
claude.ui.invalidate('ui.render');
|
|
49
|
+
};
|
|
50
|
+
const disarm = (entry) => {
|
|
51
|
+
entry.timer?.cancel();
|
|
52
|
+
entry.timer = undefined;
|
|
53
|
+
};
|
|
54
|
+
const arm = (entry) => {
|
|
55
|
+
const duration = entry.options.duration ?? defaultDuration;
|
|
56
|
+
if (entry.timer !== undefined || entry.type === 'loading' || entry.isOpen || !Number.isFinite(duration))
|
|
57
|
+
return;
|
|
58
|
+
entry.timer = claude.clock.after(duration, () => {
|
|
59
|
+
entry.timer = undefined;
|
|
60
|
+
if (isHeld)
|
|
61
|
+
arm(entry);
|
|
62
|
+
else
|
|
63
|
+
dismiss(entry.id);
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
const dismiss = (id) => {
|
|
67
|
+
for (const entry of entries.values()) {
|
|
68
|
+
if (id !== undefined && entry.id !== id)
|
|
69
|
+
continue;
|
|
70
|
+
disarm(entry);
|
|
71
|
+
entries.delete(entry.id);
|
|
72
|
+
}
|
|
73
|
+
changed();
|
|
74
|
+
};
|
|
75
|
+
const put = (type, message, options, actions, hides) => {
|
|
76
|
+
const id = options.id ?? (lastId += 1);
|
|
77
|
+
const previous = entries.get(id);
|
|
78
|
+
if (previous !== undefined)
|
|
79
|
+
disarm(previous);
|
|
80
|
+
const reporting = hides !== undefined && previous?.hides === hides ? previous.reporting : undefined;
|
|
81
|
+
const entry = { id, type, message, options, actions, ...(hides === undefined ? {} : { hides }), isOpen: previous?.isOpen ?? false, reporting, timer: undefined };
|
|
82
|
+
if (type === 'error' && previous?.message !== message)
|
|
83
|
+
claude.ui.log(`${name}: ${message}${typeof options.description === 'string' ? `: ${options.description}` : ''}`, { to: 'debug' });
|
|
84
|
+
entries.set(id, entry);
|
|
85
|
+
changed();
|
|
86
|
+
return id;
|
|
87
|
+
};
|
|
88
|
+
const show = (type) => (message, options = {}) => put(type, message, options, options.action === undefined ? [] : [options.action]);
|
|
89
|
+
const hide = (entry) => {
|
|
90
|
+
dismiss(entry.id);
|
|
91
|
+
if (entry.hides === undefined)
|
|
92
|
+
return;
|
|
93
|
+
hidden.set(String(entry.id), entry.hides);
|
|
94
|
+
void claude.store.set(hiddenKey, Object.fromEntries(hidden)).catch((error) => claude.ui.log(`${name}: could not remember the failure you hid: ${messageOf(error)}`, { to: 'debug' }));
|
|
95
|
+
};
|
|
96
|
+
const toast = Object.assign(show('default'), {
|
|
97
|
+
success: show('success'),
|
|
98
|
+
info: show('info'),
|
|
99
|
+
warning: show('warning'),
|
|
100
|
+
error: show('error'),
|
|
101
|
+
loading: show('loading'),
|
|
102
|
+
dismiss,
|
|
103
|
+
promise(task, messages) {
|
|
104
|
+
const id = show('loading')(messages.loading);
|
|
105
|
+
const settled = Promise.resolve()
|
|
106
|
+
.then(() => (typeof task === 'function' ? task() : task))
|
|
107
|
+
.then((value) => {
|
|
108
|
+
show('success')(typeof messages.success === 'function' ? messages.success(value) : messages.success, { id });
|
|
109
|
+
return value;
|
|
110
|
+
}, (error) => {
|
|
111
|
+
show('error')(typeof messages.error === 'function' ? messages.error(error) : messages.error, { id });
|
|
112
|
+
throw error;
|
|
113
|
+
});
|
|
114
|
+
settled.catch(() => undefined);
|
|
115
|
+
return { unwrap: () => settled };
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
const copy = async (text) => {
|
|
119
|
+
const copied = await claude.ui.copy({ text });
|
|
120
|
+
if (!copied.isCopied)
|
|
121
|
+
throw new Error(copied.reason);
|
|
122
|
+
};
|
|
123
|
+
const press = (entry, run) => () => void Promise.resolve()
|
|
124
|
+
.then(run)
|
|
125
|
+
.catch((error) => fail({ id: `press:${entry.id}`, message: `${entry.message}: the press failed`, error }));
|
|
126
|
+
const fail = ({ id, message, error, description = error === undefined ? undefined : describe(error), retry }) => {
|
|
127
|
+
const hides = `${version() ?? ''}\n${message}\n${error === undefined ? '' : describe(error)}`;
|
|
128
|
+
if (hidden.get(id) === hides)
|
|
129
|
+
return id;
|
|
130
|
+
hidden.delete(id);
|
|
131
|
+
const stack = error instanceof Error && error.stack !== undefined ? `\n\n${error.stack}` : '';
|
|
132
|
+
const details = async () => {
|
|
133
|
+
const modVersion = await claude.fs
|
|
134
|
+
.read(`${claude.plugin.root}/.claude-plugin/plugin.json`)
|
|
135
|
+
.then((text) => String(JSON.parse(text).version))
|
|
136
|
+
.catch((failure) => `unknown (${messageOf(failure)})`);
|
|
137
|
+
return `${name} ${modVersion}, @cmodjs/core ${coreVersion}\n${message}${description === undefined ? '' : `\n${description}`}${stack}`;
|
|
138
|
+
};
|
|
139
|
+
const startReporting = async () => {
|
|
140
|
+
const shown = entries.get(id);
|
|
141
|
+
if (shown === undefined)
|
|
142
|
+
return;
|
|
143
|
+
shown.reporting = { contact: await contactOf(claude), details: await details() };
|
|
144
|
+
disarm(shown);
|
|
145
|
+
changed();
|
|
146
|
+
if (bandId === undefined)
|
|
147
|
+
return;
|
|
148
|
+
const focused = await claude.ui.focus({ requestId: bandId, key: `toast:${id}:ask-claude` });
|
|
149
|
+
if ('deny' in focused)
|
|
150
|
+
claude.ui.log(`${name}: the Report choices did not take the keys: ${String(focused.deny)}`, { to: 'debug' });
|
|
151
|
+
};
|
|
152
|
+
const actions = [...(retry === undefined ? [] : [{ label: 'Retry', onClick: retry }]), { label: 'Report', onClick: startReporting }];
|
|
153
|
+
return put('error', message, { id, ...(description === undefined ? {} : { description }), duration: Infinity }, actions, hides);
|
|
154
|
+
};
|
|
155
|
+
const toggle = (entry) => () => {
|
|
156
|
+
entry.isOpen = !entry.isOpen;
|
|
157
|
+
disarm(entry);
|
|
158
|
+
changed();
|
|
159
|
+
};
|
|
160
|
+
const row = (entry) => {
|
|
161
|
+
const { description } = entry.options;
|
|
162
|
+
const isText = typeof description === 'string';
|
|
163
|
+
const icon = entry.type === 'loading' ? { glyph: spinner[frame % spinner.length], color: 'claude' } : icons[entry.type];
|
|
164
|
+
const message = isText ? Button({ plain: true, key: `toast:${entry.id}:open`, label: entry.message, onPress: toggle(entry) }) : Text({ children: entry.message });
|
|
165
|
+
const details = !isText ? description : Text({ dimColor: true, wrap: entry.isOpen ? 'wrap' : 'truncate-end', children: entry.isOpen ? description : firstLine(description) });
|
|
166
|
+
const fixed = (child) => Box({ flexShrink: 0, children: child });
|
|
167
|
+
const close = Button({ plain: true, dimColor: true, key: `toast:${entry.id}:close`, label: entry.hides === undefined ? '✕' : 'Hide', onPress: () => hide(entry) });
|
|
168
|
+
const actions = entry.actions.map((action, index) => Button({ plain: true, key: `toast:${entry.id}:${index === 0 ? 'action' : `action-${index}`}`, label: action.label, onPress: press(entry, action.onClick) }));
|
|
169
|
+
const text = details === undefined ? [message] : [message, details];
|
|
170
|
+
const under = entry.reporting === undefined ? [Box({ gap: 2, flexWrap: 'wrap', children: [...actions, close] })] : reportChoices(entry, entry.reporting);
|
|
171
|
+
return Box({
|
|
172
|
+
gap: 2,
|
|
173
|
+
children: [
|
|
174
|
+
fixed(Text({ ...(icon.color === undefined ? {} : { color: icon.color }), children: icon.glyph })),
|
|
175
|
+
fixed(Text({ dimColor: true, children: name })),
|
|
176
|
+
Box({ flexDirection: 'column', flexGrow: 1, flexShrink: 1, children: entry.hides === undefined ? text : [...text, ...under] }),
|
|
177
|
+
...(entry.hides === undefined ? [...actions.map(fixed), fixed(close)] : []),
|
|
178
|
+
],
|
|
179
|
+
});
|
|
180
|
+
};
|
|
181
|
+
const reportChoices = (entry, { contact, details, status }) => {
|
|
182
|
+
const say = (said) => {
|
|
183
|
+
entry.reporting = { contact, details, status: said };
|
|
184
|
+
changed();
|
|
185
|
+
};
|
|
186
|
+
const back = () => {
|
|
187
|
+
entry.reporting = undefined;
|
|
188
|
+
changed();
|
|
189
|
+
};
|
|
190
|
+
const askClaude = async () => {
|
|
191
|
+
await claude.prompt.submit({ text: reportPrompt(name, contact, details), asUser: true });
|
|
192
|
+
back();
|
|
193
|
+
};
|
|
194
|
+
const copyError = async () => {
|
|
195
|
+
const copied = await claude.ui.copy({ text: details });
|
|
196
|
+
say(copied.isCopied ? 'Copied the error.' : `Could not copy it: ${copied.reason}`);
|
|
197
|
+
};
|
|
198
|
+
const report = contact === undefined ? [] : [Link({ href: reportLink(contact, name, entry.message, details), label: contact.kind === 'github' ? 'Report it on GitHub (opens your browser)' : 'Email the author (opens your mail app)' })];
|
|
199
|
+
return [
|
|
200
|
+
Text({ dimColor: true, children: 'Claude writes up the problem and files it with your tools. You approve each step before anything is posted.' }),
|
|
201
|
+
Box({
|
|
202
|
+
gap: 2,
|
|
203
|
+
flexWrap: 'wrap',
|
|
204
|
+
children: [
|
|
205
|
+
Button({ plain: true, key: `toast:${entry.id}:ask-claude`, label: 'Ask Claude to report it', onPress: press(entry, askClaude) }),
|
|
206
|
+
...report,
|
|
207
|
+
Button({ plain: true, key: `toast:${entry.id}:copy`, label: 'Copy the error', onPress: press(entry, copyError) }),
|
|
208
|
+
Button({ plain: true, dimColor: true, key: `toast:${entry.id}:back`, label: 'Back', onPress: back }),
|
|
209
|
+
],
|
|
210
|
+
}),
|
|
211
|
+
...(status === undefined ? [] : [Text({ dimColor: true, children: status })]),
|
|
212
|
+
];
|
|
213
|
+
};
|
|
214
|
+
return {
|
|
215
|
+
toast,
|
|
216
|
+
copy,
|
|
217
|
+
fail,
|
|
218
|
+
get shown() {
|
|
219
|
+
return [...entries.values()].map(({ type, message, options }) => ({ type, message, ...(typeof options.description === 'string' ? { description: options.description } : {}) }));
|
|
220
|
+
},
|
|
221
|
+
draw(drawn, table, band, drawnIn) {
|
|
222
|
+
bandId = drawnIn;
|
|
223
|
+
isHeld = band.hasSurvey === true;
|
|
224
|
+
if (isHeld || entries.size === 0)
|
|
225
|
+
return drawn;
|
|
226
|
+
const all = [...entries.values()];
|
|
227
|
+
const hidden = showsAll ? 0 : Math.max(all.length - visibleToasts, 0);
|
|
228
|
+
for (const entry of all.slice(hidden))
|
|
229
|
+
arm(entry);
|
|
230
|
+
const more = Button({
|
|
231
|
+
plain: true,
|
|
232
|
+
dimColor: true,
|
|
233
|
+
key: 'toasts:more',
|
|
234
|
+
label: `+${hidden} more`,
|
|
235
|
+
onPress: () => {
|
|
236
|
+
showsAll = true;
|
|
237
|
+
changed();
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
return drawWith(table, () => Box({ flexDirection: 'column', children: [drawn, ...(hidden > 0 ? [more] : []), ...all.slice(hidden).map(row)] }));
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function describe(error) {
|
|
245
|
+
const messages = [];
|
|
246
|
+
for (let cause = error; cause !== undefined; cause = cause instanceof Error ? cause.cause : undefined) {
|
|
247
|
+
const text = messageOf(cause);
|
|
248
|
+
if (!messages.some((earlier) => earlier.includes(text) || text.includes(earlier)))
|
|
249
|
+
messages.push(text);
|
|
250
|
+
}
|
|
251
|
+
return messages.join('\n');
|
|
252
|
+
}
|
|
253
|
+
async function contactOf(claude) {
|
|
254
|
+
const manifest = await claude.fs
|
|
255
|
+
.read(`${claude.plugin.root}/.claude-plugin/plugin.json`)
|
|
256
|
+
.then((text) => JSON.parse(text))
|
|
257
|
+
.catch((error) => {
|
|
258
|
+
claude.ui.log(`${claude.plugin.name}: could not read whom to report to: ${messageOf(error)}`, { to: 'debug' });
|
|
259
|
+
return {};
|
|
260
|
+
});
|
|
261
|
+
const repositoryUrl = isObject(manifest['repository']) ? manifest['repository']['url'] : manifest['repository'];
|
|
262
|
+
const repository = [manifest['supportUrl'], repositoryUrl].map((url) => (typeof url === 'string' ? githubRepository.exec(url)?.[1] : undefined)).find((found) => found !== undefined);
|
|
263
|
+
if (repository !== undefined)
|
|
264
|
+
return { kind: 'github', repository };
|
|
265
|
+
const author = manifest['author'];
|
|
266
|
+
const address = isObject(author) ? author['email'] : undefined;
|
|
267
|
+
return typeof address === 'string' && address !== '' ? { kind: 'email', address } : undefined;
|
|
268
|
+
}
|
|
269
|
+
function reportLink(contact, name, message, details) {
|
|
270
|
+
const title = `${name}: ${message}`.slice(0, 120);
|
|
271
|
+
const linkOf = (body) => contact.kind === 'github'
|
|
272
|
+
? `https://github.com/${contact.repository}/issues/new?title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}`
|
|
273
|
+
: `mailto:${contact.address}?subject=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}`;
|
|
274
|
+
let body = details;
|
|
275
|
+
while (linkOf(body).length > linkLimit && body.length > 0)
|
|
276
|
+
body = `${body.slice(0, Math.floor(body.length * 0.8))}…`;
|
|
277
|
+
return linkOf(body);
|
|
278
|
+
}
|
|
279
|
+
function reportPrompt(name, contact, details) {
|
|
280
|
+
const where = contact === undefined
|
|
281
|
+
? `${name} names no place to report it, so find its author from its plugin.json or its marketplace.`
|
|
282
|
+
: contact.kind === 'github'
|
|
283
|
+
? `Report it as an issue on https://github.com/${contact.repository}.`
|
|
284
|
+
: `Email it to ${contact.address}.`;
|
|
285
|
+
return `The ${name} mod failed. Report it to its author. ${where} First check whether it is reported already, and add to that report instead of filing a new one. Show me the report before you post it.\n\n${details}`;
|
|
286
|
+
}
|
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,CA8OxI"}
|
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,10 +65,21 @@ 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();
|
|
72
|
+
const paneStates = new Map();
|
|
73
|
+
const syncPanes = async () => {
|
|
74
|
+
for (const open of await claude.ui.panes()) {
|
|
75
|
+
if (!panes.has(open.id))
|
|
76
|
+
continue;
|
|
77
|
+
if (open.isPlaced)
|
|
78
|
+
openPanes.add(open.id);
|
|
79
|
+
paneStates.set(open.id, { isShown: open.isShown, isFocused: open.isFocused });
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const readPanes = () => void syncPanes().catch((error) => claude.ui.log(`${name} keeps its panes' shown and focused state from before: ${messageOf(error)}`, { to: 'debug' }));
|
|
156
83
|
const requestedSizes = new Map();
|
|
157
84
|
const renders = new Set();
|
|
158
85
|
const markdownRenders = new Map();
|
|
@@ -163,7 +90,7 @@ export function createUi({ name, claude, router, progress, announce, mod }) {
|
|
|
163
90
|
let hasLogged = false;
|
|
164
91
|
return (failure) => {
|
|
165
92
|
if (!hasLogged)
|
|
166
|
-
|
|
93
|
+
report({ id: `render:${what}`, message: `The ${what} render failed`, description: failure });
|
|
167
94
|
hasLogged = true;
|
|
168
95
|
};
|
|
169
96
|
};
|
|
@@ -192,10 +119,10 @@ export function createUi({ name, claude, router, progress, announce, mod }) {
|
|
|
192
119
|
return drawPieces(drawings.map((drawing) => ({ drawing, isClaudesRow: false })), 'pane');
|
|
193
120
|
};
|
|
194
121
|
};
|
|
195
|
-
const routeMarkdown = (
|
|
122
|
+
const routeMarkdown = (createReader) => {
|
|
196
123
|
if (read !== undefined)
|
|
197
124
|
return;
|
|
198
|
-
const reader =
|
|
125
|
+
const reader = createReader();
|
|
199
126
|
read = reader;
|
|
200
127
|
router.add('ui.render', async (e, next) => {
|
|
201
128
|
if (e.component !== 'AssistantMessage')
|
|
@@ -244,6 +171,8 @@ export function createUi({ name, claude, router, progress, announce, mod }) {
|
|
|
244
171
|
if (pane === undefined || e.component !== 'Pane')
|
|
245
172
|
return next(e);
|
|
246
173
|
openPanes.add(pane.id);
|
|
174
|
+
paneStates.set(pane.id, { isShown: true, isFocused: e.props.isFocused });
|
|
175
|
+
readPanes();
|
|
247
176
|
const table = claude.ui.resolve(e);
|
|
248
177
|
try {
|
|
249
178
|
return drawWith(table, () => pane.render(mod(), e.props), markdownIn(table));
|
|
@@ -263,6 +192,8 @@ export function createUi({ name, claude, router, progress, announce, mod }) {
|
|
|
263
192
|
});
|
|
264
193
|
router.add('ui.close', async (e, next) => {
|
|
265
194
|
openPanes.delete(e.id);
|
|
195
|
+
paneStates.delete(e.id);
|
|
196
|
+
readPanes();
|
|
266
197
|
const closed = await next(e);
|
|
267
198
|
const pane = panes.get(e.id);
|
|
268
199
|
if (pane?.onClose !== undefined)
|
|
@@ -275,7 +206,7 @@ export function createUi({ name, claude, router, progress, announce, mod }) {
|
|
|
275
206
|
await run();
|
|
276
207
|
}
|
|
277
208
|
catch (error) {
|
|
278
|
-
|
|
209
|
+
report({ id: `pane:${pane.id}:${handler}`, message: `The ${pane.title} pane's ${handler} threw`, error });
|
|
279
210
|
}
|
|
280
211
|
};
|
|
281
212
|
return {
|
|
@@ -291,35 +222,53 @@ export function createUi({ name, claude, router, progress, announce, mod }) {
|
|
|
291
222
|
get isOpen() {
|
|
292
223
|
return openPanes.has(pane.id);
|
|
293
224
|
},
|
|
225
|
+
get isShown() {
|
|
226
|
+
return paneStates.get(pane.id)?.isShown ?? false;
|
|
227
|
+
},
|
|
228
|
+
get isFocused() {
|
|
229
|
+
return paneStates.get(pane.id)?.isFocused ?? false;
|
|
230
|
+
},
|
|
294
231
|
async open(options) {
|
|
295
232
|
const { isPlaced } = await openAt(pane, paneSize(pane, mod().state), options?.focus);
|
|
296
233
|
if (isPlaced)
|
|
297
234
|
openPanes.add(pane.id);
|
|
235
|
+
await syncPanes();
|
|
298
236
|
},
|
|
299
237
|
async close() {
|
|
300
238
|
await claude.ui.close({ id: pane.id });
|
|
301
239
|
openPanes.delete(pane.id);
|
|
240
|
+
paneStates.delete(pane.id);
|
|
241
|
+
await syncPanes();
|
|
302
242
|
},
|
|
303
243
|
toggle: (options) => (openPanes.has(pane.id) ? handle.close() : handle.open(options)),
|
|
304
244
|
};
|
|
305
245
|
return handle;
|
|
306
246
|
},
|
|
307
|
-
render(
|
|
308
|
-
const
|
|
309
|
-
const
|
|
247
|
+
render(place, Component) {
|
|
248
|
+
const target = place;
|
|
249
|
+
const markdown = typeof target === 'string' ? undefined : target;
|
|
250
|
+
const component = componentOf(place);
|
|
251
|
+
if (markdown === undefined && component === undefined)
|
|
252
|
+
throw new Error(`${name}: ${String(place)} is not a place render draws, such as ToolCall or CodeBlock.`);
|
|
253
|
+
const feature = `a render of ${nameOf(place)}`;
|
|
310
254
|
if (renders.has(feature))
|
|
311
|
-
throw new Error(`${name}: ${feature} is already added. Render each
|
|
255
|
+
throw new Error(`${name}: ${feature} is already added. Render each place once.`);
|
|
312
256
|
renders.add(feature);
|
|
313
257
|
announce(feature);
|
|
314
|
-
if (
|
|
315
|
-
const
|
|
316
|
-
markdownRenders.set(
|
|
317
|
-
routeMarkdown(
|
|
318
|
-
return
|
|
258
|
+
if (markdown !== undefined) {
|
|
259
|
+
const markdownType = markdown.markdown;
|
|
260
|
+
markdownRenders.set(markdownType, { Component: Component, log: logOnce(nameOf(place)) });
|
|
261
|
+
routeMarkdown(markdown.createReader);
|
|
262
|
+
return {
|
|
263
|
+
dispose() {
|
|
264
|
+
markdownRenders.delete(markdownType);
|
|
265
|
+
renders.delete(feature);
|
|
266
|
+
claude.ui.invalidate('ui.render');
|
|
267
|
+
},
|
|
268
|
+
};
|
|
319
269
|
}
|
|
320
|
-
const
|
|
321
|
-
const
|
|
322
|
-
router.add('ui.render', async (e, next) => {
|
|
270
|
+
const log = logOnce(nameOf(place));
|
|
271
|
+
const drawing = router.add('ui.render', async (e, next) => {
|
|
323
272
|
if (e.component !== component)
|
|
324
273
|
return next(e);
|
|
325
274
|
const table = claude.ui.resolve(e);
|
|
@@ -332,17 +281,16 @@ export function createUi({ name, claude, router, progress, announce, mod }) {
|
|
|
332
281
|
return drawn.drawing;
|
|
333
282
|
return drawWith(table, () => drawPieces([drawn], e.props.isFirstOfReply ? 'opensReply' : 'inReply'));
|
|
334
283
|
});
|
|
284
|
+
return {
|
|
285
|
+
dispose() {
|
|
286
|
+
drawing.dispose();
|
|
287
|
+
renders.delete(feature);
|
|
288
|
+
claude.ui.invalidate('ui.render');
|
|
289
|
+
},
|
|
290
|
+
};
|
|
335
291
|
},
|
|
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
|
-
},
|
|
292
|
+
toast: toaster.toast,
|
|
293
|
+
copy: toaster.copy,
|
|
346
294
|
ask: (question, options) => claude.ui.ask(question, options),
|
|
347
295
|
scroll: (args) => claude.ui.scroll(args),
|
|
348
296
|
},
|
|
@@ -362,12 +310,8 @@ export function createUi({ name, claude, router, progress, announce, mod }) {
|
|
|
362
310
|
});
|
|
363
311
|
},
|
|
364
312
|
async restorePanes() {
|
|
365
|
-
if (panes.size
|
|
366
|
-
|
|
367
|
-
for (const open of await claude.ui.panes()) {
|
|
368
|
-
if (open.isPlaced && panes.has(open.id))
|
|
369
|
-
openPanes.add(open.id);
|
|
370
|
-
}
|
|
313
|
+
if (panes.size > 0)
|
|
314
|
+
await syncPanes();
|
|
371
315
|
},
|
|
372
316
|
};
|
|
373
317
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RecordPart } from '../session.js';
|
|
2
|
+
import type { Turn } from './turn/turn.js';
|
|
3
|
+
export declare const ContextStart = "contextStart";
|
|
4
|
+
export declare const ContextEnd = "contextEnd";
|
|
5
|
+
export type Context = RecordPart & {
|
|
6
|
+
readonly number: number;
|
|
7
|
+
readonly openedAt: string;
|
|
8
|
+
readonly closedAt?: string;
|
|
9
|
+
readonly task?: string;
|
|
10
|
+
turns(): Promise<Turn[]>;
|
|
11
|
+
};
|
|
12
|
+
//# 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,UAAU,EAAE,MAAM,eAAe,CAAA;AAC/C,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,UAAU,GAAG;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,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';
|