@adecore/agents-react 0.37.2 → 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.
Files changed (115) hide show
  1. package/dist/chat/chat-client.d.ts +1 -0
  2. package/dist/chat/chat-client.js +2 -1
  3. package/dist/chat/chat-references.d.ts +16 -1
  4. package/dist/chat/chat-references.js +29 -3
  5. package/dist/chat/drafts.d.ts +2 -0
  6. package/dist/chat/drafts.js +21 -5
  7. package/dist/chat/index.d.ts +1 -1
  8. package/dist/chat/logic/visual-height.d.ts +1 -0
  9. package/dist/chat/logic/visual-height.js +1 -1
  10. package/dist/chat/mentions.d.ts +31 -14
  11. package/dist/chat/mentions.js +186 -41
  12. package/dist/chat/stash.d.ts +1 -0
  13. package/dist/chat/stash.js +4 -2
  14. package/dist/chat/ui/Composer.js +438 -429
  15. package/dist/chat/ui/ComposerInput.d.ts +1 -0
  16. package/dist/chat/ui/ComposerInput.js +2 -0
  17. package/dist/chat/ui/HostMenuItems.d.ts +4 -0
  18. package/dist/chat/ui/HostMenuItems.js +15 -0
  19. package/dist/chat/ui/Markdown.d.ts +3 -1
  20. package/dist/chat/ui/Markdown.js +194 -120
  21. package/dist/chat/ui/MessageActions.js +70 -66
  22. package/dist/chat/ui/ReferenceChip.d.ts +15 -0
  23. package/dist/chat/ui/ReferenceChip.js +255 -0
  24. package/dist/chat/ui/Timeline.js +52 -29
  25. package/dist/chat/ui/TimelineMenu.js +84 -78
  26. package/dist/chat/ui/VisualFrame.d.ts +8 -0
  27. package/dist/chat/ui/VisualFrame.js +217 -115
  28. package/dist/chat/ui/chip-clipboard.d.ts +9 -0
  29. package/dist/chat/ui/chip-clipboard.js +105 -0
  30. package/dist/chat/ui/chips.d.ts +5 -0
  31. package/dist/chat/ui/chips.js +15 -1
  32. package/dist/chat/ui/composer/ComposerMenu.js +140 -79
  33. package/dist/chat/ui/composer/DraftParts.d.ts +0 -4
  34. package/dist/chat/ui/composer/DraftParts.js +0 -41
  35. package/dist/chat/ui/composer/chip-paste.d.ts +15 -0
  36. package/dist/chat/ui/composer/chip-paste.js +104 -0
  37. package/dist/chat/ui/composer/chips.d.ts +5 -0
  38. package/dist/chat/ui/composer/chips.js +59 -9
  39. package/dist/chat/ui/composer/key-handler.js +9 -0
  40. package/dist/chat/ui/composer/use-composer-draft.d.ts +0 -1
  41. package/dist/chat/ui/composer/use-composer-draft.js +2 -10
  42. package/dist/chat/ui/composer/use-composer-menus.d.ts +12 -2
  43. package/dist/chat/ui/composer/use-composer-menus.js +192 -149
  44. package/dist/chat/ui/composer/use-composer-submit.js +17 -12
  45. package/dist/chat/ui/composer/use-draft-files.d.ts +7 -1
  46. package/dist/chat/ui/composer/use-draft-files.js +90 -58
  47. package/dist/chat/ui/composer/use-take-back.js +2 -1
  48. package/dist/chat/ui/index.d.ts +1 -1
  49. package/dist/chat/ui/index.js +1 -1
  50. package/dist/chat/ui/reference-icons.d.ts +3 -0
  51. package/dist/chat/ui/reference-icons.js +15 -0
  52. package/dist/chat/ui/reference-lookup.d.ts +7 -0
  53. package/dist/chat/ui/reference-lookup.js +6 -0
  54. package/dist/chat/ui/rehype-chips.d.ts +8 -1
  55. package/dist/chat/ui/rehype-chips.js +35 -11
  56. package/dist/chat/ui/rows/GeneratedImageRow.js +8 -4
  57. package/dist/chat/ui/rows/MessageRows.js +218 -158
  58. package/dist/chat/ui/rows/VisualRow.js +184 -142
  59. package/dist/chat/ui/rows/row-rhythm.js +6 -4
  60. package/dist/chat/ui/thread-references.d.ts +4 -0
  61. package/dist/chat/ui/thread-references.js +78 -0
  62. package/dist/host-hooks.d.ts +2 -0
  63. package/dist/host-hooks.js +7 -0
  64. package/dist/host.d.ts +14 -1
  65. package/dist/index.d.ts +1 -1
  66. package/dist/locales/en/agent-chat.d.ts +20 -3
  67. package/dist/locales/en/agent-chat.js +24 -7
  68. package/dist/locales/nl/agent-chat.d.ts +20 -3
  69. package/dist/locales/nl/agent-chat.js +24 -7
  70. package/package.json +8 -8
  71. package/src/chat/chat-client.ts +4 -1
  72. package/src/chat/chat-references.ts +44 -3
  73. package/src/chat/drafts.ts +24 -6
  74. package/src/chat/index.ts +1 -1
  75. package/src/chat/logic/visual-height.ts +1 -1
  76. package/src/chat/mentions.ts +202 -36
  77. package/src/chat/stash.ts +6 -2
  78. package/src/chat/ui/Composer.tsx +14 -4
  79. package/src/chat/ui/ComposerInput.tsx +4 -0
  80. package/src/chat/ui/HostMenuItems.tsx +11 -0
  81. package/src/chat/ui/Markdown.tsx +57 -25
  82. package/src/chat/ui/MessageActions.tsx +5 -2
  83. package/src/chat/ui/ReferenceChip.tsx +74 -0
  84. package/src/chat/ui/Timeline.tsx +24 -2
  85. package/src/chat/ui/TimelineMenu.tsx +7 -3
  86. package/src/chat/ui/VisualFrame.tsx +68 -21
  87. package/src/chat/ui/chip-clipboard.ts +115 -0
  88. package/src/chat/ui/chips.ts +20 -1
  89. package/src/chat/ui/composer/ComposerMenu.tsx +53 -37
  90. package/src/chat/ui/composer/DraftParts.tsx +0 -12
  91. package/src/chat/ui/composer/chip-paste.ts +122 -0
  92. package/src/chat/ui/composer/chips.ts +60 -10
  93. package/src/chat/ui/composer/key-handler.ts +9 -0
  94. package/src/chat/ui/composer/use-composer-draft.ts +1 -7
  95. package/src/chat/ui/composer/use-composer-menus.ts +68 -29
  96. package/src/chat/ui/composer/use-composer-submit.ts +13 -6
  97. package/src/chat/ui/composer/use-draft-files.ts +39 -5
  98. package/src/chat/ui/composer/use-take-back.ts +2 -1
  99. package/src/chat/ui/index.ts +1 -1
  100. package/src/chat/ui/reference-icons.ts +17 -0
  101. package/src/chat/ui/reference-lookup.ts +13 -0
  102. package/src/chat/ui/rehype-chips.ts +38 -13
  103. package/src/chat/ui/rows/GeneratedImageRow.tsx +9 -4
  104. package/src/chat/ui/rows/MessageRows.tsx +88 -64
  105. package/src/chat/ui/rows/VisualRow.tsx +5 -1
  106. package/src/chat/ui/rows/row-rhythm.ts +6 -4
  107. package/src/chat/ui/thread-references.ts +47 -0
  108. package/src/host-hooks.ts +9 -0
  109. package/src/host.ts +24 -1
  110. package/src/index.ts +1 -0
  111. package/src/locales/en/agent-chat.ts +24 -7
  112. package/src/locales/nl/agent-chat.ts +24 -7
  113. package/dist/chat/ui/ChatReferenceChip.d.ts +0 -4
  114. package/dist/chat/ui/ChatReferenceChip.js +0 -110
  115. package/src/chat/ui/ChatReferenceChip.tsx +0 -26
