@adecore/agents-react 0.37.3 → 0.38.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.
@@ -5,6 +5,7 @@ import { ButtonGroup, ErrorBoundary, Icon, IconButton, Menu, Surface } from '@ad
5
5
  import { useChatHost } from '../../../host.js';
6
6
  import { useChatScope } from '../../../scope.js';
7
7
  import { useVisualDialog } from '../../visuals.js';
8
+ import { HostMenuItems } from '../HostMenuItems.js';
8
9
  import { VisualFrame } from '../VisualFrame.js';
9
10
  /*
10
11
  * A page an agent published, borderless on the thread's ground, with its width set by the row. Its few
@@ -14,7 +15,7 @@ import { VisualFrame } from '../VisualFrame.js';
14
15
  */
15
16
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
16
17
  export function VisualRow(t0) {
17
- const $ = _c(50);
18
+ const $ = _c(63);
18
19
  const {
19
20
  chatId,
20
21
  visual
@@ -24,223 +25,264 @@ export function VisualRow(t0) {
24
25
  t
25
26
  } = useTranslation("agent-chat");
26
27
  const {
28
+ id: scopeId,
27
29
  keyOf
28
30
  } = useChatScope();
29
31
  if (host.visuals === null) {
30
32
  return null;
31
33
  }
32
34
  let t1;
33
- if ($[0] !== chatId || $[1] !== keyOf || $[2] !== visual.id) {
34
- t1 = kind => useVisualDialog.getState().open(keyOf(chatId), visual.id, kind);
35
+ if ($[0] !== chatId || $[1] !== host.visuals || $[2] !== scopeId || $[3] !== visual) {
36
+ t1 = host.visuals.actions?.(scopeId, chatId, visual) ?? [];
35
37
  $[0] = chatId;
36
- $[1] = keyOf;
37
- $[2] = visual.id;
38
- $[3] = t1;
38
+ $[1] = host.visuals;
39
+ $[2] = scopeId;
40
+ $[3] = visual;
41
+ $[4] = t1;
39
42
  } else {
40
- t1 = $[3];
43
+ t1 = $[4];
41
44
  }
42
- const open = t1;
45
+ const extra = t1;
43
46
  let t2;
44
- if ($[4] !== t) {
45
- t2 = t("visuals.renderFailed");
46
- $[4] = t;
47
- $[5] = t2;
47
+ if ($[5] !== chatId || $[6] !== keyOf || $[7] !== visual.id) {
48
+ t2 = kind => useVisualDialog.getState().open(keyOf(chatId), visual.id, kind);
49
+ $[5] = chatId;
50
+ $[6] = keyOf;
51
+ $[7] = visual.id;
52
+ $[8] = t2;
48
53
  } else {
49
- t2 = $[5];
54
+ t2 = $[8];
50
55
  }
56
+ const open = t2;
51
57
  let t3;
52
- if ($[6] !== visual) {
53
- t3 = [visual];
54
- $[6] = visual;
55
- $[7] = t3;
58
+ if ($[9] !== t) {
59
+ t3 = t("visuals.renderFailed");
60
+ $[9] = t;
61
+ $[10] = t3;
56
62
  } else {
57
- t3 = $[7];
63
+ t3 = $[10];
58
64
  }
59
65
  let t4;
60
- if ($[8] !== chatId || $[9] !== visual) {
61
- t4 = /*#__PURE__*/_jsx(VisualFrame, {
62
- chatId: chatId,
63
- visual: visual
64
- });
65
- $[8] = chatId;
66
- $[9] = visual;
67
- $[10] = t4;
66
+ if ($[11] !== visual) {
67
+ t4 = [visual];
68
+ $[11] = visual;
69
+ $[12] = t4;
68
70
  } else {
69
- t4 = $[10];
71
+ t4 = $[12];
70
72
  }
71
73
  let t5;
72
- if ($[11] !== t || $[12] !== visual.title) {
73
- t5 = t("visuals.actions", {
74
- title: visual.title
74
+ if ($[13] !== chatId || $[14] !== visual) {
75
+ t5 = /*#__PURE__*/_jsx(VisualFrame, {
76
+ chatId: chatId,
77
+ visual: visual
75
78
  });
76
- $[11] = t;
77
- $[12] = visual.title;
78
- $[13] = t5;
79
+ $[13] = chatId;
80
+ $[14] = visual;
81
+ $[15] = t5;
79
82
  } else {
80
- t5 = $[13];
83
+ t5 = $[15];
81
84
  }
82
85
  let t6;
83
- if ($[14] !== t) {
84
- t6 = t("visuals.expand");
85
- $[14] = t;
86
- $[15] = t6;
86
+ if ($[16] !== t || $[17] !== visual.title) {
87
+ t6 = t("visuals.actions", {
88
+ title: visual.title
89
+ });
90
+ $[16] = t;
91
+ $[17] = visual.title;
92
+ $[18] = t6;
87
93
  } else {
88
- t6 = $[15];
94
+ t6 = $[18];
89
95
  }
90
96
  let t7;
91
- if ($[16] !== open) {
92
- t7 = () => open("expand");
93
- $[16] = open;
94
- $[17] = t7;
97
+ if ($[19] !== t) {
98
+ t7 = t("visuals.expand");
99
+ $[19] = t;
100
+ $[20] = t7;
95
101
  } else {
96
- t7 = $[17];
102
+ t7 = $[20];
97
103
  }
98
104
  let t8;
99
- if ($[18] !== t6 || $[19] !== t7) {
100
- t8 = /*#__PURE__*/_jsx(IconButton, {
101
- icon: Maximize2,
102
- size: "sm",
103
- label: t6,
104
- onClick: t7
105
- });
106
- $[18] = t6;
107
- $[19] = t7;
108
- $[20] = t8;
105
+ if ($[21] !== open) {
106
+ t8 = () => open("expand");
107
+ $[21] = open;
108
+ $[22] = t8;
109
109
  } else {
110
- t8 = $[20];
110
+ t8 = $[22];
111
111
  }
112
112
  let t9;
113
- if ($[21] !== t) {
114
- t9 = t("visuals.more");
115
- $[21] = t;
116
- $[22] = t9;
113
+ if ($[23] !== t7 || $[24] !== t8) {
114
+ t9 = /*#__PURE__*/_jsx(IconButton, {
115
+ icon: Maximize2,
116
+ size: "sm",
117
+ label: t7,
118
+ onClick: t8
119
+ });
120
+ $[23] = t7;
121
+ $[24] = t8;
122
+ $[25] = t9;
117
123
  } else {
118
- t9 = $[22];
124
+ t9 = $[25];
119
125
  }
120
126
  let t10;
121
- if ($[23] === Symbol.for("react.memo_cache_sentinel")) {
122
- t10 = /*#__PURE__*/_jsx(Menu.Trigger, {});
123
- $[23] = t10;
127
+ if ($[26] !== t) {
128
+ t10 = t("visuals.more");
129
+ $[26] = t;
130
+ $[27] = t10;
124
131
  } else {
125
- t10 = $[23];
132
+ t10 = $[27];
126
133
  }
127
134
  let t11;
128
- if ($[24] !== t9) {
129
- t11 = /*#__PURE__*/_jsx(IconButton, {
130
- icon: Ellipsis,
131
- size: "sm",
132
- label: t9,
133
- render: t10
134
- });
135
- $[24] = t9;
136
- $[25] = t11;
135
+ if ($[28] === Symbol.for("react.memo_cache_sentinel")) {
136
+ t11 = /*#__PURE__*/_jsx(Menu.Trigger, {});
137
+ $[28] = t11;
137
138
  } else {
138
- t11 = $[25];
139
+ t11 = $[28];
139
140
  }
140
141
  let t12;
141
- if ($[26] !== open) {
142
- t12 = () => open("remove");
143
- $[26] = open;
144
- $[27] = t12;
142
+ if ($[29] !== t10) {
143
+ t12 = /*#__PURE__*/_jsx(IconButton, {
144
+ icon: Ellipsis,
145
+ size: "sm",
146
+ label: t10,
147
+ render: t11
148
+ });
149
+ $[29] = t10;
150
+ $[30] = t12;
145
151
  } else {
146
- t12 = $[27];
152
+ t12 = $[30];
147
153
  }
148
154
  let t13;
149
- if ($[28] === Symbol.for("react.memo_cache_sentinel")) {
150
- t13 = /*#__PURE__*/_jsx(Icon, {
151
- icon: Trash2,
152
- size: 14
155
+ if ($[31] !== extra) {
156
+ t13 = /*#__PURE__*/_jsx(HostMenuItems, {
157
+ actions: extra
153
158
  });
154
- $[28] = t13;
159
+ $[31] = extra;
160
+ $[32] = t13;
155
161
  } else {
156
- t13 = $[28];
162
+ t13 = $[32];
157
163
  }
158
164
  let t14;
159
- if ($[29] !== t) {
160
- t14 = t("visuals.remove");
161
- $[29] = t;
162
- $[30] = t14;
165
+ if ($[33] !== extra.length) {
166
+ t14 = extra.length > 0 && /*#__PURE__*/_jsx(Menu.Separator, {});
167
+ $[33] = extra.length;
168
+ $[34] = t14;
163
169
  } else {
164
- t14 = $[30];
170
+ t14 = $[34];
165
171
  }
166
172
  let t15;
167
- if ($[31] !== t12 || $[32] !== t14) {
168
- t15 = /*#__PURE__*/_jsx(Menu.Popup, {
169
- align: "end",
170
- children: /*#__PURE__*/_jsxs(Menu.Item, {
171
- onClick: t12,
172
- children: [t13, " ", t14]
173
- })
174
- });
175
- $[31] = t12;
176
- $[32] = t14;
177
- $[33] = t15;
173
+ if ($[35] !== open) {
174
+ t15 = () => open("remove");
175
+ $[35] = open;
176
+ $[36] = t15;
178
177
  } else {
179
- t15 = $[33];
178
+ t15 = $[36];
180
179
  }
181
180
  let t16;
182
- if ($[34] !== t11 || $[35] !== t15) {
183
- t16 = /*#__PURE__*/_jsxs(Menu.Root, {
184
- children: [t11, t15]
181
+ if ($[37] === Symbol.for("react.memo_cache_sentinel")) {
182
+ t16 = /*#__PURE__*/_jsx(Icon, {
183
+ icon: Trash2,
184
+ size: 14
185
185
  });
186
- $[34] = t11;
187
- $[35] = t15;
188
- $[36] = t16;
186
+ $[37] = t16;
189
187
  } else {
190
- t16 = $[36];
188
+ t16 = $[37];
191
189
  }
192
190
  let t17;
193
- if ($[37] !== t16 || $[38] !== t8) {
194
- t17 = /*#__PURE__*/_jsxs(ButtonGroup, {
195
- children: [t8, t16]
196
- });
197
- $[37] = t16;
198
- $[38] = t8;
191
+ if ($[38] !== t) {
192
+ t17 = t("visuals.remove");
193
+ $[38] = t;
199
194
  $[39] = t17;
200
195
  } else {
201
196
  t17 = $[39];
202
197
  }
203
198
  let t18;
204
- if ($[40] !== t17 || $[41] !== t5) {
205
- t18 = /*#__PURE__*/_jsx(Surface, {
206
- role: "toolbar",
207
- "aria-label": t5,
208
- className: "absolute top-2 right-2 rounded-lg p-0.5 opacity-0 transition-opacity group-focus-within/visual:opacity-100 group-hover/visual:opacity-100 has-[[data-popup-open]]:opacity-100 pointer-coarse:opacity-100",
209
- children: t17
199
+ if ($[40] !== t15 || $[41] !== t17) {
200
+ t18 = /*#__PURE__*/_jsxs(Menu.Item, {
201
+ onClick: t15,
202
+ children: [t16, " ", t17]
210
203
  });
211
- $[40] = t17;
212
- $[41] = t5;
204
+ $[40] = t15;
205
+ $[41] = t17;
213
206
  $[42] = t18;
214
207
  } else {
215
208
  t18 = $[42];
216
209
  }
217
210
  let t19;
218
- if ($[43] !== t18 || $[44] !== t4) {
219
- t19 = /*#__PURE__*/_jsxs("div", {
220
- className: "group/visual relative",
221
- children: [t4, t18]
211
+ if ($[43] !== t13 || $[44] !== t14 || $[45] !== t18) {
212
+ t19 = /*#__PURE__*/_jsxs(Menu.Popup, {
213
+ align: "end",
214
+ children: [t13, t14, t18]
222
215
  });
223
- $[43] = t18;
224
- $[44] = t4;
225
- $[45] = t19;
216
+ $[43] = t13;
217
+ $[44] = t14;
218
+ $[45] = t18;
219
+ $[46] = t19;
226
220
  } else {
227
- t19 = $[45];
221
+ t19 = $[46];
228
222
  }
229
223
  let t20;
230
- if ($[46] !== t19 || $[47] !== t2 || $[48] !== t3) {
231
- t20 = /*#__PURE__*/_jsx(ErrorBoundary, {
232
- label: t2,
233
- resetKeys: t3,
234
- compact: true,
235
- className: "relative rounded-lg",
236
- children: t19
224
+ if ($[47] !== t12 || $[48] !== t19) {
225
+ t20 = /*#__PURE__*/_jsxs(Menu.Root, {
226
+ children: [t12, t19]
237
227
  });
238
- $[46] = t19;
239
- $[47] = t2;
240
- $[48] = t3;
228
+ $[47] = t12;
229
+ $[48] = t19;
241
230
  $[49] = t20;
242
231
  } else {
243
232
  t20 = $[49];
244
233
  }
245
- return t20;
234
+ let t21;
235
+ if ($[50] !== t20 || $[51] !== t9) {
236
+ t21 = /*#__PURE__*/_jsxs(ButtonGroup, {
237
+ children: [t9, t20]
238
+ });
239
+ $[50] = t20;
240
+ $[51] = t9;
241
+ $[52] = t21;
242
+ } else {
243
+ t21 = $[52];
244
+ }
245
+ let t22;
246
+ if ($[53] !== t21 || $[54] !== t6) {
247
+ t22 = /*#__PURE__*/_jsx(Surface, {
248
+ role: "toolbar",
249
+ "aria-label": t6,
250
+ className: "absolute top-2 right-2 rounded-lg p-0.5 opacity-0 transition-opacity group-focus-within/visual:opacity-100 group-hover/visual:opacity-100 has-[[data-popup-open]]:opacity-100 pointer-coarse:opacity-100",
251
+ children: t21
252
+ });
253
+ $[53] = t21;
254
+ $[54] = t6;
255
+ $[55] = t22;
256
+ } else {
257
+ t22 = $[55];
258
+ }
259
+ let t23;
260
+ if ($[56] !== t22 || $[57] !== t5) {
261
+ t23 = /*#__PURE__*/_jsxs("div", {
262
+ className: "group/visual relative",
263
+ children: [t5, t22]
264
+ });
265
+ $[56] = t22;
266
+ $[57] = t5;
267
+ $[58] = t23;
268
+ } else {
269
+ t23 = $[58];
270
+ }
271
+ let t24;
272
+ if ($[59] !== t23 || $[60] !== t3 || $[61] !== t4) {
273
+ t24 = /*#__PURE__*/_jsx(ErrorBoundary, {
274
+ label: t3,
275
+ resetKeys: t4,
276
+ compact: true,
277
+ className: "relative rounded-lg",
278
+ children: t23
279
+ });
280
+ $[59] = t23;
281
+ $[60] = t3;
282
+ $[61] = t4;
283
+ $[62] = t24;
284
+ } else {
285
+ t24 = $[62];
286
+ }
287
+ return t24;
246
288
  }
@@ -4,13 +4,15 @@ import { isBlock } from '../../logic/timeline.js';
4
4
  export const FOLLOW_THRESHOLD_PX = 40;
5
5
  /*
6
6
  * The gaps a row wears for where it sits: a question is followed by its answer gap, and a seam where
7
- * the list rhythm of tool lines meets the block rhythm of prose gets the block gap. A visual has no
8
- * margin of its own like prose does, so the row under one takes the block gap too. A question
9
- * already carries the gap before it, and the row after one is the answer, so neither takes a seam.
7
+ * the list rhythm of tool lines meets the block rhythm of prose gets the block gap. A visual and an
8
+ * app's card have no margin of their own like prose does, so the row under one takes the block gap
9
+ * too. A question already carries the gap before it, and the row after one is the answer, so neither
10
+ * takes a seam.
10
11
  */
11
12
  export function rowRhythm(row, previous) {
12
13
  const question = row.kind === 'user';
13
- const seam = !question && previous !== null && previous.kind !== 'user' && (isBlock(row) !== isBlock(previous) || previous.kind === 'visual');
14
+ const bare = previous?.kind === 'visual' || previous?.kind === 'app-card';
15
+ const seam = !question && previous !== null && previous.kind !== 'user' && (isBlock(row) !== isBlock(previous) || bare);
14
16
  return clsx(question && 'pb-(--chat-answer-gap)', seam && 'pt-(--chat-block-gap)');
15
17
  }
16
18
  /*
package/dist/host.d.ts CHANGED
@@ -2,8 +2,9 @@ import { type ComponentProps, type ComponentType, type ReactNode, type RefObject
2
2
  import type { Extension } from '@codemirror/state';
3
3
  import type { EditorView } from '@codemirror/view';
4
4
  import type { Highlighter, ThemeRegistration } from 'shiki';
5
+ import type { LucideIcon } from 'lucide-react';
5
6
  import type { EditorEngine } from '@adecore/editor';
6
- import type { AgentKind, ChatCheckpointDiff, ChatConfigurePayload, ChatGoalAction, ChatItem, ChatSubagentItem, ChatUiChoicePayload, ChatUiQueryPayload, ChatUiQueryReading, ChatUiLinkPayload, ChatUiLinkReading, ProviderPlatform } from '@adecore/agent-contracts';
7
+ import type { AgentKind, ChatAttachment, ChatCheckpointDiff, ChatConfigurePayload, ChatGoalAction, ChatItem, ChatSubagentItem, ChatUiChoicePayload, ChatUiQueryPayload, ChatUiQueryReading, ChatUiLinkPayload, ChatUiLinkReading, ChatVisual, ProviderPlatform } from '@adecore/agent-contracts';
7
8
  import type { Referables } from './chat/chat-references.js';
8
9
  import type { TimelineRow } from './chat/logic/timeline.js';
9
10
  import type { FindReveal } from './chat/ui/find-reveal.js';
@@ -121,6 +122,14 @@ export interface ThreadCard {
121
122
  export interface VisualHost {
122
123
  frameUrl: string;
123
124
  openLink(url: string): void;
125
+ actions?(scopeId: string, chatId: string, visual: ChatVisual): readonly HostMenuAction[];
126
+ }
127
+ export interface HostMenuAction {
128
+ id: string;
129
+ label: string;
130
+ icon?: LucideIcon;
131
+ disabled?: boolean;
132
+ run(): void;
124
133
  }
125
134
  export interface ReplyAuthor {
126
135
  name: string;
@@ -201,6 +210,7 @@ export interface ChatHost {
201
210
  read(scopeId: string, chatId: string, attachmentId: string): Promise<Blob>;
202
211
  open?(scopeId: string, chatId: string, attachmentId: string, suggestedName: string): void;
203
212
  saveToProject?(scopeId: string, chatId: string, attachmentId: string, suggestedName: string): Promise<string | null>;
213
+ actions?(scopeId: string, chatId: string, attachment: ChatAttachment): readonly HostMenuAction[];
204
214
  };
205
215
  ReadImage: ComponentType<{
206
216
  path: string;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { type ChatActions, type FileRef, type ShellCodeBlockContext, type ChatPlace, type SubagentTask, type TimelineFindOptions, type TimelineFind, type ComposerDictationProps, type ComposerSlotProps, type SubagentSlotProps, type ThreadCard, type VisualHost, type ReplyAuthor, type ChatHost, type UiMapSource, type UiModelViewProps, type CommandLook, type CommandLookMatch, type CommandName, ChatHostProvider, useChatHost, type AccentChoice, type ChatToast, type TextareaProps, type ResourceUrl } from './host.js';
1
+ export { type ChatActions, type FileRef, type ShellCodeBlockContext, type ChatPlace, type SubagentTask, type TimelineFindOptions, type TimelineFind, type ComposerDictationProps, type ComposerSlotProps, type SubagentSlotProps, type ThreadCard, type VisualHost, type HostMenuAction, type ReplyAuthor, type ChatHost, type UiMapSource, type UiModelViewProps, type CommandLook, type CommandLookMatch, type CommandName, ChatHostProvider, useChatHost, type AccentChoice, type ChatToast, type TextareaProps, type ResourceUrl } from './host.js';
2
2
  export { type ChatScope, ChatScopeContext, useChatScope } from './scope.js';
3
3
  export { type ChatRequestMap, type ChatEventMap, type ChatTransportStatus, type ChatTransport, ChatTransportError, errorCode, isConnectionError } from './transport.js';
4
4
  export { portTransport, type PortTransport } from './port-transport.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/agents-react",
3
- "version": "0.37.3",
3
+ "version": "0.38.0",
4
4
  "description": "React agent chat threads, composers, approvals, provider accounts and usage views.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -67,11 +67,11 @@
67
67
  "test:visuals": "bun --config=integration.bunfig.toml --conditions=source test ./src/chat/visual-viewport.integration.test.ts"
68
68
  },
69
69
  "dependencies": {
70
- "@adecore/agent-contracts": "0.37.3",
71
- "@adecore/editor": "0.37.3",
72
- "@adecore/editor-react": "0.37.3",
73
- "@adecore/intelligent-ui": "0.37.3",
74
- "@adecore/merge": "0.37.3",
70
+ "@adecore/agent-contracts": "0.38.0",
71
+ "@adecore/editor": "0.38.0",
72
+ "@adecore/editor-react": "0.38.0",
73
+ "@adecore/intelligent-ui": "0.38.0",
74
+ "@adecore/merge": "0.38.0",
75
75
  "@codemirror/commands": "^6.11.1",
76
76
  "@codemirror/language": "^6.12.4",
77
77
  "@codemirror/state": "^6.7.6",
@@ -94,14 +94,14 @@
94
94
  "zustand": "^5.0.15"
95
95
  },
96
96
  "peerDependencies": {
97
- "@adecore/ui": "^0.37.3",
97
+ "@adecore/ui": "^0.38.0",
98
98
  "i18next": "^26.4.2",
99
99
  "react": "^19.3.0",
100
100
  "react-dom": "^19.3.0",
101
101
  "react-i18next": "^17.0.15"
102
102
  },
103
103
  "devDependencies": {
104
- "@adecore/ui": "0.37.3",
104
+ "@adecore/ui": "0.38.0",
105
105
  "@types/bun": "^1.4.2",
106
106
  "@types/react": "^19.3.0",
107
107
  "@types/react-dom": "^19.3.0",
@@ -1,7 +1,7 @@
1
1
  import { VISUAL_LIMITS, visualFrameHeight, type ChatVisual } from '@adecore/agent-contracts/visual';
2
2
 
3
3
  /* A frame before anything measured its page: room for a chart, never taller than the page may grow. */
4
- const DEFAULT_HEIGHT_PX = 240;
4
+ export const DEFAULT_HEIGHT_PX = 240;
5
5
  /* Plenty for the visuals of a long session; the oldest goes first. */
6
6
  const REMEMBERED_HEIGHTS = 500;
7
7
  // Stay within browser layout coordinates even if a page reports a bogus finite size.
@@ -0,0 +1,11 @@
1
+ import { Icon, Menu } from '@adecore/ui';
2
+ import type { HostMenuAction } from '../../host';
3
+
4
+ /* The items a host adds to a menu of the chat's, in the order it gave them. */
5
+ export function HostMenuItems({ actions }: { actions: readonly HostMenuAction[] }) {
6
+ return actions.map((action) => (
7
+ <Menu.Item key={action.id} disabled={action.disabled} onClick={() => action.run()}>
8
+ {action.icon !== undefined && <Icon icon={action.icon} size={14} />} {action.label}
9
+ </Menu.Item>
10
+ ));
11
+ }