@cmodjs/core 4.0.2 → 4.1.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.
Files changed (141) hide show
  1. package/docs/commands.md +43 -8
  2. package/docs/index.md +3 -3
  3. package/docs/install-steps.md +6 -6
  4. package/docs/jobs.md +2 -1
  5. package/docs/mod.md +1 -1
  6. package/docs/options.md +1 -1
  7. package/docs/permissions.md +1 -1
  8. package/docs/session.md +44 -13
  9. package/docs/testing.md +14 -10
  10. package/docs/ui.md +43 -11
  11. package/installer.d.ts +3 -2
  12. package/installer.d.ts.map +1 -1
  13. package/jobs/program.d.ts +1 -0
  14. package/jobs/program.d.ts.map +1 -1
  15. package/jobs/program.js +39 -23
  16. package/mod.d.ts +3 -2
  17. package/mod.d.ts.map +1 -1
  18. package/mod.js +1 -0
  19. package/package.json +1 -1
  20. package/records.d.ts +15 -1
  21. package/records.d.ts.map +1 -1
  22. package/records.js +39 -1
  23. package/register.d.ts.map +1 -1
  24. package/register.js +5 -2
  25. package/runtime/before.d.ts +6 -2
  26. package/runtime/before.d.ts.map +1 -1
  27. package/runtime/before.js +56 -18
  28. package/runtime/buttons.d.ts +10 -0
  29. package/runtime/buttons.d.ts.map +1 -0
  30. package/runtime/buttons.js +12 -0
  31. package/runtime/conversations.d.ts.map +1 -1
  32. package/runtime/conversations.js +134 -25
  33. package/runtime/engine.d.ts +2 -0
  34. package/runtime/engine.d.ts.map +1 -1
  35. package/runtime/grants.d.ts +1 -0
  36. package/runtime/grants.d.ts.map +1 -1
  37. package/runtime/grants.js +5 -2
  38. package/runtime/hotkeys.d.ts.map +1 -1
  39. package/runtime/hotkeys.js +7 -4
  40. package/runtime/installer.d.ts +9 -6
  41. package/runtime/installer.d.ts.map +1 -1
  42. package/runtime/installer.js +104 -47
  43. package/runtime/lifecycle.d.ts.map +1 -1
  44. package/runtime/lifecycle.js +121 -40
  45. package/runtime/pages.d.ts +3 -1
  46. package/runtime/pages.d.ts.map +1 -1
  47. package/runtime/pages.js +34 -10
  48. package/runtime/parts.d.ts +1 -0
  49. package/runtime/parts.d.ts.map +1 -1
  50. package/runtime/parts.js +6 -3
  51. package/runtime/programs.d.ts +1 -0
  52. package/runtime/programs.d.ts.map +1 -1
  53. package/runtime/programs.js +4 -1
  54. package/runtime/sessions.d.ts +2 -2
  55. package/runtime/sessions.d.ts.map +1 -1
  56. package/runtime/sessions.js +88 -58
  57. package/runtime/toaster.d.ts.map +1 -1
  58. package/runtime/toaster.js +23 -9
  59. package/runtime/ui.d.ts +21 -4
  60. package/runtime/ui.d.ts.map +1 -1
  61. package/runtime/ui.js +158 -58
  62. package/runtime/watches.d.ts.map +1 -1
  63. package/runtime/watches.js +18 -4
  64. package/runtime/widgets.d.ts +7 -5
  65. package/runtime/widgets.d.ts.map +1 -1
  66. package/runtime/widgets.js +29 -20
  67. package/session/context/turn/message/attached-file.d.ts +13 -0
  68. package/session/context/turn/message/attached-file.d.ts.map +1 -0
  69. package/session/context/turn/message/attached-file.js +21 -0
  70. package/session/context/turn/message/message.d.ts +3 -2
  71. package/session/context/turn/message/message.d.ts.map +1 -1
  72. package/session/context/turn/message/message.js +20 -6
  73. package/session/conversation.d.ts +14 -3
  74. package/session/conversation.d.ts.map +1 -1
  75. package/session/conversation.js +23 -4
  76. package/session/goal/task.d.ts +5 -0
  77. package/session/goal/task.d.ts.map +1 -1
  78. package/session/session.d.ts +35 -7
  79. package/session/session.d.ts.map +1 -1
  80. package/session/session.js +2 -1
  81. package/src/installer.ts +3 -2
  82. package/src/jobs/program.ts +41 -21
  83. package/src/mod.ts +3 -2
  84. package/src/records.ts +49 -1
  85. package/src/register.ts +9 -6
  86. package/src/runtime/before.ts +61 -22
  87. package/src/runtime/buttons.ts +22 -0
  88. package/src/runtime/conversations.ts +138 -32
  89. package/src/runtime/engine.ts +2 -0
  90. package/src/runtime/grants.ts +6 -2
  91. package/src/runtime/hotkeys.ts +7 -4
  92. package/src/runtime/installer.ts +144 -82
  93. package/src/runtime/lifecycle.ts +128 -43
  94. package/src/runtime/pages.ts +45 -8
  95. package/src/runtime/parts.ts +8 -4
  96. package/src/runtime/programs.ts +5 -1
  97. package/src/runtime/sessions.ts +83 -57
  98. package/src/runtime/toaster.ts +27 -9
  99. package/src/runtime/ui.ts +163 -61
  100. package/src/runtime/watches.ts +20 -5
  101. package/src/runtime/widgets.ts +34 -22
  102. package/src/session/context/turn/message/attached-file.ts +36 -0
  103. package/src/session/context/turn/message/message.ts +20 -7
  104. package/src/session/conversation.ts +30 -6
  105. package/src/session/goal/task.ts +6 -0
  106. package/src/session/session.ts +17 -7
  107. package/src/testing/fake-claude.ts +21 -6
  108. package/src/testing/fake-elements.ts +51 -8
  109. package/src/testing/fake-sessions.ts +60 -23
  110. package/src/testing.ts +86 -28
  111. package/src/ui/areas.ts +1 -0
  112. package/src/ui/components.ts +14 -8
  113. package/src/ui/elements.ts +6 -2
  114. package/src/ui/markdown.ts +3 -3
  115. package/src/ui/panes.ts +2 -2
  116. package/src/ui/styles.ts +6 -2
  117. package/testing/fake-claude.d.ts +3 -0
  118. package/testing/fake-claude.d.ts.map +1 -1
  119. package/testing/fake-claude.js +18 -5
  120. package/testing/fake-elements.d.ts.map +1 -1
  121. package/testing/fake-elements.js +46 -7
  122. package/testing/fake-sessions.d.ts +21 -10
  123. package/testing/fake-sessions.d.ts.map +1 -1
  124. package/testing/fake-sessions.js +58 -20
  125. package/testing.d.ts +8 -12
  126. package/testing.d.ts.map +1 -1
  127. package/testing.js +82 -21
  128. package/ui/areas.d.ts +4 -0
  129. package/ui/areas.d.ts.map +1 -1
  130. package/ui/components.d.ts +1 -0
  131. package/ui/components.d.ts.map +1 -1
  132. package/ui/components.js +11 -7
  133. package/ui/elements.d.ts +3 -0
  134. package/ui/elements.d.ts.map +1 -1
  135. package/ui/elements.js +6 -1
  136. package/ui/markdown.d.ts +2 -2
  137. package/ui/markdown.d.ts.map +1 -1
  138. package/ui/panes.d.ts +2 -1
  139. package/ui/panes.d.ts.map +1 -1
  140. package/ui/styles.d.ts +11 -2
  141. package/ui/styles.d.ts.map +1 -1
