@adecore/agents-react 0.20.0 → 0.21.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 (134) hide show
  1. package/dist/chat/chat-client.d.ts +1 -0
  2. package/dist/chat/logic/generated-image.d.ts +27 -0
  3. package/dist/chat/logic/generated-image.js +32 -0
  4. package/dist/chat/logic/timeline-target.d.ts +19 -1
  5. package/dist/chat/logic/timeline-target.js +49 -3
  6. package/dist/chat/logic/timeline.js +25 -17
  7. package/dist/chat/subagent-conversation.d.ts +2 -1
  8. package/dist/chat/subagent-conversation.js +7 -1
  9. package/dist/chat/timeline-flash.d.ts +3 -1
  10. package/dist/chat/timeline-flash.js +2 -2
  11. package/dist/chat/ui/CodeBlock.d.ts +3 -1
  12. package/dist/chat/ui/CodeBlock.js +38 -7
  13. package/dist/chat/ui/Composer.js +13 -3
  14. package/dist/chat/ui/ImageView.d.ts +8 -2
  15. package/dist/chat/ui/ImageView.js +12 -6
  16. package/dist/chat/ui/Markdown.d.ts +7 -1
  17. package/dist/chat/ui/Markdown.js +80 -23
  18. package/dist/chat/ui/MessageActions.js +6 -7
  19. package/dist/chat/ui/SubagentTimeline.js +7 -3
  20. package/dist/chat/ui/Timeline.js +68 -8
  21. package/dist/chat/ui/TimelineMenu.js +7 -3
  22. package/dist/chat/ui/file-links.d.ts +2 -1
  23. package/dist/chat/ui/file-links.js +8 -3
  24. package/dist/chat/ui/intelligent-ui/UiBlockFooter.d.ts +15 -0
  25. package/dist/chat/ui/intelligent-ui/UiBlockFooter.js +50 -0
  26. package/dist/chat/ui/intelligent-ui/UiBlockFrame.d.ts +10 -0
  27. package/dist/chat/ui/intelligent-ui/UiBlockFrame.js +61 -0
  28. package/dist/chat/ui/intelligent-ui/UiFallbackPart.d.ts +10 -0
  29. package/dist/chat/ui/intelligent-ui/UiFallbackPart.js +11 -0
  30. package/dist/chat/ui/intelligent-ui/UiReply.d.ts +13 -0
  31. package/dist/chat/ui/intelligent-ui/UiReply.js +325 -0
  32. package/dist/chat/ui/intelligent-ui/block-local.d.ts +2 -0
  33. package/dist/chat/ui/intelligent-ui/block-local.js +13 -0
  34. package/dist/chat/ui/intelligent-ui/chart-data.d.ts +16 -0
  35. package/dist/chat/ui/intelligent-ui/chart-data.js +46 -0
  36. package/dist/chat/ui/intelligent-ui/node-text.d.ts +9 -0
  37. package/dist/chat/ui/intelligent-ui/node-text.js +52 -0
  38. package/dist/chat/ui/intelligent-ui/registry.d.ts +2 -0
  39. package/dist/chat/ui/intelligent-ui/registry.js +50 -0
  40. package/dist/chat/ui/intelligent-ui/render-context.d.ts +50 -0
  41. package/dist/chat/ui/intelligent-ui/render-context.js +3 -0
  42. package/dist/chat/ui/intelligent-ui/renderers/chart.d.ts +3 -0
  43. package/dist/chat/ui/intelligent-ui/renderers/chart.js +125 -0
  44. package/dist/chat/ui/intelligent-ui/renderers/choices.d.ts +4 -0
  45. package/dist/chat/ui/intelligent-ui/renderers/choices.js +41 -0
  46. package/dist/chat/ui/intelligent-ui/renderers/content.d.ts +14 -0
  47. package/dist/chat/ui/intelligent-ui/renderers/content.js +66 -0
  48. package/dist/chat/ui/intelligent-ui/renderers/data.d.ts +7 -0
  49. package/dist/chat/ui/intelligent-ui/renderers/data.js +87 -0
  50. package/dist/chat/ui/intelligent-ui/renderers/inputs.d.ts +7 -0
  51. package/dist/chat/ui/intelligent-ui/renderers/inputs.js +60 -0
  52. package/dist/chat/ui/intelligent-ui/renderers/links.d.ts +10 -0
  53. package/dist/chat/ui/intelligent-ui/renderers/links.js +86 -0
  54. package/dist/chat/ui/intelligent-ui/renderers/structure.d.ts +6 -0
  55. package/dist/chat/ui/intelligent-ui/renderers/structure.js +32 -0
  56. package/dist/chat/ui/intelligent-ui/renderers/text.d.ts +8 -0
  57. package/dist/chat/ui/intelligent-ui/renderers/text.js +61 -0
  58. package/dist/chat/ui/intelligent-ui/reply-parts.d.ts +9 -0
  59. package/dist/chat/ui/intelligent-ui/reply-parts.js +18 -0
  60. package/dist/chat/ui/intelligent-ui/table-data.d.ts +31 -0
  61. package/dist/chat/ui/intelligent-ui/table-data.js +79 -0
  62. package/dist/chat/ui/intelligent-ui/tones.d.ts +7 -0
  63. package/dist/chat/ui/intelligent-ui/tones.js +31 -0
  64. package/dist/chat/ui/intelligent-ui/use-later.d.ts +1 -0
  65. package/dist/chat/ui/intelligent-ui/use-later.js +16 -0
  66. package/dist/chat/ui/intelligent-ui/use-ui-links.d.ts +9 -0
  67. package/dist/chat/ui/intelligent-ui/use-ui-links.js +107 -0
  68. package/dist/chat/ui/intelligent-ui/use-ui-queries.d.ts +20 -0
  69. package/dist/chat/ui/intelligent-ui/use-ui-queries.js +259 -0
  70. package/dist/chat/ui/reply-context.d.ts +8 -0
  71. package/dist/chat/ui/reply-context.js +1 -0
  72. package/dist/chat/ui/rows/GeneratedImageRow.d.ts +5 -0
  73. package/dist/chat/ui/rows/GeneratedImageRow.js +121 -0
  74. package/dist/chat/ui/rows/MessageRows.js +27 -7
  75. package/dist/chat/ui/rows/Rows.js +4 -2
  76. package/dist/chat/ui/rows/SubagentRow.js +5 -0
  77. package/dist/chat/ui/rows/WorkRows.d.ts +4 -1
  78. package/dist/chat/ui/rows/WorkRows.js +4 -3
  79. package/dist/host.d.ts +25 -3
  80. package/dist/host.js +1 -0
  81. package/dist/locales/en/agent-chat.json +89 -1
  82. package/dist/locales/nl/agent-chat.json +89 -1
  83. package/dist/state/chats.js +9 -0
  84. package/dist/theme.css +38 -2
  85. package/package.json +133 -7
  86. package/src/chat/chat-client.ts +1 -0
  87. package/src/chat/logic/generated-image.ts +48 -0
  88. package/src/chat/logic/timeline-target.ts +69 -4
  89. package/src/chat/logic/timeline.ts +28 -17
  90. package/src/chat/subagent-conversation.ts +13 -2
  91. package/src/chat/timeline-flash.ts +5 -3
  92. package/src/chat/ui/CodeBlock.tsx +70 -21
  93. package/src/chat/ui/Composer.tsx +22 -5
  94. package/src/chat/ui/ImageView.tsx +51 -8
  95. package/src/chat/ui/Markdown.tsx +123 -26
  96. package/src/chat/ui/MessageActions.tsx +15 -8
  97. package/src/chat/ui/SubagentTimeline.tsx +26 -1
  98. package/src/chat/ui/Timeline.tsx +98 -22
  99. package/src/chat/ui/TimelineMenu.tsx +6 -3
  100. package/src/chat/ui/file-links.ts +9 -3
  101. package/src/chat/ui/intelligent-ui/UiBlockFooter.tsx +139 -0
  102. package/src/chat/ui/intelligent-ui/UiBlockFrame.tsx +89 -0
  103. package/src/chat/ui/intelligent-ui/UiFallbackPart.tsx +21 -0
  104. package/src/chat/ui/intelligent-ui/UiReply.tsx +531 -0
  105. package/src/chat/ui/intelligent-ui/block-local.ts +16 -0
  106. package/src/chat/ui/intelligent-ui/chart-data.ts +67 -0
  107. package/src/chat/ui/intelligent-ui/node-text.ts +59 -0
  108. package/src/chat/ui/intelligent-ui/registry.ts +52 -0
  109. package/src/chat/ui/intelligent-ui/render-context.ts +78 -0
  110. package/src/chat/ui/intelligent-ui/renderers/chart.tsx +321 -0
  111. package/src/chat/ui/intelligent-ui/renderers/choices.tsx +98 -0
  112. package/src/chat/ui/intelligent-ui/renderers/content.tsx +145 -0
  113. package/src/chat/ui/intelligent-ui/renderers/data.tsx +187 -0
  114. package/src/chat/ui/intelligent-ui/renderers/inputs.tsx +121 -0
  115. package/src/chat/ui/intelligent-ui/renderers/links.tsx +157 -0
  116. package/src/chat/ui/intelligent-ui/renderers/structure.tsx +84 -0
  117. package/src/chat/ui/intelligent-ui/renderers/text.tsx +124 -0
  118. package/src/chat/ui/intelligent-ui/reply-parts.ts +22 -0
  119. package/src/chat/ui/intelligent-ui/table-data.ts +104 -0
  120. package/src/chat/ui/intelligent-ui/tones.ts +37 -0
  121. package/src/chat/ui/intelligent-ui/use-later.ts +17 -0
  122. package/src/chat/ui/intelligent-ui/use-ui-links.ts +123 -0
  123. package/src/chat/ui/intelligent-ui/use-ui-queries.ts +282 -0
  124. package/src/chat/ui/reply-context.ts +7 -0
  125. package/src/chat/ui/rows/GeneratedImageRow.tsx +244 -0
  126. package/src/chat/ui/rows/MessageRows.tsx +69 -7
  127. package/src/chat/ui/rows/Rows.tsx +4 -2
  128. package/src/chat/ui/rows/SubagentRow.tsx +5 -0
  129. package/src/chat/ui/rows/WorkRows.tsx +20 -5
  130. package/src/host.ts +46 -3
  131. package/src/locales/en/agent-chat.json +89 -1
  132. package/src/locales/nl/agent-chat.json +89 -1
  133. package/src/state/chats.ts +9 -0
  134. package/src/theme.css +38 -2