@@ -1,12 +1,12 @@
1
1
  import { c as _c } from "react/compiler-runtime";
2
- import { useEffect, useLayoutEffect, useRef, useState } from 'react';
2
+ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import clsx from 'clsx';
5
- import { visualThemeFragment } from '@adecore/agent-contracts/visual';
5
+ import { VISUAL_LIMITS, visualThemeFragment } from '@adecore/agent-contracts/visual';
6
6
  import { Spinner, useMeasuredWidth } from '@adecore/ui';
7
7
  import { useChatHost } from '../../host.js';
8
8
  import { useChatScope } from '../../scope.js';
9
- import { initialVisualContentHeight, rememberVisualHeight } from '../logic/visual-height.js';
9
+ import { DEFAULT_HEIGHT_PX, initialVisualContentHeight, rememberVisualHeight } from '../logic/visual-height.js';
10
10
  import { VisualBridge } from '../visual-bridge.js';
11
11
  import { VisualViewportController } from '../visual-viewport.js';
12
12
  import { useVisualTheme } from './visual-theme.js';
@@ -19,13 +19,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
19
19
  function personActsIn(frame) {
20
20
  return frame.ownerDocument.activeElement === frame && navigator.userActivation?.isActive !== false;
21
21
  }
22
- /*
23
- * One visual's page in a frame that loads the app's sandbox host page; the bridge hands it the page and
24
- * the theme. The frame never gets `allow-same-origin`, which keeps the page on an opaque origin even
25
- * when the host page is served on the app's own. Its box keeps one height while it loads or fails.
26
- */
22
+ const defaultHeight = () => DEFAULT_HEIGHT_PX;
23
+ /* A visual of a chat, its page read from the chat's attachments under the visual's id. */
27
24
  export function VisualFrame(t0) {
28
- const $ = _c(65);
25
+ const $ = _c(17);
29
26
  const {
30
27
  chatId,
31
28
  visual,
@@ -34,13 +31,123 @@ export function VisualFrame(t0) {
34
31
  } = t0;
35
32
  const fill = t1 === undefined ? false : t1;
36
33
  const {
37
- visuals,
38
34
  attachments
39
35
  } = useChatHost();
36
+ const scope = useChatScope();
37
+ let t2;
38
+ if ($[0] !== attachments || $[1] !== chatId || $[2] !== scope.id || $[3] !== visual.id) {
39
+ t2 = () => attachments.read(scope.id, chatId, visual.id).then(_temp);
40
+ $[0] = attachments;
41
+ $[1] = chatId;
42
+ $[2] = scope.id;
43
+ $[3] = visual.id;
44
+ $[4] = t2;
45
+ } else {
46
+ t2 = $[4];
47
+ }
48
+ const page = t2;
49
+ let t3;
50
+ if ($[5] !== visual) {
51
+ t3 = width => initialVisualContentHeight(visual, width);
52
+ $[5] = visual;
53
+ $[6] = t3;
54
+ } else {
55
+ t3 = $[6];
56
+ }
57
+ let t4;
58
+ if ($[7] !== visual.id) {
59
+ t4 = (width_0, height) => rememberVisualHeight(visual.id, width_0, height);
60
+ $[7] = visual.id;
61
+ $[8] = t4;
62
+ } else {
63
+ t4 = $[8];
64
+ }
65
+ let t5;
66
+ if ($[9] !== className || $[10] !== fill || $[11] !== page || $[12] !== t3 || $[13] !== t4 || $[14] !== visual.maxHeight || $[15] !== visual.title) {
67
+ t5 = /*#__PURE__*/_jsx(PageFrame, {
68
+ page: page,
69
+ title: visual.title,
70
+ maxHeight: visual.maxHeight,
71
+ fill: fill,
72
+ className: className,
73
+ initialHeight: t3,
74
+ onHeight: t4
75
+ });
76
+ $[9] = className;
77
+ $[10] = fill;
78
+ $[11] = page;
79
+ $[12] = t3;
80
+ $[13] = t4;
81
+ $[14] = visual.maxHeight;
82
+ $[15] = visual.title;
83
+ $[16] = t5;
84
+ } else {
85
+ t5 = $[16];
86
+ }
87
+ return t5;
88
+ }
89
+ /*
90
+ * A visual's page outside a chat thread, such as one an app kept: the same sandboxed frame, theme and
91
+ * links as in a thread, with the page read through `page`. Draws nothing while the host has no `visuals`.
92
+ */
93
+ function _temp(blob) {
94
+ return blob.text();
95
+ }
96
+ export function VisualPageFrame(t0) {
97
+ const $ = _c(6);
98
+ let props;
99
+ let t1;
100
+ if ($[0] !== t0) {
101
+ ({
102
+ maxHeight: t1,
103
+ ...props
104
+ } = t0);
105
+ $[0] = t0;
106
+ $[1] = props;
107
+ $[2] = t1;
108
+ } else {
109
+ props = $[1];
110
+ t1 = $[2];
111
+ }
112
+ const maxHeight = t1 === undefined ? VISUAL_LIMITS.maxHeight : t1;
113
+ let t2;
114
+ if ($[3] !== maxHeight || $[4] !== props) {
115
+ t2 = /*#__PURE__*/_jsx(PageFrame, {
116
+ ...props,
117
+ maxHeight: maxHeight,
118
+ initialHeight: defaultHeight
119
+ });
120
+ $[3] = maxHeight;
121
+ $[4] = props;
122
+ $[5] = t2;
123
+ } else {
124
+ t2 = $[5];
125
+ }
126
+ return t2;
127
+ }
128
+ /*
129
+ * One page in a frame that loads the app's sandbox host page; the bridge hands it the page and the
130
+ * theme. The frame never gets `allow-same-origin`, which keeps the page on an opaque origin even when
131
+ * the host page is served on the app's own. Its box keeps one height while it loads or fails.
132
+ */
133
+ function PageFrame(t0) {
134
+ const $ = _c(61);
135
+ const {
136
+ page,
137
+ title,
138
+ maxHeight,
139
+ fill: t1,
140
+ className,
141
+ initialHeight,
142
+ onHeight
143
+ } = t0;
144
+ const fill = t1 === undefined ? false : t1;
145
+ const {
146
+ visuals
147
+ } = useChatHost();
40
148
  const {
41
149
  t
42
150
  } = useTranslation("agent-chat");
43
- const scope = useChatScope();
44
151
  const frameUrl = visuals?.frameUrl ?? null;
45
152
  const boxRef = useRef(null);
46
153
  const frameRef = useRef(null);
@@ -63,6 +170,7 @@ export function VisualFrame(t0) {
63
170
  const setBox = t2;
64
171
  const theme = useVisualTheme(boxRef);
65
172
  const widthRef = useRef(width);
173
+ const onHeightRef = useRef(onHeight);
66
174
  const [reported, setReported] = useState(null);
67
175
  const [drawn, setDrawn] = useState(null);
68
176
  const [failed, setFailed] = useState(false);
@@ -74,11 +182,11 @@ export function VisualFrame(t0) {
74
182
  });
75
183
  }
76
184
  let t3;
77
- if ($[2] !== fill || $[3] !== reported || $[4] !== visual || $[5] !== width) {
78
- t3 = fill || width === 0 ? undefined : reported ?? initialVisualContentHeight(visual, width);
185
+ if ($[2] !== fill || $[3] !== initialHeight || $[4] !== reported || $[5] !== width) {
186
+ t3 = fill || width === 0 ? undefined : reported ?? initialHeight(width);
79
187
  $[2] = fill;
80
- $[3] = reported;
81
- $[4] = visual;
188
+ $[3] = initialHeight;
189
+ $[4] = reported;
82
190
  $[5] = width;
83
191
  $[6] = t3;
84
192
  } else {
@@ -87,22 +195,24 @@ export function VisualFrame(t0) {
87
195
  const height = t3;
88
196
  let t4;
89
197
  let t5;
90
- if ($[7] !== width) {
198
+ if ($[7] !== onHeight || $[8] !== width) {
91
199
  t4 = () => {
92
200
  widthRef.current = width;
201
+ onHeightRef.current = onHeight;
93
202
  };
94
- t5 = [width];
95
- $[7] = width;
96
- $[8] = t4;
97
- $[9] = t5;
203
+ t5 = [width, onHeight];
204
+ $[7] = onHeight;
205
+ $[8] = width;
206
+ $[9] = t4;
207
+ $[10] = t5;
98
208
  } else {
99
- t4 = $[8];
100
- t5 = $[9];
209
+ t4 = $[9];
210
+ t5 = $[10];
101
211
  }
102
212
  useLayoutEffect(t4, t5);
103
213
  let t6;
104
214
  let t7;
105
- if ($[10] !== attachments || $[11] !== chatId || $[12] !== fill || $[13] !== opening || $[14] !== scope.id || $[15] !== visual.id || $[16] !== visual.maxHeight || $[17] !== visuals) {
215
+ if ($[11] !== fill || $[12] !== maxHeight || $[13] !== opening || $[14] !== page || $[15] !== visuals) {
106
216
  t6 = () => {
107
217
  const frame = frameRef.current;
108
218
  if (opening === null || frame === null) {
@@ -111,9 +221,9 @@ export function VisualFrame(t0) {
111
221
  const bridge = new VisualBridge({
112
222
  frame: () => frame.contentWindow,
113
223
  visual: {
114
- maxHeight: visual.maxHeight
224
+ maxHeight
115
225
  },
116
- page: attachments.read(scope.id, chatId, visual.id).then(_temp),
226
+ page: page(),
117
227
  theme: opening.theme,
118
228
  mayOpenLink: () => personActsIn(frame),
119
229
  openLink: url => visuals?.openLink(url),
@@ -122,7 +232,7 @@ export function VisualFrame(t0) {
122
232
  return;
123
233
  }
124
234
  setReported(next);
125
- rememberVisualHeight(visual.id, widthRef.current, next);
235
+ onHeightRef.current?.(widthRef.current, next);
126
236
  },
127
237
  ...(!fill ? {
128
238
  onViewportReady: () => setViewportReady(true),
@@ -141,24 +251,21 @@ export function VisualFrame(t0) {
141
251
  bridgeRef.current = null;
142
252
  };
143
253
  };
144
- t7 = [opening, scope.id, chatId, visual.id, visual.maxHeight, fill, visuals, attachments];
145
- $[10] = attachments;
146
- $[11] = chatId;
147
- $[12] = fill;
254
+ t7 = [opening, page, maxHeight, fill, visuals];
255
+ $[11] = fill;
256
+ $[12] = maxHeight;
148
257
  $[13] = opening;
149
- $[14] = scope.id;
150
- $[15] = visual.id;
151
- $[16] = visual.maxHeight;
152
- $[17] = visuals;
153
- $[18] = t6;
154
- $[19] = t7;
258
+ $[14] = page;
259
+ $[15] = visuals;
260
+ $[16] = t6;
261
+ $[17] = t7;
155
262
  } else {
156
- t6 = $[18];
157
- t7 = $[19];
263
+ t6 = $[16];
264
+ t7 = $[17];
158
265
  }
159
266
  useLayoutEffect(t6, t7);
160
267
  let t8;
161
- if ($[20] !== fill || $[21] !== viewportReady) {
268
+ if ($[18] !== fill || $[19] !== viewportReady) {
162
269
  t8 = () => {
163
270
  const box = boxRef.current;
164
271
  const viewport = viewportRef.current;
@@ -174,98 +281,96 @@ export function VisualFrame(t0) {
174
281
  controllerRef.current = null;
175
282
  };
176
283
  };
177
- $[20] = fill;
178
- $[21] = viewportReady;
179
- $[22] = t8;
284
+ $[18] = fill;
285
+ $[19] = viewportReady;
286
+ $[20] = t8;
180
287
  } else {
181
- t8 = $[22];
288
+ t8 = $[20];
182
289
  }
183
290
  let t9;
184
- if ($[23] !== chatId || $[24] !== fill || $[25] !== opening || $[26] !== scope.id || $[27] !== viewportReady || $[28] !== visual.id || $[29] !== visual.maxHeight) {
185
- t9 = [fill, viewportReady, opening, scope.id, chatId, visual.id, visual.maxHeight];
186
- $[23] = chatId;
187
- $[24] = fill;
188
- $[25] = opening;
189
- $[26] = scope.id;
190
- $[27] = viewportReady;
191
- $[28] = visual.id;
192
- $[29] = visual.maxHeight;
193
- $[30] = t9;
291
+ if ($[21] !== fill || $[22] !== maxHeight || $[23] !== opening || $[24] !== page || $[25] !== viewportReady) {
292
+ t9 = [fill, viewportReady, opening, page, maxHeight];
293
+ $[21] = fill;
294
+ $[22] = maxHeight;
295
+ $[23] = opening;
296
+ $[24] = page;
297
+ $[25] = viewportReady;
298
+ $[26] = t9;
194
299
  } else {
195
- t9 = $[30];
300
+ t9 = $[26];
196
301
  }
197
302
  useLayoutEffect(t8, t9);
198
303
  let t10;
199
- if ($[31] === Symbol.for("react.memo_cache_sentinel")) {
304
+ if ($[27] === Symbol.for("react.memo_cache_sentinel")) {
200
305
  t10 = () => controllerRef.current?.refresh();
201
- $[31] = t10;
306
+ $[27] = t10;
202
307
  } else {
203
- t10 = $[31];
308
+ t10 = $[27];
204
309
  }
205
310
  let t11;
206
- if ($[32] !== height || $[33] !== width) {
311
+ if ($[28] !== height || $[29] !== width) {
207
312
  t11 = [height, width];
208
- $[32] = height;
209
- $[33] = width;
210
- $[34] = t11;
313
+ $[28] = height;
314
+ $[29] = width;
315
+ $[30] = t11;
211
316
  } else {
212
- t11 = $[34];
317
+ t11 = $[30];
213
318
  }
214
319
  useLayoutEffect(t10, t11);
215
320
  let t12;
216
321
  let t13;
217
- if ($[35] !== theme) {
322
+ if ($[31] !== theme) {
218
323
  t12 = () => {
219
324
  if (theme !== null) {
220
325
  bridgeRef.current?.setTheme(theme);
221
326
  }
222
327
  };
223
328
  t13 = [theme];
224
- $[35] = theme;
225
- $[36] = t12;
226
- $[37] = t13;
329
+ $[31] = theme;
330
+ $[32] = t12;
331
+ $[33] = t13;
227
332
  } else {
228
- t12 = $[36];
229
- t13 = $[37];
333
+ t12 = $[32];
334
+ t13 = $[33];
230
335
  }
231
336
  useEffect(t12, t13);
232
337
  const t14 = fill ? "absolute inset-0" : "relative w-full";
233
338
  let t15;
234
- if ($[38] !== className || $[39] !== t14) {
339
+ if ($[34] !== className || $[35] !== t14) {
235
340
  t15 = clsx(t14, className);
236
- $[38] = className;
237
- $[39] = t14;
238
- $[40] = t15;
341
+ $[34] = className;
342
+ $[35] = t14;
343
+ $[36] = t15;
239
344
  } else {
240
- t15 = $[40];
345
+ t15 = $[36];
241
346
  }
242
347
  let t16;
243
- if ($[41] !== height) {
348
+ if ($[37] !== height) {
244
349
  t16 = {
245
350
  height
246
351
  };
247
- $[41] = height;
248
- $[42] = t16;
352
+ $[37] = height;
353
+ $[38] = t16;
249
354
  } else {
250
- t16 = $[42];
355
+ t16 = $[38];
251
356
  }
252
357
  const t17 = fill ? "absolute inset-0" : "sticky top-0 w-full";
253
358
  let t18;
254
- if ($[43] !== fill) {
359
+ if ($[39] !== fill) {
255
360
  t18 = fill ? undefined : {
256
361
  height: "100%"
257
362
  };
258
- $[43] = fill;
259
- $[44] = t18;
363
+ $[39] = fill;
364
+ $[40] = t18;
260
365
  } else {
261
- t18 = $[44];
366
+ t18 = $[40];
262
367
  }
263
368
  let t19;
264
- if ($[45] !== drawn || $[46] !== failed || $[47] !== opening || $[48] !== visual.title) {
369
+ if ($[41] !== drawn || $[42] !== failed || $[43] !== opening || $[44] !== title) {
265
370
  t19 = opening !== null && /*#__PURE__*/_jsx("iframe", {
266
371
  ref: frameRef,
267
372
  src: opening.src,
268
- title: visual.title,
373
+ title: title,
269
374
  loading: "lazy",
270
375
  sandbox: "allow-scripts allow-forms",
271
376
  className: clsx("block size-full border-0", failed && "invisible"),
@@ -274,72 +379,69 @@ export function VisualFrame(t0) {
274
379
  },
275
380
  onLoad: () => bridgeRef.current?.loaded()
276
381
  });
277
- $[45] = drawn;
278
- $[46] = failed;
279
- $[47] = opening;
280
- $[48] = visual.title;
281
- $[49] = t19;
382
+ $[41] = drawn;
383
+ $[42] = failed;
384
+ $[43] = opening;
385
+ $[44] = title;
386
+ $[45] = t19;
282
387
  } else {
283
- t19 = $[49];
388
+ t19 = $[45];
284
389
  }
285
390
  let t20;
286
- if ($[50] !== drawn || $[51] !== failed || $[52] !== t || $[53] !== visual.title) {
391
+ if ($[46] !== drawn || $[47] !== failed || $[48] !== t || $[49] !== title) {
287
392
  t20 = failed ? /*#__PURE__*/_jsx("p", {
288
393
  className: "absolute inset-0 grid place-items-center px-4 text-center text-xs text-text-muted",
289
394
  children: t("visuals.failed", {
290
- title: visual.title
395
+ title
291
396
  })
292
397
  }) : drawn === null && /*#__PURE__*/_jsx("span", {
293
398
  className: "chat-visual-wait pointer-events-none absolute inset-0 grid place-items-center text-text-faint",
294
399
  children: /*#__PURE__*/_jsx(Spinner, {
295
400
  size: 16,
296
401
  label: t("visuals.loading", {
297
- title: visual.title
402
+ title
298
403
  })
299
404
  })
300
405
  });
301
- $[50] = drawn;
302
- $[51] = failed;
303
- $[52] = t;
304
- $[53] = visual.title;
305
- $[54] = t20;
406
+ $[46] = drawn;
407
+ $[47] = failed;
408
+ $[48] = t;
409
+ $[49] = title;
410
+ $[50] = t20;
306
411
  } else {
307
- t20 = $[54];
412
+ t20 = $[50];
308
413
  }
309
414
  let t21;
310
- if ($[55] !== t17 || $[56] !== t18 || $[57] !== t19 || $[58] !== t20) {
415
+ if ($[51] !== t17 || $[52] !== t18 || $[53] !== t19 || $[54] !== t20) {
311
416
  t21 = /*#__PURE__*/_jsxs("div", {
312
417
  ref: viewportRef,
313
418
  className: t17,
314
419
  style: t18,
315
420
  children: [t19, t20]
316
421
  });
317
- $[55] = t17;
318
- $[56] = t18;
319
- $[57] = t19;
320
- $[58] = t20;
321
- $[59] = t21;
422
+ $[51] = t17;
423
+ $[52] = t18;
424
+ $[53] = t19;
425
+ $[54] = t20;
426
+ $[55] = t21;
322
427
  } else {
323
- t21 = $[59];
428
+ t21 = $[55];
324
429
  }
325
430
  let t22;
326
- if ($[60] !== setBox || $[61] !== t15 || $[62] !== t16 || $[63] !== t21) {
431
+ if ($[56] !== setBox || $[57] !== t15 || $[58] !== t16 || $[59] !== t21) {
327
432
  t22 = /*#__PURE__*/_jsx("div", {
328
433
  ref: setBox,
329
434
  className: t15,
330
435
  style: t16,
331
436
  children: t21
332
437
  });
333
- $[60] = setBox;
334
- $[61] = t15;
335
- $[62] = t16;
336
- $[63] = t21;
337
- $[64] = t22;
438
+ $[56] = setBox;
439
+ $[57] = t15;
440
+ $[58] = t16;
441
+ $[59] = t21;
442
+ $[60] = t22;
338
443
  } else {
339
- t22 = $[64];
444
+ t22 = $[60];
340
445
  }
341
446
  return t22;
342
- }
343
- function _temp(blob) {
344
- return blob.text();
345
447
  }
@@ -0,0 +1,9 @@
1
+ import type { ChatAssistantItem, ChatUserItem } from '@adecore/agent-contracts';
2
+ import { type ClipboardChips } from '../mentions.js';
3
+ import type { TimelineRow } from '../logic/timeline.js';
4
+ import type { ReferenceLookup } from './reference-lookup.js';
5
+ export declare function messageClipboardChips(item: ChatUserItem | ChatAssistantItem, text: string, lookup: Pick<ReferenceLookup, 'items' | 'referenced'>): ClipboardChips;
6
+ export declare function rowClipboardChips(row: TimelineRow, text: string, lookup: Pick<ReferenceLookup, 'items' | 'referenced'>): ClipboardChips;
7
+ export declare function selectionChips(selection: Selection): ClipboardChips;
8
+ export declare function copySelectionWithChips(event: ClipboardEvent): void;
9
+ export declare function copyWithChips(chips: ClipboardChips): void;
@@ -0,0 +1,105 @@
1
+ import { copyText } from '@adecore/ui';
2
+ import { referenceTokens } from '../chat-references.js';
3
+ import { clipboardChipsOf, hasClipboardChips, tokenizeChips, tokenizeNamedReferences, writeClipboardChips } from '../mentions.js';
4
+ /* The chips a copy of a whole message carries: what the person picked, or what a reply names. */
5
+ export function messageClipboardChips(item, text, lookup) {
6
+ if (item.kind === 'assistant') {
7
+ return clipboardChipsOf(text, tokenizeNamedReferences(text, lookup.items, lookup.referenced));
8
+ }
9
+ const tokens = referenceTokens([...(item.chats ?? []), ...(item.references ?? [])], lookup.items);
10
+ return clipboardChipsOf(text, tokenizeChips(text, item.mentions ?? [], item.skills ?? [], tokens));
11
+ }
12
+ /* The chips a copy of a row of the thread carries; only a message has any. */
13
+ export function rowClipboardChips(row, text, lookup) {
14
+ if (row.kind === 'user' || row.kind === 'assistant') {
15
+ return messageClipboardChips(row.item, text, lookup);
16
+ }
17
+ return clipboardChipsOf(text, []);
18
+ }
19
+ /* What a chip the transcript drew stands for, read back from the attributes `TokenChip` writes. */
20
+ function segmentOf(chip) {
21
+ const value = chip.getAttribute('data-chip-value');
22
+ if (value === null) {
23
+ return null;
24
+ }
25
+ switch (chip.getAttribute('data-chip')) {
26
+ case 'mention':
27
+ return {
28
+ kind: 'mention',
29
+ path: value
30
+ };
31
+ case 'skill':
32
+ return {
33
+ kind: 'skill',
34
+ name: value
35
+ };
36
+ case 'reference':
37
+ {
38
+ const name = chip.getAttribute('data-chip-name');
39
+ return name === null || name === '' ? null : {
40
+ kind: 'reference',
41
+ id: value,
42
+ name
43
+ };
44
+ }
45
+ default:
46
+ return null;
47
+ }
48
+ }
49
+ /* The chips a selection holds whole, beside the text it copies; a chip cut in half is only text. */
50
+ export function selectionChips(selection) {
51
+ const segments = [];
52
+ for (let i = 0; i < selection.rangeCount; i++) {
53
+ const container = selection.getRangeAt(i).commonAncestorContainer;
54
+ const root = container instanceof Element ? container : container.parentElement;
55
+ const own = root?.closest('[data-chip]');
56
+ const candidates = own ? [own] : [...(root?.querySelectorAll('[data-chip]') ?? [])];
57
+ for (const chip of candidates) {
58
+ const segment = selection.containsNode(chip, false) ? segmentOf(chip) : null;
59
+ if (segment !== null) {
60
+ segments.push(segment);
61
+ }
62
+ }
63
+ }
64
+ return clipboardChipsOf(selection.toString(), segments);
65
+ }
66
+ /* A copy of a selection in the thread, which carries the chips in it as well when it holds any. */
67
+ export function copySelectionWithChips(event) {
68
+ const selection = document.getSelection();
69
+ if (event.defaultPrevented || event.clipboardData === null || selection === null || selection.isCollapsed) {
70
+ return;
71
+ }
72
+ const chips = selectionChips(selection);
73
+ if (!hasClipboardChips(chips)) {
74
+ return;
75
+ }
76
+ writeClipboardChips(event.clipboardData, chips);
77
+ event.preventDefault();
78
+ }
79
+ /*
80
+ * Copies a text and its chips. The asynchronous clipboard writes no type of an app's own that a paste
81
+ * can read, so the copy goes through a copy event, which only a page with the user's gesture gets.
82
+ */
83
+ export function copyWithChips(chips) {
84
+ if (!hasClipboardChips(chips) || typeof document.execCommand !== 'function') {
85
+ void copyText(chips.text);
86
+ return;
87
+ }
88
+ let written = false;
89
+ const write = event => {
90
+ if (event.clipboardData !== null) {
91
+ writeClipboardChips(event.clipboardData, chips);
92
+ event.preventDefault();
93
+ written = true;
94
+ }
95
+ };
96
+ document.addEventListener('copy', write, true);
97
+ try {
98
+ document.execCommand('copy');
99
+ } finally {
100
+ document.removeEventListener('copy', write, true);
101
+ }
102
+ if (!written) {
103
+ void copyText(chips.text);
104
+ }
105
+ }
@@ -1,4 +1,9 @@
1
1
  export declare const MENTION_TONE = "bg-accent-soft text-accent shadow-[inset_0_0_0_1px_color-mix(in_srgb,var(--accent)_25%,transparent)]";
2
2
  export declare const SKILL_TONE = "bg-skill-soft text-skill shadow-[inset_0_0_0_1px_color-mix(in_srgb,var(--skill)_25%,transparent)]";
3
+ export declare const REFERENCE_TONE = "bg-text/8 text-text shadow-[inset_0_0_0_1px_var(--border-strong)]";
4
+ export declare const REFERENCE_GONE_TONE = "bg-text/4 text-text-faint shadow-[inset_0_0_0_1px_var(--border)]";
3
5
  export declare const CHIP_IN_EDITOR = "box-decoration-clone rounded-md px-1 py-0.5";
6
+ export declare const TOKEN_IN_MESSAGE = "box-decoration-clone rounded-md px-1.5 py-0.5 text-xs font-medium";
7
+ export declare const TOKEN_ICON = "mr-1 inline align-[calc(0.5cap-7px)]!";
8
+ export declare const TOKEN_ICON_IN_EDITOR = "mr-1 inline-block align-[calc(0.5cap-6px)]";
4
9
  export declare const CHIP_IN_MESSAGE = "inline-flex h-5 max-w-full items-center gap-1 rounded-md px-1.5 align-[-3px] text-xs/none font-medium";
@@ -1,11 +1,25 @@
1
- /* An `@file` or `$skill` token drawn as a pill: the tone says which of the two it is, the shape
1
+ /* An `@file`, `$skill` or `#reference` token drawn as a pill: the tone says which it is, the shape
2
2
  says where it is drawn. */
3
3
  export const MENTION_TONE = 'bg-accent-soft text-accent shadow-[inset_0_0_0_1px_color-mix(in_srgb,var(--accent)_25%,transparent)]';
4
4
  /* A skill is the same token shape as a file mention, in its own hue. */
5
5
  export const SKILL_TONE = 'bg-skill-soft text-skill shadow-[inset_0_0_0_1px_color-mix(in_srgb,var(--skill)_25%,transparent)]';
6
+ /* A `#` to a view, node or chat is neutral, so it never reads as a file or a skill. */
7
+ export const REFERENCE_TONE = 'bg-text/8 text-text shadow-[inset_0_0_0_1px_var(--border-strong)]';
8
+ /* A `#` whose view or node is no longer in the project. */
9
+ export const REFERENCE_GONE_TONE = 'bg-text/4 text-text-faint shadow-[inset_0_0_0_1px_var(--border)]';
6
10
  /* The pill in the composer's editor, around the token's own characters: the editor places the caret
7
11
  from what it drew, so padding is safe, and a chip that wraps keeps it on every fragment. */
8
12
  export const CHIP_IN_EDITOR = 'box-decoration-clone rounded-md px-1 py-0.5';
13
+ /* A picked or named token in a message or a reply, a shade smaller than the text around it. Inline like
14
+ the editor's pill, so its name sits on the baseline of the sentence, and a selection copies it on the
15
+ same line as the words around it, where a flex box would put it on lines of its own. */
16
+ export const TOKEN_IN_MESSAGE = 'box-decoration-clone rounded-md px-1.5 py-0.5 text-xs font-medium';
17
+ /* The mark in place of the sigil, a 14px icon. Inline, since the base styles make an svg a block of its
18
+ own. Its middle goes on the middle of the name's capitals, where a mark reads as centered; the middle
19
+ of the x-height `Icon` aligns on sits low next to a capital. */
20
+ export const TOKEN_ICON = 'mr-1 inline align-[calc(0.5cap-7px)]!';
21
+ /* The same middle for the 12px mark the composer draws, in a box whose bottom edge is its baseline. */
22
+ export const TOKEN_ICON_IN_EDITOR = 'mr-1 inline-block align-[calc(0.5cap-6px)]';
9
23
  /* The same pill in the transcript: a real inline box a shade smaller than the text around it, with
10
24
  the icon in place of the sigil. */
11
25
  export const CHIP_IN_MESSAGE = 'inline-flex h-5 max-w-full items-center gap-1 rounded-md px-1.5 align-[-3px] text-xs/none font-medium';