package/runtime/ui.d.ts CHANGED
@@ -1,6 +1,8 @@
1
+ import type { Args, ElementTable, Frozen, MarkdownProps, RenderElement } from 'claude-code';
1
2
  import type { Mod, Turn } from '../mod.js';
2
- import { type Area } from '../ui/areas.js';
3
+ import { type Area, type WidgetInit } from '../ui/areas.js';
3
4
  import { type ScrollOptions, type ScrollTarget } from '../ui/panes.js';
5
+ import { type StyledAt } from '../ui/styles.js';
4
6
  import type { Claude } from './claude.js';
5
7
  import type { Router } from './router.js';
6
8
  import type { Failure } from './toaster.js';
@@ -10,12 +12,14 @@ export declare const partMethods: {
10
12
  readonly press: 'cmod:press';
11
13
  readonly shown: 'cmod:shown';
12
14
  readonly changed: 'cmod:partsChanged';
15
+ readonly stylers: 'cmod:markdownStylers';
13
16
  };
14
17
  export type PagePlace = 'sidebar' | 'settings';
15
18
  export type WidgetDeclaration = {
16
19
  readonly id: string;
17
20
  readonly title: string;
18
21
  readonly areas: readonly Area[];
22
+ readonly place?: NonNullable<WidgetInit['place']>;
19
23
  };