@@ -4,7 +4,9 @@ import type { ThreadCard } from '../../../host';
4
4
  import type { TimelineRow } from '../../logic/timeline';
5
5
  import type { SubagentStep } from '../../subagent-view';
6
6
  import { AgentTurnRow, ApprovalHistoryRow, AssistantRow, CompactionRow, NoteRow, QuestionHistoryRow, ReportRow, ThinkingRow, UserRow } from './MessageRows';
7
+ import { isImageGeneration } from '../../logic/generated-image';
7
8
  import { ForksRow } from './ForksRow';
9
+ import { GeneratedImageRow } from './GeneratedImageRow';
8
10
  import { SubagentBranchRow } from './SubagentRow';
9
11
  import { VisualRow } from './VisualRow';
10
12
  import { WorkflowRow } from './WorkflowRow';
@@ -51,9 +53,9 @@ export function Row({ row, chatId, toggleGroup, toggleTurn, toggleSubagent, open
51
53
  case 'thinking':
52
54
  return <ThinkingRow chatId={chatId} item={row.item} />;
53
55
  case 'work':
54
- return <WorkRow tool={row.tool} />;
56
+ return isImageGeneration(row.tool) ? <GeneratedImageRow chatId={chatId} tool={row.tool} /> : <WorkRow tool={row.tool} />;
55
57
  case 'work-live':
56
- return <WorkLiveRow chatId={chatId} tool={row.tool} />;
58
+ return isImageGeneration(row.tool) ? <GeneratedImageRow chatId={chatId} tool={row.tool} /> : <WorkLiveRow chatId={chatId} tool={row.tool} />;
57
59
  case 'workflow':
58
60
  return <WorkflowRow chatId={chatId} tool={row.tool} workflow={row.workflow} onOpenAgent={openConversation} />;
59
61
  case 'work-group':
@@ -9,6 +9,8 @@ import { Icon, useNow } from '@adecore/ui';
9
9
  import { useOpenForFind } from '../find-reveal';
10
10
  import { Markdown } from '../Markdown';
11
11
  import { TaskRow } from './TaskRow';
12
+ import { isImageGeneration } from '../../logic/generated-image';
13
+ import { GeneratedImageRow } from './GeneratedImageRow';
12
14
  import { RunningFor, ToggleLine, WorkLiveRow, WorkRow } from './WorkRows';
13
15
  import { entryTimeOf, statusWordOf, taskIdOf } from '../../subagent-list';
14
16
  import { useSubagentSupport } from '../../subagent-support';
@@ -27,6 +29,9 @@ const CHILDREN_MAX_PX = 320;
27
29
  */
28
30
  function ChildRow({ chatId, item }: { chatId: string; item: ChatItem }) {
29
31
  if (item.kind === 'tool') {
32
+ if (isImageGeneration(item)) {
33
+ return <GeneratedImageRow chatId={chatId} tool={item} />;
34
+ }
30
35
  return item.state === 'running' ? <WorkLiveRow chatId={chatId} tool={item} /> : <WorkRow tool={item} />;
31
36
  }
32
37
  if (item.kind === 'assistant') {
@@ -114,8 +114,23 @@ function ToolBody({ tool }: { tool: ChatToolItem }) {
114
114
  }
115
115
 
116
116
  /* One settled tool call: a line, and its input and output behind it. An image the call looked at
117
- is drawn under the line, because a picture says more about that read than its path does. */
118
- export function WorkRow({ tool, nested, detail }: { tool: ChatToolItem; nested?: boolean; detail?: string }) {
117
+ is drawn under the line, because a picture says more about that read than its path does.
118
+ `icon`, `label` and `failed` let a row that knows the call better name it in its own words. */
119
+ export function WorkRow({
120
+ tool,
121
+ nested,
122
+ detail,
123
+ icon,
124
+ label,
125
+ failed
126
+ }: {
127
+ tool: ChatToolItem;
128
+ nested?: boolean;
129
+ detail?: string;
130
+ icon?: React.ReactNode;
131
+ label?: string;
132
+ failed?: boolean;
133
+ }) {
119
134
  const [open, setOpen] = useState(false);
120
135
  const image = tool.state === 'done' ? readImagePath(tool.name, tool.input) : null;
121
136
  const { ReadImage } = chatHost();
@@ -123,12 +138,12 @@ export function WorkRow({ tool, nested, detail }: { tool: ChatToolItem; nested?:
123
138
  return (
124
139
  <div data-find-item={tool.id} className={nested ? 'ml-6' : undefined}>
125
140
  <ToggleLine
126
- icon={toolIcon(tool.name)}
127
- label={tool.name}
141
+ icon={icon ?? toolIcon(tool.name)}
142
+ label={label ?? tool.name}
128
143
  detail={detail ?? toolSummary(tool.name, tool.input)}
129
144
  open={open}
130
145
  onToggle={() => setOpen((o) => !o)}
131
- failed={tool.state === 'error'}
146
+ failed={failed ?? tool.state === 'error'}
132
147
  />
133
148
  {image !== null && ReadImage !== null && <ReadImage path={image} />}
134
149
  {open && <ToolBody tool={tool} />}
package/src/host.ts CHANGED
@@ -2,7 +2,18 @@ import type { ComponentProps, ComponentType, ReactNode, RefObject } from 'react'
2
2
  import type { Extension } from '@codemirror/state';
3
3
  import type { EditorView } from '@codemirror/view';
4
4
  import type { ThemeRegistration } from 'shiki';
5
- import type { AgentKind, ChatCheckpointDiff, ChatConfigurePayload, ChatItem, ChatSubagentItem } from '@adecore/agent-contracts';
5
+ import type {
6
+ AgentKind,
7
+ ChatCheckpointDiff,
8
+ ChatConfigurePayload,
9
+ ChatItem,
10
+ ChatSubagentItem,
11
+ ChatUiChoicePayload,
12
+ ChatUiQueryPayload,
13
+ ChatUiQueryReading,
14
+ ChatUiLinkPayload,
15
+ ChatUiLinkReading
16
+ } from '@adecore/agent-contracts';
6
17
  import { isApplePlatform } from '@adecore/ui';
7
18
  import type { TimelineRow } from './chat/logic/timeline';
8
19
  import type { FindReveal } from './chat/ui/find-reveal';
@@ -56,10 +67,22 @@ export interface FileRef {
56
67
  path: string;
57
68
  /* One-based, when the reference named a line. */
58
69
  line?: number;
70
+ column?: number;
71
+ endLine?: number;
59
72
  /* A trailing separator is the only thing that says a reference means a directory. */
60
73
  directory: boolean;
61
74
  }
62
75
 
76
+ export interface ShellCodeBlockContext {
77
+ scopeId: string;
78
+ chatId: string;
79
+ itemId: string;
80
+ language: 'sh' | 'bash' | 'zsh';
81
+ code: string;
82
+ /* A closed fence in a main reply that has stopped streaming. */
83
+ complete: boolean;
84
+ }
85
+
63
86
  /* Where another chat stands and how to get there; a null title is a chat that is gone. */
64
87
  export interface ChatPlace {
65
88
  title: string | null;
@@ -177,6 +200,14 @@ export interface ReplyAuthor {
177
200
  */
178
201
  export interface ChatHost {
179
202
  storage?: ChatStorageOptions;
203
+ intelligentUi?: {
204
+ subscribe?(scopeId: string, chatId: string, changed: () => void): () => void;
205
+ link?(scopeId: string, payload: ChatUiLinkPayload): Promise<ChatUiLinkReading>;
206
+ openLink?(scopeId: string, reading: ChatUiLinkReading): void;
207
+ openUrl?(scopeId: string, url: string): void;
208
+ query?(scopeId: string, payload: ChatUiQueryPayload): Promise<ChatUiQueryReading>;
209
+ sendChoice(scopeId: string, payload: ChatUiChoicePayload): Promise<'sent' | 'queued'>;
210
+ };
180
211
  /* The colors an account may wear in the order a picker offers them, the few it shows first, and their names. */
181
212
  accents: {
182
213
  all: readonly AccentChoice[];
@@ -197,6 +228,13 @@ export interface ChatHost {
197
228
  attachments: {
198
229
  useUrl(scopeId: string, chatId: string, attachmentId: string): ResourceUrl;
199
230
  read(scopeId: string, chatId: string, attachmentId: string): Promise<Blob>;
231
+ /* Opens the file outside the chat, such as in the system's own app or as a download named `suggestedName`; left out, a generated image offers no Open. */
232
+ open?(scopeId: string, chatId: string, attachmentId: string, suggestedName: string): void;
233
+ /*
234
+ * Saves a copy into the folder the chat works for, through the host's own dialog, which also shows a failure.
235
+ * Resolves with the path as a file link opens it, or null when the person cancels; left out, a generated image offers no Save.
236
+ */
237
+ saveToProject?(scopeId: string, chatId: string, attachmentId: string, suggestedName: string): Promise<string | null>;
200
238
  };
201
239
  /* The image a tool read, drawn under its row; null draws none. */
202
240
  ReadImage: ComponentType<{ path: string }> | null;
@@ -210,12 +248,16 @@ export interface ChatHost {
210
248
  };
211
249
  /* How a reply appears while it streams: a word, a block or the whole of it at a time. */
212
250
  useStreaming(): 'words' | 'blocks' | 'whole';
251
+ /* How Send handles a running turn; Option or Alt selects the other behavior. */
252
+ useSendDelivery(): 'queue' | 'steer';
213
253
  /* Paths in an answer as links; null leaves them text. */
214
254
  fileLinks: {
215
255
  /* The reference a piece of text names, where the folder it counts from lets it be opened. */
216
- target(text: string, cwd: string | null): FileRef | null;
217
- open(cwd: string | null, ref: FileRef): void;
256
+ target(text: string, cwd: string | null, scopeId?: string | null): FileRef | null;
257
+ open(cwd: string | null, ref: FileRef, scopeId?: string | null): void;
218
258
  } | null;
259
+ /* Renders in a main reply's shell block action group. Rendering must not execute an action. */
260
+ renderShellCodeBlock?: (context: ShellCodeBlockContext) => ReactNode;
219
261
  /* Find in a thread; without one the thread has no find. */
220
262
  useTimelineFind(options: TimelineFindOptions): TimelineFind;
221
263
  dictation: {
@@ -296,6 +338,7 @@ const DEFAULT_HOST: ChatHost = {
296
338
  ReadImage: null,
297
339
  code: { useMode: () => 'light', useThemes: () => ({ light: 'github-light', dark: 'github-dark' }), custom: [] },
298
340
  useStreaming: () => 'words',
341
+ useSendDelivery: () => 'queue',
299
342
  fileLinks: null,
300
343
  useTimelineFind: () => NO_FIND,
301
344
  dictation: { Textarea: PlainTextarea, composer: null },
@@ -307,7 +307,9 @@
307
307
  "description": "A turn is running. Stop it and clear the thread?",
308
308
  "confirm": "Stop and clear"
309
309
  },
310
- "waitingBelow": "Waiting for your answer below"
310
+ "waitingBelow": "Waiting for your answer below",
311
+ "steerButton": "Send to the running agent. Hold Option or Alt to queue after this turn.",
312
+ "queueWithSteerButton": "Queue after this turn. Hold Option or Alt to send to the running agent."
311
313
  },
312
314
  "activity": {
313
315
  "subagents_one": "{{count}} sub-agent",
@@ -433,6 +435,25 @@
433
435
  "zoomOut": "Zoom out",
434
436
  "reset": "Reset"
435
437
  },
438
+ "generatedImage": {
439
+ "label": "Generated image",
440
+ "generating": "Generating image",
441
+ "failed": "Image generation failed",
442
+ "empty": "The image came back empty",
443
+ "notImage": "The result is not an image",
444
+ "tooLarge": "The image is larger than {{size}}",
445
+ "dimensions": "{{width}} × {{height}}",
446
+ "transparent": "transparent",
447
+ "actions": "Image actions",
448
+ "more": "More actions",
449
+ "copyPrompt": "Copy prompt",
450
+ "open": "Open",
451
+ "openLarge": "Open large",
452
+ "save": "Save to project…",
453
+ "saved": "Saved",
454
+ "savedTo": "Saved to <file>{{path}}</file>",
455
+ "renderFailed": "This image failed to render"
456
+ },
436
457
  "account": {
437
458
  "pill": "{{provider}} account {{account}}"
438
459
  },
@@ -454,5 +475,72 @@
454
475
  "failed": "failed",
455
476
  "cancelled": "cancelled"
456
477
  }
478
+ },
479
+ "code": {
480
+ "actions": "Code actions",
481
+ "copyFailed": "Could not copy the code. Try again."
482
+ },
483
+ "blocks": {
484
+ "label": "Interactive block",
485
+ "progress": {
486
+ "label": "Progress",
487
+ "of": "{{value}} of {{max}}"
488
+ },
489
+ "step": {
490
+ "done": "Done",
491
+ "running": "Running",
492
+ "pending": "Waiting",
493
+ "failed": "Failed",
494
+ "skipped": "Skipped"
495
+ },
496
+ "stat": {
497
+ "change": "{{change}} from {{previous}}",
498
+ "from": "from {{previous}}"
499
+ },
500
+ "table": {
501
+ "showAll": "Show all {{total}} rows"
502
+ },
503
+ "chart": {
504
+ "values": "Values",
505
+ "summary": "{{count}} values, highest {{label}} at {{value}}",
506
+ "summarySeries": "{{count}} categories, highest {{series}} for {{label}} at {{value}}"
507
+ },
508
+ "image": {
509
+ "label": "Image",
510
+ "unavailable": "The image is not available"
511
+ },
512
+ "sources": "Sources",
513
+ "citation": "Source {{number}}: {{title}}",
514
+ "outsideProject": "Outside the project, shown as text",
515
+ "options": "Options",
516
+ "choices": "Choices",
517
+ "choice": {
518
+ "waiting": "Available when the reply is done",
519
+ "unavailable": "Not available",
520
+ "sent": "Sent",
521
+ "queued": "Queued",
522
+ "failed": "Could not send. Try again.",
523
+ "ready": "Choices are ready"
524
+ },
525
+ "answered": "Answered with “{{label}}”",
526
+ "chose": "Chose “{{label}}”",
527
+ "choseOlder": "Chose “{{label}}” on the reply of {{time}}",
528
+ "sendContext": "Send",
529
+ "copyText": "Copy text",
530
+ "fixes_one": "{{count}} fix",
531
+ "fixes_other": "{{count}} fixes",
532
+ "shownAsText": "Shown as text",
533
+ "unknownComponent": "This version cannot draw {{component}}",
534
+ "renderFailed": "Could not draw this part",
535
+ "tooLarge": "The block is too large to draw",
536
+ "unreadable": "The block could not be read",
537
+ "live": {
538
+ "label": "Live",
539
+ "read": "read {{time}}",
540
+ "sources": "Reads {{sources}}",
541
+ "failed": "Could not read {{source}}",
542
+ "refused": "Not allowed to read {{source}}",
543
+ "notRead": "Not read"
544
+ }
457
545
  }
458
546
  }
@@ -307,7 +307,9 @@
307
307
  "description": "Er loopt een turn. Die stoppen en de thread wissen?",
308
308
  "confirm": "Stoppen en wissen"
309
309
  },
310
- "waitingBelow": "Wacht hieronder op je antwoord"
310
+ "waitingBelow": "Wacht hieronder op je antwoord",
311
+ "steerButton": "Stuur naar de werkende agent. Houd Option of Alt ingedrukt om te wachten tot deze beurt klaar is.",
312
+ "queueWithSteerButton": "Wacht tot deze beurt klaar is. Houd Option of Alt ingedrukt om naar de werkende agent te sturen."
311
313
  },
312
314
  "activity": {
313
315
  "subagents_one": "{{count}} sub-agent",
@@ -433,6 +435,25 @@
433
435
  "zoomOut": "Uitzoomen",
434
436
  "reset": "Herstellen"
435
437
  },
438
+ "generatedImage": {
439
+ "label": "Gegenereerde afbeelding",
440
+ "generating": "Afbeelding wordt gemaakt",
441
+ "failed": "Afbeelding maken mislukt",
442
+ "empty": "De afbeelding kwam leeg terug",
443
+ "notImage": "Het resultaat is geen afbeelding",
444
+ "tooLarge": "De afbeelding is groter dan {{size}}",
445
+ "dimensions": "{{width}} × {{height}}",
446
+ "transparent": "transparant",
447
+ "actions": "Acties voor de afbeelding",
448
+ "more": "Meer acties",
449
+ "copyPrompt": "Prompt kopiëren",
450
+ "open": "Openen",
451
+ "openLarge": "Groot openen",
452
+ "save": "Opslaan in project…",
453
+ "saved": "Opgeslagen",
454
+ "savedTo": "Opgeslagen in <file>{{path}}</file>",
455
+ "renderFailed": "Deze afbeelding kon niet worden getoond"
456
+ },
436
457
  "account": {
437
458
  "pill": "{{provider}}-account {{account}}"
438
459
  },
@@ -454,5 +475,72 @@
454
475
  "failed": "mislukt",
455
476
  "cancelled": "geannuleerd"
456
477
  }
478
+ },
479
+ "code": {
480
+ "actions": "Codeacties",
481
+ "copyFailed": "De code kon niet worden gekopieerd. Probeer het opnieuw."
482
+ },
483
+ "blocks": {
484
+ "label": "Interactief blok",
485
+ "progress": {
486
+ "label": "Voortgang",
487
+ "of": "{{value}} van {{max}}"
488
+ },
489
+ "step": {
490
+ "done": "Klaar",
491
+ "running": "Bezig",
492
+ "pending": "Wacht",
493
+ "failed": "Mislukt",
494
+ "skipped": "Overgeslagen"
495
+ },
496
+ "stat": {
497
+ "change": "{{change}} vanaf {{previous}}",
498
+ "from": "vanaf {{previous}}"
499
+ },
500
+ "table": {
501
+ "showAll": "Alle {{total}} rijen tonen"
502
+ },
503
+ "chart": {
504
+ "values": "Waarden",
505
+ "summary": "{{count}} waarden, hoogste {{label}} met {{value}}",
506
+ "summarySeries": "{{count}} categorieën, hoogste {{series}} bij {{label}} met {{value}}"
507
+ },
508
+ "image": {
509
+ "label": "Afbeelding",
510
+ "unavailable": "De afbeelding is niet beschikbaar"
511
+ },
512
+ "sources": "Bronnen",
513
+ "citation": "Bron {{number}}: {{title}}",
514
+ "outsideProject": "Buiten het project, als tekst getoond",
515
+ "options": "Opties",
516
+ "choices": "Keuzes",
517
+ "choice": {
518
+ "waiting": "Beschikbaar zodra het antwoord klaar is",
519
+ "unavailable": "Niet beschikbaar",
520
+ "sent": "Verstuurd",
521
+ "queued": "In de wachtrij",
522
+ "failed": "Versturen lukte niet. Probeer het opnieuw.",
523
+ "ready": "De keuzes staan klaar"
524
+ },
525
+ "answered": "Beantwoord met “{{label}}”",
526
+ "chose": "Gekozen: “{{label}}”",
527
+ "choseOlder": "Gekozen: “{{label}}”, bij het antwoord van {{time}}",
528
+ "sendContext": "Versturen",
529
+ "copyText": "Tekst kopiëren",
530
+ "fixes_one": "{{count}} correctie",
531
+ "fixes_other": "{{count}} correcties",
532
+ "shownAsText": "Als tekst getoond",
533
+ "unknownComponent": "Deze versie kan {{component}} niet tekenen",
534
+ "renderFailed": "Dit deel kon niet getekend worden",
535
+ "tooLarge": "Het blok is te groot om te tekenen",
536
+ "unreadable": "Het blok kon niet gelezen worden",
537
+ "live": {
538
+ "label": "Live",
539
+ "read": "gelezen {{time}}",
540
+ "sources": "Leest {{sources}}",
541
+ "failed": "{{source}} lezen lukte niet",
542
+ "refused": "Geen toegang tot {{source}}",
543
+ "notRead": "Niet gelezen"
544
+ }
457
545
  }
458
546
  }
@@ -159,6 +159,15 @@ export function applyEvent(state: ChatState, event: ChatEvent): ChatState {
159
159
  }
160
160
  case 'delta': {
161
161
  const item = state.items[event.itemId];
162
+ if (event.uiQueries && item?.kind === 'assistant') {
163
+ return { ...state, items: { ...state.items, [event.itemId]: { ...item, uiQueries: event.uiQueries } } };
164
+ }
165
+ if (event.ui !== undefined && event.text === '') {
166
+ if (item?.kind === 'assistant' && item.streaming && event.textLength === item.text.length) {
167
+ return { ...state, items: { ...state.items, [event.itemId]: { ...item, ui: event.ui } } };
168
+ }
169
+ return state;
170
+ }
162
171
  if (item?.kind === 'assistant' || item?.kind === 'thinking') {
163
172
  const grown = { ...item, text: item.text + event.text };
164
173
  // The first text is structure after all, since an empty reply that is not streaming has no row.
package/src/theme.css CHANGED
@@ -117,6 +117,11 @@
117
117
  }
118
118
  @keyframes chat-shine { from { background-position: 200% 0; } to { background-position: -200% 0; } }
119
119
 
120
+ /* What shows through a transparent image: a checkerboard of two grounds, so its edges read in both themes. */
121
+ .chat-checkerboard {
122
+ background: repeating-conic-gradient(var(--surface-sunken) 0 25%, var(--surface-raised) 0 50%) 0 0 / 16px 16px;
123
+ }
124
+
120
125
  /* A word of a reply that just arrived. The duration is REVEAL_FADE_MS in chat/ui/reveal.ts, which
121
126
  keeps the spans until it is over; ease-in-out, because ease-out jumps up and then trails. */
122
127
  .chat-fade { animation: chat-fade 300ms ease-in-out both; }
@@ -129,6 +134,20 @@
129
134
  @starting-style { opacity: 0; }
130
135
  }
131
136
  @keyframes chat-fade { from { opacity: 0; } to { opacity: 1; } }
137
+ /* A node of an intelligent UI block that lands inside another, such as a step or a row: 160ms like
138
+ the card's own children, but only a fade, so a parent arriving with its children rises once. */
139
+ .chat-ui-nodes > * {
140
+ transition: opacity 160ms ease-out;
141
+ @starting-style { opacity: 0; }
142
+ }
143
+ /* A value a live reading changed lights up in the accent once and settles back in 800ms. */
144
+ .chat-ui-changed { animation: chat-ui-changed 800ms ease-out; }
145
+ @keyframes chat-ui-changed { from { color: var(--accent); } }
146
+ @media (prefers-reduced-motion: reduce) {
147
+ .chat-ui-changed { animation: none; }
148
+ }
149
+ /* A source row a citation revealed. The row only takes focus from code, since its own button takes a click. */
150
+ .chat-ui-source:focus { animation: chat-flash 1600ms ease-out both; }
132
151
  /* A visual still on its way says so only once it takes long enough to be noticed. */
133
152
  .chat-visual-wait { animation: chat-fade 200ms ease-out 600ms both; }
134
153
 
@@ -193,9 +212,26 @@
193
212
  .chat-markdown :is(th, td) { border: 1px solid var(--tw-prose-td-borders); padding: 4px 8px; text-align: left; }
194
213
 
195
214
  /* A code block is shiki's own `<pre>`, which brings a background of its own to override. */
196
- .chat-code { border-radius: 8px; border: 1px solid var(--border); background: var(--chat-code-bg); overflow: auto; }
197
- .chat-code pre { margin: 0; padding: round(0.625rem, 1px) round(0.75rem, 1px); background: transparent !important; font-family: var(--font-mono); font-size: var(--text-code); line-height: var(--text-code--line-height); }
215
+ .chat-code { position: relative; min-height: 38px; border-radius: 8px; border: 1px solid var(--border); background: var(--chat-code-bg); overflow: hidden; }
216
+ .chat-code pre { margin: 0; padding: round(0.625rem, 1px) round(0.75rem, 1px); overflow: auto; user-select: text; background: transparent !important; font-family: var(--font-mono); font-size: var(--text-code); line-height: var(--text-code--line-height); }
198
217
  .chat-code code { font-family: inherit; }
218
+ .chat-code-actions { position: absolute; top: 4px; right: 4px; border-radius: 6px; background: var(--surface-raised); color: var(--text-muted); box-shadow: 0 0 0 1px var(--border); }
219
+ .chat-code-error { margin: 0; padding: 0 12px 10px; color: var(--status-error); font-size: var(--text-sm); }
220
+ @media (hover: hover) and (pointer: fine) {
221
+ .chat-code-actions { opacity: 0; pointer-events: none; }
222
+ .chat-code:hover .chat-code-actions, .chat-code:focus-within .chat-code-actions,
223
+ .chat-code[data-copy-state="failed"] .chat-code-actions, .chat-code-actions:has([aria-busy="true"]) { opacity: 1; pointer-events: auto; }
224
+ }
225
+ @media (pointer: coarse) {
226
+ .chat-code { min-height: 54px; }
227
+ .chat-code-actions { padding: 8px; gap: 17px; }
228
+ .chat-code-actions .icon-btn { position: relative; }
229
+ .chat-code-actions .icon-btn::after { content: ''; position: absolute; inset: -8px; }
230
+ }
231
+ @media (prefers-reduced-motion: reduce) {
232
+ .chat-code-actions .icon-btn { transition: none; }
233
+ .chat-code-actions .icon-btn:active { scale: 1; }
234
+ }
199
235
 
200
236
  /* The chat composer is a CodeMirror editor, whose base theme lands in an unlayered style tag at
201
237
  mount. These rules stay unlayered too, and carry one class more than the theme's selectors. */