20
24
  export type PageDeclaration = {
21
25
  readonly id: string;
@@ -25,6 +29,7 @@ export type PageDeclaration = {
25
29
  export type PartList = {
26
30
  readonly widgets: readonly WidgetDeclaration[];
27
31
  readonly pages: readonly PageDeclaration[];
32
+ readonly stylesMarkdown?: true;
28
33
  };
29
34
  export type PartDraw = {
30
35
  readonly widget: string;
@@ -36,7 +41,14 @@ export type PartDraw = {
36
41
  readonly params: unknown;
37
42
  readonly columns: number;
38
43
  readonly isFocused: boolean;
39
- };
44
+ } | ({
45
+ readonly markdown: string;
46
+ } & StyledAt);
47
+ export type BorrowedMarkdown = (mod: string, cached: string, input: {
48
+ readonly markdown: string;
49
+ } & StyledAt) => RenderElement | undefined;
50
+ export declare function viewportColumns(e: Frozen<Args<'ui.render'>>): number;
51
+ export declare function styledAt(e: Frozen<Args<'ui.render'>>, columns?: number): StyledAt;
40
52
  export type PartPress = {
41
53
  readonly draw: number;
42
54
  readonly handler: number;
@@ -75,6 +87,7 @@ export type ShownPages = {
75
87
  readonly shown: string | null;
76
88
  readonly isFocused: boolean;
77
89
  };
90
+ export declare function merged(updates: readonly PartsUpdate[]): PartsUpdate;
78
91
  export type UiOptions<State extends object> = {
79
92
  readonly name: string;
80
93
  readonly claude: Claude;
@@ -82,6 +95,8 @@ export type UiOptions<State extends object> = {
82
95
  readonly report: (failure: Failure) => void;
83
96
  readonly announce: (feature: string) => void;
84
97
  readonly mod: () => Mod<State>;
98
+ readonly borrowed: BorrowedMarkdown;
99
+ readonly nextDraw: () => number;
85
100
  };
86
101
  export type UiArea<State extends object> = {
87
102
  readonly panes: Mod<State>['panes'];
@@ -91,12 +106,14 @@ export type UiArea<State extends object> = {
91
106
  readonly styles: Mod<State>['styles'];
92
107
  asked<Value>(run: () => Value | Promise<Value>): Promise<Value>;
93
108
  answerInput<Answer>(run: () => Promise<Answer>, passOn: () => Promise<unknown>): Promise<Answer>;
94
- answer(method: string, input: unknown): Promise<{
109
+ answer(method: string, input: unknown, caller: string): Promise<{
95
110
  readonly value: unknown;
96
111
  }> | undefined;
112
+ markdownIn(table: ElementTable, at: StyledAt): ((props: MarkdownProps) => RenderElement) | undefined;
113
+ restyled(stylers: readonly string[]): void;
97
114
  started(): void;
98
115
  changed(): void;
99
116
  restorePanes(): Promise<void>;
100
117
  };
101
- export declare function createUi<State extends object>({ name, claude, router, report, announce, mod }: UiOptions<State>): UiArea<State>;
118
+ export declare function createUi<State extends object>({ name, claude, router, report, announce, mod, borrowed, nextDraw }: UiOptions<State>): UiArea<State>;
102
119
  //# sourceMappingURL=ui.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/runtime/ui.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/runtime/ui.ts"],"names":[],"mappings":"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"}
package/runtime/ui.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { isObject } from '../records.js';
2
2
  import { TurnEnd } from '../ui/areas.js';
3
- import { Box, drawWith, elementData, Text } from '../ui/elements.js';
3
+ import { Box, drawWith, elementData, Markdown, Text } from '../ui/elements.js';
4
4
  import { checkPane, checkPart, sizeKeys } from '../ui/panes.js';
5
5
  import { componentOf, nameOf } from '../ui/styles.js';
6
6
  import { messageOf } from '../utils/text.js';
@@ -11,7 +11,15 @@ export const partMethods = {
11
11
  press: 'cmod:press',
12
12
  shown: 'cmod:shown',
13
13
  changed: 'cmod:partsChanged',
14
+ stylers: 'cmod:markdownStylers',
14
15
  };
16
+ const unmeasuredColumns = 80;
17
+ export function viewportColumns(e) {
18
+ return e.viewport?.columns ?? unmeasuredColumns;
19
+ }
20
+ export function styledAt(e, columns = viewportColumns(e)) {
21
+ return { surface: e.surface, columns };
22
+ }
15
23
  const bullet = '⏺';
16
24
  function paneSize(pane, state) {
17
25
  const size = {};
@@ -75,32 +83,39 @@ function drawPieces(pieces, place) {
75
83
  return Box({ children: [Box({ minWidth: 2, children: Text({ color: 'text', children: bullet }) }), column(spaced)] });
76
84
  return column([opening, Box({ paddingLeft: 2, children: column(rest) })]);
77
85
  }
78
- function merged(updates) {
86
+ export function merged(updates) {
79
87
  const parts = updates.findLast((update) => update.parts !== undefined)?.parts;
80
88
  const requests = updates.flatMap((update) => update.requests ?? []);
81
89
  const isRedrawn = updates.some((update) => update.redraw === true);
82
90
  return { ...(parts === undefined ? {} : { parts }), ...(isRedrawn ? { redraw: true } : {}), ...(requests.length === 0 ? {} : { requests }) };
83
91
  }
84
92
  const keptDrawInputs = 200;
85
- export function createUi({ name, claude, router, report, announce, mod }) {
93
+ export function createUi({ name, claude, router, report, announce, mod, borrowed, nextDraw }) {
86
94
  const panes = new Map();
87
95
  const paneLogs = new Map();
88
96
  const openPanes = new Set();
89
97
  const paneStates = new Map();
90
98
  const syncPanes = async () => {
91
- for (const open of await claude.ui.panes()) {
92
- if (!panes.has(open.id))
93
- continue;
94
- if (open.isPlaced)
95
- openPanes.add(open.id);
96
- paneStates.set(open.id, { isShown: open.isShown, isFocused: open.isFocused });
99
+ const listed = new Map((await claude.ui.panes()).map((open) => [open.id, open]));
100
+ for (const id of panes.keys()) {
101
+ const open = listed.get(id);
102
+ if (open?.isPlaced === true)
103
+ openPanes.add(id);
104
+ else
105
+ openPanes.delete(id);
106
+ if (open === undefined)
107
+ paneStates.delete(id);
108
+ else
109
+ paneStates.set(id, { isShown: open.isShown, isFocused: open.isFocused });
97
110
  }
98
111
  };
99
- const readPanes = () => void syncPanes().catch((error) => claude.ui.log(`${name} keeps its panes' shown and focused state from before: ${messageOf(error)}`, { to: 'debug' }));
112
+ const readPanes = () => syncPanes().catch((error) => claude.ui.log(`${name} keeps its panes' open, shown, and focused state from before: ${messageOf(error)}`, { to: 'debug' }));
100
113
  const requestedSizes = new Map();
101
114
  const styles = new Set();
102
115
  const markdownRenders = new Map();
103
116
  let read;
117
+ let stylers = [];
118
+ let stylersSeen = 0;
104
119
  let isRouted = false;
105
120
  let isResizeQueued = false;
106
121
  const widgets = new Map();
@@ -110,17 +125,54 @@ export function createUi({ name, claude, router, report, announce, mod }) {
110
125
  const draws = new Map();
111
126
  const latestDraws = new Map();
112
127
  const failedDraws = new Set();
113
- let drawCount = 0;
114
128
  let beforeStart = [];
115
129
  let askedDepth = 0;
130
+ const askedCalls = new Set();
116
131
  let unheldRequests = 0;
117
132
  let collected;
118
133
  let collecting = 0;
119
134
  let queued;
120
135
  const partList = () => ({
121
- widgets: [...widgets.values()].map(({ id, title, areas: listed }) => ({ id, title, areas: listed })),
136
+ widgets: [...widgets.values()].map(({ id, title, areas: listed, place }) => ({ id, title, areas: listed, ...(place === undefined ? {} : { place }) })),
122
137
  pages: [...pages.values()].map(({ init: { id, title }, place }) => ({ id, title, place })),
138
+ ...(markdownRenders.size > 0 ? { stylesMarkdown: true } : {}),
123
139
  });
140
+ const hear = (listed, isRestyled) => {
141
+ if (!Array.isArray(listed))
142
+ return;
143
+ const heard = listed.filter((styler) => typeof styler === 'string' && styler !== name);
144
+ if (!isRestyled && heard.join('\0') === stylers.join('\0'))
145
+ return;
146
+ stylers = heard;
147
+ stylersSeen += 1;
148
+ claude.ui.invalidate('ui.render');
149
+ };
150
+ const treeOf = (render, handlers) => {
151
+ const drawn = render();
152
+ return drawn === undefined ? null : elementData(drawn, handlers);
153
+ };
154
+ const isAnyDrawingChanged = () => {
155
+ for (const [key, inputs] of latestDraws) {
156
+ if (key === 'markdown')
157
+ continue;
158
+ for (const sent of inputs.values()) {
159
+ const handlers = [];
160
+ try {
161
+ if (JSON.stringify(treeOf(sent.render, handlers)) !== sent.tree)
162
+ return true;
163
+ }
164
+ catch (error) {
165
+ claude.ui.log(`${name}: the ${key} drawing threw, so the cmod plugin asks for it again: ${messageOf(error)}`, { to: 'debug' });
166
+ return true;
167
+ }
168
+ const latest = sent.draws.at(-1);
169
+ if (latest !== undefined && draws.has(latest))
170
+ draws.set(latest, { id: key, handlers });
171
+ }
172
+ }
173
+ return false;
174
+ };
175
+ const withNeededRedraw = ({ redraw, ...update }) => (redraw === true && isAnyDrawingChanged() ? { ...update, redraw } : update);
124
176
  const send = (update) => {
125
177
  if (collected !== undefined)
126
178
  return void collected.push(update);
@@ -132,21 +184,33 @@ export function createUi({ name, claude, router, report, announce, mod }) {
132
184
  const batch = [update];
133
185
  if (!isAsked)
134
186
  queued = batch;
135
- const tell = () => {
187
+ const tell = async () => {
136
188
  if (queued === batch)
137
189
  queued = undefined;
138
- const input = { ...merged(batch), ...(isAsked ? { isAsked: true } : {}) };
190
+ const needed = withNeededRedraw(merged(batch));
191
+ if (Object.keys(needed).length === 0)
192
+ return;
193
+ const input = { ...needed, ...(isAsked ? { isAsked: true } : {}) };
139
194
  return claude.cmod.call({ to: cmodPluginName, method: partMethods.changed, input }).then((answer) => {
140
195
  if (isAsked && input.requests !== undefined && !(isObject(answer) && answer['isHeld'] === true))
141
196
  unheldRequests += 1;
197
+ if (isObject(answer) && name !== cmodPluginName)
198
+ hear(answer['stylers'], false);
142
199
  }, (error) => claude.ui.log(`${name}: the cmod plugin did not hear about its pages and widgets: ${messageOf(error)}`, { to: 'debug' }));
143
200
  };
144
- return isAsked ? tell() : Promise.resolve().then(tell);
201
+ if (!isAsked)
202
+ return Promise.resolve().then(tell);
203
+ const told = tell().finally(() => askedCalls.delete(told));
204
+ askedCalls.add(told);
205
+ return told;
145
206
  };
146
207
  const asked = async (run) => {
147
208
  askedDepth += 1;
148
209
  try {
149
- return await run();
210
+ const value = await run();
211
+ while (askedCalls.size > 0)
212
+ await Promise.allSettled(askedCalls);
213
+ return value;
150
214
  }
151
215
  finally {
152
216
  askedDepth -= 1;
@@ -163,7 +227,7 @@ export function createUi({ name, claude, router, report, announce, mod }) {
163
227
  }
164
228
  const mine = collected;
165
229
  collected = collecting === 0 ? undefined : [];
166
- return merged(mine);
230
+ return withNeededRedraw(merged(mine));
167
231
  };
168
232
  const logOnce = (what) => {
169
233
  let hasLogged = false;
@@ -174,30 +238,49 @@ export function createUi({ name, claude, router, report, announce, mod }) {
174
238
  };
175
239
  };
176
240
  const renderOf = (type) => markdownRenders.get(type);
177
- const markdownIn = (table) => {
241
+ const drawnBlock = (piece, at, drawClaudes, table) => {
242
+ if ('text' in piece)
243
+ return drawClaudes(piece.text);
244
+ const { render, props: block } = piece;
245
+ const draw = () => render.Component({ ...block, ...at, Default: ({ source = block.source }) => drawClaudes(source) });
246
+ try {
247
+ return table === undefined ? draw() : drawWith(table, draw);
248
+ }
249
+ catch (error) {
250
+ render.log(threw(error));
251
+ return drawClaudes(block.source);
252
+ }
253
+ };
254
+ const othersFrom = (table, at, from) => (props) => {
255
+ for (const [index, styler] of stylers.entries()) {
256
+ if (index < from)
257
+ continue;
258
+ const cached = `${stylersSeen}\u0000${at.surface}\u0000${at.columns}\u0000${props.text}`;
259
+ const drawn = drawWith(table, () => borrowed(styler, cached, { markdown: props.text, ...at }), othersFrom(table, at, index + 1));
260
+ if (drawn !== undefined)
261
+ return drawn;
262
+ }
263
+ return table.Markdown(props);
264
+ };
265
+ const markdownIn = (table, at) => {
178
266
  const reader = read;
267
+ const others = othersFrom(table, at, 0);
179
268
  if (reader === undefined)
180
- return undefined;
269
+ return stylers.length === 0 ? undefined : others;
181
270
  return (props) => {
182
271
  const pieces = reader(props.text, renderOf);
183
272
  if (pieces.every((piece) => 'text' in piece))
184
- return table.Markdown(props);
185
- const drawClaudes = (text) => table.Markdown({ ...props, text });
186
- const drawings = pieces.map((piece) => {
187
- if ('text' in piece)
188
- return drawClaudes(piece.text);
189
- const { render, props: block } = piece;
190
- try {
191
- return drawWith(table, () => render.Component({ ...block, Default: ({ source = block.source }) => drawClaudes(source) }));
192
- }
193
- catch (error) {
194
- render.log(threw(error));
195
- return drawClaudes(block.source);
196
- }
197
- });
273
+ return others(props);
274
+ const drawings = pieces.map((piece) => drawnBlock(piece, at, (text) => others({ ...props, text }), table));
198
275
  return drawPieces(drawings.map((drawing) => ({ drawing, isClaudesRow: false })), 'pane');
199
276
  };
200
277
  };
278
+ const markdownDrawing = (text, at) => {
279
+ const pieces = read === undefined ? [] : read(text, renderOf);
280
+ if (pieces.every((piece) => 'text' in piece))
281
+ return Markdown({ text });
282
+ return drawPieces(pieces.map((piece) => ({ drawing: drawnBlock(piece, at, (source) => Markdown({ text: source })), isClaudesRow: false })), 'pane');
283
+ };
201
284
  const routeMarkdown = (createReader) => {
202
285
  if (read !== undefined)
203
286
  return;
@@ -215,7 +298,7 @@ export function createUi({ name, claude, router, report, announce, mod }) {
215
298
  if ('text' in piece)
216
299
  return { drawing: await drawClaudes(piece.text, index === 0), isClaudesRow: true };
217
300
  const { render, props: block } = piece;
218
- const component = await drawComponent(table, (Default) => render.Component({ ...block, Default }), ({ source = block.source }, isClaudesRow) => drawClaudes(source, index === 0 && isClaudesRow));
301
+ const component = await drawComponent(table, (Default) => render.Component({ ...block, ...styledAt(e), id: e.requestId, Default }), ({ source = block.source }, isClaudesRow) => drawClaudes(source, index === 0 && isClaudesRow));
219
302
  if (!('failure' in component))
220
303
  return component;
221
304
  render.log(component.failure);
@@ -260,10 +343,10 @@ export function createUi({ name, claude, router, report, announce, mod }) {
260
343
  const { init } = added;
261
344
  openPanes.add(init.id);
262
345
  paneStates.set(init.id, { isShown: true, isFocused: e.props.isFocused });
263
- readPanes();
346
+ void readPanes();
264
347
  const table = claude.ui.resolve(e);
265
348
  try {
266
- return drawWith(table, () => init.render(mod(), { ...e.props, params: added.params }), markdownIn(table));
349
+ return drawWith(table, () => init.render(mod(), { ...e.props, surface: e.surface, params: added.params }), markdownIn(table, styledAt(e, e.props.bodyColumns)));
267
350
  }
268
351
  catch (error) {
269
352
  paneLogs.get(init.id)?.(`threw: ${messageOf(error)}`);
@@ -281,8 +364,8 @@ export function createUi({ name, claude, router, report, announce, mod }) {
281
364
  router.add('ui.close', async (e, next) => {
282
365
  openPanes.delete(e.id);
283
366
  paneStates.delete(e.id);
284
- readPanes();
285
367
  const closed = await next(e);
368
+ await readPanes();
286
369
  const init = panes.get(e.id)?.init;
287
370
  if (init?.onClose !== undefined)
288
371
  await handle(init, 'onClose', () => init.onClose?.(mod(), e));
@@ -427,14 +510,15 @@ export function createUi({ name, claude, router, report, announce, mod }) {
427
510
  const Component = (props) => render(mod(), props);
428
511
  const stop = (subscription) => () => {
429
512
  subscription?.remove();
430
- if (markdown !== undefined)
431
- markdownRenders.delete(markdown.markdown);
513
+ if (markdown !== undefined && markdownRenders.delete(markdown.markdown))
514
+ void send({ parts: partList() });
432
515
  styles.delete(feature);
433
516
  claude.ui.invalidate('ui.render');
434
517
  };
435
518
  if (markdown !== undefined) {
436
519
  markdownRenders.set(markdown.markdown, { Component, log: logOnce(`${nameOf(kind)} style`) });
437
520
  routeMarkdown(markdown.createReader);
521
+ void send({ parts: partList() });
438
522
  return { kind, remove: stop() };
439
523
  }
440
524
  const log = logOnce(`${nameOf(kind)} style`);
@@ -442,7 +526,7 @@ export function createUi({ name, claude, router, report, announce, mod }) {
442
526
  if (e.component !== component)
443
527
  return next(e);
444
528
  const table = claude.ui.resolve(e);
445
- const drawn = await drawComponent(table, (Default) => Component({ ...e.props, Default }), (given, isClaudesRow) => next({ ...e, props: pieceProps(e.props, given, isClaudesRow) }));
529
+ const drawn = await drawComponent(table, (Default) => Component({ ...e.props, ...styledAt(e), id: e.requestId, Default }), (given, isClaudesRow) => next({ ...e, props: pieceProps(e.props, given, isClaudesRow) }));
446
530
  if ('failure' in drawn) {
447
531
  log(drawn.failure);
448
532
  return next(e);
@@ -455,48 +539,49 @@ export function createUi({ name, claude, router, report, announce, mod }) {
455
539
  };
456
540
  const forgetDraws = (key) => {
457
541
  for (const kept of latestDraws.get(key)?.values() ?? [])
458
- for (const draw of kept)
542
+ for (const draw of kept.draws)
459
543
  draws.delete(draw);
460
544
  latestDraws.delete(key);
461
545
  };
462
546
  const drawPart = (key, title, input, render) => {
463
547
  try {
464
548
  const pressed = [];
465
- const drawn = render();
466
- const tree = drawn === undefined ? null : elementData(drawn, pressed);
549
+ const tree = treeOf(render, pressed);
467
550
  failedDraws.delete(key);
468
- drawCount += 1;
551
+ const number = nextDraw();
469
552
  const inputs = latestDraws.get(key) ?? new Map();
470
553
  latestDraws.set(key, inputs);
471
554
  const drawnFor = JSON.stringify('turn' in input ? { ...input, turn: input.turn.prompt.id } : input);
472
- const earlier = inputs.get(drawnFor) ?? [];
473
- const kept = [...earlier, drawCount].slice(-2);
555
+ const earlier = inputs.get(drawnFor)?.draws ?? [];
556
+ const kept = [...earlier, number].slice(-2);
474
557
  for (const draw of earlier)
475
558
  if (!kept.includes(draw))
476
559
  draws.delete(draw);
477
560
  inputs.delete(drawnFor);
478
- inputs.set(drawnFor, kept);
561
+ inputs.set(drawnFor, { draws: kept, tree: JSON.stringify(tree), render });
479
562
  const [oldest] = inputs;
480
563
  if (inputs.size > keptDrawInputs && oldest !== undefined) {
481
- for (const draw of oldest[1])
564
+ for (const draw of oldest[1].draws)
482
565
  draws.delete(draw);
483
566
  inputs.delete(oldest[0]);
484
567
  }
485
- draws.set(drawCount, { id: key, handlers: pressed });
486
- return { draw: drawCount, tree };
568
+ draws.set(number, { id: key, handlers: pressed });
569
+ return { draw: number, tree };
487
570
  }
488
571
  catch (error) {
489
572
  if (!failedDraws.has(key))
490
573
  report({ id: key, message: `The ${title} could not draw`, error });
491
574
  failedDraws.add(key);
492
- return null;
575
+ throw error;
493
576
  }
494
577
  };
495
578
  const draw = (input) => {
579
+ if ('markdown' in input)
580
+ return drawPart('markdown', 'markdown style', input, () => markdownDrawing(input.markdown, { surface: input.surface, columns: input.columns }));
496
581
  if ('page' in input) {
497
582
  const added = pages.get(input.page);
498
583
  if (added === undefined)
499
- return null;
584
+ throw new Error(`${name} has no ${input.page} page.`);
500
585
  return drawPart(`page:${input.page}`, `${added.init.title} page`, input, () => added.init.render(mod(), { params: input.params, isFocused: input.isFocused, columns: input.columns }));
501
586
  }
502
587
  const widget = widgets.get(input.widget);
@@ -506,19 +591,25 @@ export function createUi({ name, claude, router, report, announce, mod }) {
506
591
  const context = (input.area === TurnEnd && input.turn !== undefined ? { area: input.area, columns: input.columns, turn: input.turn } : { area: input.area, columns: input.columns });
507
592
  return drawPart(`widget:${widget.id}`, `${widget.title} widget`, input, () => widget.render(mod(), context));
508
593
  };
509
- const press = async ({ draw: drawn, handler, args }) => {
594
+ const press = async ({ draw: drawn, handler, args }, caller) => {
510
595
  const known = draws.get(drawn);
511
596
  const run = known?.handlers[handler];
512
597
  if (known === undefined || run === undefined)
513
598
  throw new Error(`${name}: that button drew before the last redraw. Press the one it shows now.`);
514
- return collect(async () => {
599
+ if (caller !== cmodPluginName && known.id !== 'markdown')
600
+ throw new Error(`${name}: ${caller} pressed a button only the cmod plugin draws.`);
601
+ const pressed = async () => {
515
602
  try {
516
603
  await run(...args);
517
604
  }
518
605
  catch (error) {
519
606
  report({ id: known.id, message: 'Its button failed', error });
520
607
  }
521
- });
608
+ };
609
+ if (caller === cmodPluginName)
610
+ return collect(pressed);
611
+ await pressed();
612
+ return null;
522
613
  };
523
614
  return {
524
615
  panes: { add: addPane },
@@ -539,19 +630,28 @@ export function createUi({ name, claude, router, report, announce, mod }) {
539
630
  await passOn();
540
631
  return answer;
541
632
  },
542
- answer(method, input) {
633
+ answer(method, input, caller) {
634
+ const isMarkdown = method === partMethods.draw && isObject(input) && typeof input['markdown'] === 'string';
635
+ if (caller !== cmodPluginName && !isMarkdown && method !== partMethods.press)
636
+ return undefined;
543
637
  if (method === partMethods.list)
544
638
  return Promise.resolve({ value: partList() });
545
639
  if (method === partMethods.draw)
546
- return Promise.resolve({ value: draw(input) });
640
+ return Promise.resolve().then(() => ({ value: draw(input) }));
547
641
  if (method === partMethods.press)
548
- return press(input).then((value) => ({ value }));
642
+ return press(input, caller).then((value) => ({ value }));
549
643
  if (method === partMethods.shown) {
550
644
  shownPages = input;
551
645
  return Promise.resolve({ value: null });
552
646
  }
647
+ if (method === partMethods.stylers) {
648
+ hear(input, true);
649
+ return Promise.resolve({ value: null });
650
+ }
553
651
  return undefined;
554
652
  },
653
+ markdownIn,
654
+ restyled: (stylers) => hear(stylers, true),
555
655
  started() {
556
656
  const held = merged(beforeStart ?? []);
557
657
  beforeStart = undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"watches.d.ts","sourceRoot":"","sources":["../src/runtime/watches.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAE3C,MAAM,MAAM,OAAO,GAAG;IAAE,GAAG,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,GAAG,YAAY,CAAA;CAAE,CAAA;AAQhH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CA4DzF"}
1
+ {"version":3,"file":"watches.d.ts","sourceRoot":"","sources":["../src/runtime/watches.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAE3C,MAAM,MAAM,OAAO,GAAG;IAAE,GAAG,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,GAAG,YAAY,CAAA;CAAE,CAAA;AAUhH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAyEzF"}
@@ -1,12 +1,17 @@
1
1
  import { ifMissing } from '../utils/paths.js';
2
2
  const watchMs = 500;
3
+ const missingEvery = 4;
3
4
  const watchedFiles = 2000;
4
5
  export function createWatches(claude, report) {
5
6
  const watches = new Set();
6
7
  let polling;
7
8
  let isPolling = false;
8
- const stampsOf = async (paths) => {
9
+ let ticks = 0;
10
+ const stampsOf = async (watch) => {
11
+ const { paths } = watch;
9
12
  const stamps = new Map();
13
+ const missing = new Set();
14
+ const skipped = ticks % missingEvery === 0 ? new Set() : watch.missing;
10
15
  const visit = async (path) => {
11
16
  const stat = await claude.fs.stat(path).catch(ifMissing(undefined));
12
17
  if (stat === undefined)
@@ -19,8 +24,16 @@ export function createWatches(claude, report) {
19
24
  for (const entry of await claude.fs.list(path).catch(ifMissing([])))
20
25
  await visit(`${path}/${entry.name}`);
21
26
  };
22
- for (const path of paths)
27
+ for (const path of paths) {
28
+ if (skipped.has(path)) {
29
+ missing.add(path);
30
+ continue;
31
+ }
23
32
  await visit(path);
33
+ if (![...stamps.keys()].some((seen) => seen === path || seen.startsWith(`${path}/`)))
34
+ missing.add(path);
35
+ }
36
+ watch.missing = missing;
24
37
  return stamps;
25
38
  };
26
39
  const stop = (watch) => {
@@ -30,7 +43,7 @@ export function createWatches(claude, report) {
30
43
  polling?.cancel();
31
44
  polling = undefined;
32
45
  };
33
- const stampsOrStop = (watch) => stampsOf(watch.paths).catch((error) => {
46
+ const stampsOrStop = (watch) => stampsOf(watch).catch((error) => {
34
47
  stop(watch);
35
48
  report({ id: 'fs:onChange', message: 'Could not read the files fs.onChange watches', error });
36
49
  return undefined;
@@ -39,6 +52,7 @@ export function createWatches(claude, report) {
39
52
  if (isPolling)
40
53
  return;
41
54
  isPolling = true;
55
+ ticks += 1;
42
56
  try {
43
57
  for (const watch of watches) {
44
58
  const now = await stampsOrStop(watch);
@@ -63,7 +77,7 @@ export function createWatches(claude, report) {
63
77
  };
64
78
  return {
65
79
  add(paths, listener) {
66
- const watch = { paths, listener, seen: undefined };
80
+ const watch = { paths, listener, seen: undefined, missing: new Set() };
67
81
  watches.add(watch);
68
82
  void stampsOrStop(watch).then((stamps) => void (watch.seen ??= stamps));
69
83
  polling ??= claude.clock.every(watchMs, () => void poll());
@@ -1,10 +1,11 @@
1
- import type { ElementTable, RenderElement } from 'claude-code';
1
+ import type { ElementTable, MarkdownProps, RenderElement } from 'claude-code';
2
2
  import type { TurnData } from '../session/conversation.js';
3
3
  import { type Area } from '../ui/areas.js';
4
+ import type { StyledAt } from '../ui/styles.js';
4
5
  import type { Claude } from './claude.js';
5
6
  import type { DrawnParts } from './parts.js';
6
7
  import type { Router } from './router.js';
7
- import { type PartsUpdate } from './ui.js';
8
+ import { type PartsUpdate, type WidgetDeclaration } from './ui.js';
8
9
  export type WidgetRow = {
9
10
  readonly left: readonly string[];
10
11
  readonly right: readonly string[];
@@ -18,7 +19,7 @@ export type PlacedWidget = {
18
19
  readonly mod: string;
19
20
  readonly title: string;
20
21
  readonly areas: readonly Area[];
21
- };
22
+ } & Pick<WidgetDeclaration, 'place'>;
22
23
  export type Move = 'up' | 'down' | 'left' | 'right';
23
24
  export declare const claudeHint = "claude/hint";
24
25
  export declare const claudeModes = "claude/modes";
@@ -31,18 +32,19 @@ export type Widgets = {
31
32
  reload(): Promise<void>;
32
33
  recheck(): Promise<void>;
33
34
  rows(area: Area, columns: number): RenderElement[];
34
- draw(area: Area, table: ElementTable, columns: number, turn?: TurnData): Promise<RenderElement[]>;
35
+ draw(area: Area, table: ElementTable, at: StyledAt, turn?: TurnData): Promise<RenderElement[]>;
35
36
  };
36
37
  export declare function layoutOf(value: unknown): WidgetLayout;
37
38
  export declare function placedIn(layout: WidgetLayout, key: string): Area | undefined;
38
39
  export declare function toggled(layout: WidgetLayout, key: string): WidgetLayout;
39
40
  export declare function moved(layout: WidgetLayout, widget: PlacedWidget, move: Move): WidgetLayout;
40
- export declare function serveWidgets({ claude, router, store, report, turnClosedBy, parts, }: {
41
+ export declare function serveWidgets({ claude, router, store, report, turnClosedBy, parts, markdownIn, }: {
41
42
  readonly claude: Claude;
42
43
  readonly router: Router;
43
44
  readonly store: string;
44
45
  readonly report: (id: string, message: string, error: unknown) => void;
45
46
  readonly turnClosedBy: (rowId: string) => TurnData | undefined;
46
47
  readonly parts: DrawnParts;
48
+ readonly markdownIn: (table: ElementTable, at: StyledAt) => ((props: MarkdownProps) => RenderElement) | undefined;
47
49
  }): Widgets;
48
50
  //# sourceMappingURL=widgets.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"widgets.d.ts","sourceRoot":"","sources":["../src/runtime/widgets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAC1D,OAAO,EAAqD,KAAK,IAAI,EAAE,MAAM,gBAAgB,CAAA;AAI7F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAe,KAAK,WAAW,EAA0B,MAAM,SAAS,CAAA;AAE/E,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAA;AAE/F,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,SAAS,EAAE,CAAC,CAAC,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAA;AAE/H,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,CAAA;CAAE,CAAA;AAElI,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAEnD,eAAO,MAAM,UAAU,gBAAgB,CAAA;AAEvC,eAAO,MAAM,WAAW,iBAAiB,CAAA;AAEzC,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAGjD,CAAA;AAUD,MAAM,MAAM,OAAO,GAAG;IACpB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAA;IACxE,OAAO,IAAI,SAAS,YAAY,EAAE,CAAA;IAClC,MAAM,IAAI,YAAY,CAAA;IACtB,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnE,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACvB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACxB,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE,CAAA;IAClD,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAA;CAClG,CAAA;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAKrD;AAMD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAE5E;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAEvE;AAED,wBAAgB,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,GAAG,YAAY,CA2B1F;AAUD,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,MAAM,EACN,KAAK,EACL,MAAM,EACN,YAAY,EACZ,KAAK,GACN,EAAE;IACD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACtE,QAAQ,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,QAAQ,GAAG,SAAS,CAAA;IAC9D,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;CAC3B,GAAG,OAAO,CAuKV"}
1
+ {"version":3,"file":"widgets.d.ts","sourceRoot":"","sources":["../src/runtime/widgets.ts"],"names":[],"mappings":"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"}