@arbidocs/blocks 0.4.20 → 0.4.22

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 (93) hide show
  1. package/dist/agents/definitions/AgentCard.d.ts +4 -1
  2. package/dist/agents/index.cjs +211 -211
  3. package/dist/agents/index.js +5 -5
  4. package/dist/chat/ChatPage.d.ts +1 -1
  5. package/dist/chat/chat-page-types.d.ts +2 -0
  6. package/dist/chat/index.cjs +169 -168
  7. package/dist/chat/index.js +3 -2
  8. package/dist/chunk-5TRALRWN.js +42 -0
  9. package/dist/chunk-5TRALRWN.js.map +1 -0
  10. package/dist/chunk-7UTB4PHM.cjs +300 -0
  11. package/dist/chunk-7UTB4PHM.cjs.map +1 -0
  12. package/dist/chunk-AA3PT6KR.js +286 -0
  13. package/dist/chunk-AA3PT6KR.js.map +1 -0
  14. package/dist/{chunk-QJEJBLHI.js → chunk-CQ6CYPV4.js} +7 -281
  15. package/dist/chunk-CQ6CYPV4.js.map +1 -0
  16. package/dist/chunk-EUOGPUMJ.cjs +59 -0
  17. package/dist/chunk-EUOGPUMJ.cjs.map +1 -0
  18. package/dist/{chunk-PRVKJ5XL.cjs → chunk-J22DXUJY.cjs} +4 -3
  19. package/dist/chunk-J22DXUJY.cjs.map +1 -0
  20. package/dist/{chunk-ZPOLKYR2.cjs → chunk-JCVZKCXC.cjs} +22 -69
  21. package/dist/chunk-JCVZKCXC.cjs.map +1 -0
  22. package/dist/{chunk-557EU3G4.js → chunk-JLOYWXRB.js} +3 -3
  23. package/dist/{chunk-557EU3G4.js.map → chunk-JLOYWXRB.js.map} +1 -1
  24. package/dist/{chunk-UVIOQUJA.js → chunk-JLPYOPCX.js} +4 -3
  25. package/dist/chunk-JLPYOPCX.js.map +1 -0
  26. package/dist/{chunk-4TBZRZFC.js → chunk-MLJDOH3B.js} +23 -6
  27. package/dist/chunk-MLJDOH3B.js.map +1 -0
  28. package/dist/chunk-NNH7N62N.cjs +876 -0
  29. package/dist/chunk-NNH7N62N.cjs.map +1 -0
  30. package/dist/{chunk-7Q6RBXIV.cjs → chunk-OEQXYWUO.cjs} +22 -5
  31. package/dist/chunk-OEQXYWUO.cjs.map +1 -0
  32. package/dist/{chunk-J4AOAFZF.cjs → chunk-TL2DHSC5.cjs} +11 -297
  33. package/dist/chunk-TL2DHSC5.cjs.map +1 -0
  34. package/dist/chunk-UOLMFC5K.js +846 -0
  35. package/dist/chunk-UOLMFC5K.js.map +1 -0
  36. package/dist/{chunk-DCEI37AX.cjs → chunk-YVIOCITQ.cjs} +3 -3
  37. package/dist/{chunk-DCEI37AX.cjs.map → chunk-YVIOCITQ.cjs.map} +1 -1
  38. package/dist/{chunk-S74WZNK7.js → chunk-ZEIOREJY.js} +5 -36
  39. package/dist/chunk-ZEIOREJY.js.map +1 -0
  40. package/dist/connect/docx.cjs +86 -0
  41. package/dist/connect/docx.cjs.map +1 -0
  42. package/dist/connect/docx.d.ts +1 -0
  43. package/dist/connect/docx.js +84 -0
  44. package/dist/connect/docx.js.map +1 -0
  45. package/dist/connect/index.cjs +136 -69
  46. package/dist/connect/index.cjs.map +1 -1
  47. package/dist/connect/index.d.ts +1 -0
  48. package/dist/connect/index.js +91 -25
  49. package/dist/connect/index.js.map +1 -1
  50. package/dist/connection/ConnectedNativeDocxEditor.d.ts +5 -0
  51. package/dist/connection/useConnectedDocxNotifications.d.ts +9 -0
  52. package/dist/docx/DocxMentions.d.ts +17 -0
  53. package/dist/docx/NativeDocxComparison.d.ts +10 -0
  54. package/dist/docx/NativeDocxEditor.d.ts +50 -0
  55. package/dist/docx/export.d.ts +2 -0
  56. package/dist/docx/fonts.d.ts +2 -0
  57. package/dist/docx/index.cjs +116 -0
  58. package/dist/docx/index.cjs.map +1 -0
  59. package/dist/docx/index.d.ts +10 -0
  60. package/dist/docx/index.js +63 -0
  61. package/dist/docx/index.js.map +1 -0
  62. package/dist/docx/mentions.d.ts +8 -0
  63. package/dist/facts/index.cjs +1 -1
  64. package/dist/facts/index.js +1 -1
  65. package/dist/files/index.cjs +5 -5
  66. package/dist/files/index.js +6 -6
  67. package/dist/index.cjs +795 -794
  68. package/dist/index.cjs.map +1 -1
  69. package/dist/index.js +17 -16
  70. package/dist/index.js.map +1 -1
  71. package/dist/settings/index.cjs +41 -41
  72. package/dist/settings/index.js +4 -4
  73. package/dist/shell/index.cjs +9 -9
  74. package/dist/shell/index.js +1 -1
  75. package/dist/site/index.cjs +13 -13
  76. package/dist/site/index.js +1 -1
  77. package/dist/studio/index.cjs +6 -5
  78. package/dist/studio/index.cjs.map +1 -1
  79. package/dist/studio/index.js +4 -3
  80. package/dist/studio/index.js.map +1 -1
  81. package/dist/workspaces/index.cjs +2 -2
  82. package/dist/workspaces/index.js +2 -2
  83. package/package.json +48 -7
  84. package/src/docx/README.md +68 -0
  85. package/src/docx/styles.css +36 -0
  86. package/dist/chunk-4TBZRZFC.js.map +0 -1
  87. package/dist/chunk-7Q6RBXIV.cjs.map +0 -1
  88. package/dist/chunk-J4AOAFZF.cjs.map +0 -1
  89. package/dist/chunk-PRVKJ5XL.cjs.map +0 -1
  90. package/dist/chunk-QJEJBLHI.js.map +0 -1
  91. package/dist/chunk-S74WZNK7.js.map +0 -1
  92. package/dist/chunk-UVIOQUJA.js.map +0 -1
  93. package/dist/chunk-ZPOLKYR2.cjs.map +0 -1
@@ -0,0 +1,286 @@
1
+ "use client";
2
+ import { prefixedTestId, testIdOr } from './chunk-7F6CBP3Z.js';
3
+ import { memo, useId, useRef, useEffect, useState, useCallback } from 'react';
4
+ import { Bot, AtSign } from 'lucide-react';
5
+ import { cn } from '@arbidocs/react/ui';
6
+ import { jsxs, jsx } from 'react/jsx-runtime';
7
+
8
+ function listOptionId(listboxId, index) {
9
+ return `${listboxId}-option-${index}`;
10
+ }
11
+ function useListKeyNav({
12
+ open,
13
+ items,
14
+ onPick,
15
+ onDismiss,
16
+ resetKey
17
+ }) {
18
+ const [selectedIndex, setSelectedIndex] = useState(0);
19
+ const listboxId = useId();
20
+ const isExpanded = open && items.length > 0;
21
+ useEffect(() => {
22
+ setSelectedIndex(0);
23
+ }, [resetKey, items.length]);
24
+ const handleKeyDown = useCallback(
25
+ (event) => {
26
+ if (!open || items.length === 0) return false;
27
+ switch (event.key) {
28
+ case "ArrowDown":
29
+ event.preventDefault();
30
+ setSelectedIndex((i) => (i + 1) % items.length);
31
+ return true;
32
+ case "ArrowUp":
33
+ event.preventDefault();
34
+ setSelectedIndex((i) => (i - 1 + items.length) % items.length);
35
+ return true;
36
+ case "Enter":
37
+ case "Tab": {
38
+ event.preventDefault();
39
+ const pick = items[selectedIndex] ?? items[0];
40
+ if (pick !== void 0) onPick(pick);
41
+ return true;
42
+ }
43
+ case "Escape":
44
+ event.preventDefault();
45
+ onDismiss?.();
46
+ return true;
47
+ default:
48
+ return false;
49
+ }
50
+ },
51
+ [open, items, selectedIndex, onPick, onDismiss]
52
+ );
53
+ const comboboxProps = isExpanded ? {
54
+ role: "combobox",
55
+ "aria-autocomplete": "list",
56
+ "aria-haspopup": "listbox",
57
+ "aria-expanded": true,
58
+ "aria-controls": listboxId,
59
+ "aria-activedescendant": listOptionId(listboxId, selectedIndex)
60
+ } : {};
61
+ return { listboxId, comboboxProps, selectedIndex, setSelectedIndex, handleKeyDown };
62
+ }
63
+ var DEFAULT_MENTION_LABELS = {
64
+ hint: "Enter or Tab to mention, Esc to dismiss",
65
+ list: "People and assistant to mention",
66
+ resultCount: (count) => `${count} ${count === 1 ? "match" : "matches"}`,
67
+ assistantBadge: "AI",
68
+ chipTitle: (label, kind) => kind === "assistant" ? `${label} (AI assistant)` : label
69
+ };
70
+ var MentionMenu = memo(function MentionMenu2({
71
+ open,
72
+ placement = "above",
73
+ items,
74
+ selectedIndex,
75
+ setSelectedIndex,
76
+ listboxId: listboxIdProp,
77
+ onSelect,
78
+ className,
79
+ labels,
80
+ testId,
81
+ itemTestIdPrefix
82
+ }) {
83
+ const l = { ...DEFAULT_MENTION_LABELS, ...labels };
84
+ const ownId = useId();
85
+ const listboxId = listboxIdProp ?? ownId;
86
+ const scrollRef = useRef(null);
87
+ useEffect(() => {
88
+ if (!open) return;
89
+ const el = scrollRef.current?.querySelector(
90
+ `[data-mention-index="${selectedIndex}"]`
91
+ );
92
+ el?.scrollIntoView?.({ block: "nearest" });
93
+ }, [open, selectedIndex]);
94
+ if (!open || items.length === 0) return null;
95
+ return /* @__PURE__ */ jsxs(
96
+ "div",
97
+ {
98
+ "data-slot": "mention-menu",
99
+ className: cn(
100
+ "absolute left-0 right-0 z-30 max-h-64 overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-lg",
101
+ placement === "above" ? "bottom-full mb-2" : "top-full mt-2",
102
+ className
103
+ ),
104
+ onMouseDown: (e) => e.preventDefault(),
105
+ "data-testid": testIdOr(testId, { testId: "mention-menu" }),
106
+ children: [
107
+ /* @__PURE__ */ jsx("div", { className: "border-b border-border/50 px-3 py-1.5 text-xs text-muted-foreground", children: l.hint }),
108
+ /* @__PURE__ */ jsx("div", { className: "sr-only", role: "status", "aria-live": "polite", "aria-atomic": "true", children: l.resultCount(items.length) }),
109
+ /* @__PURE__ */ jsx(
110
+ "div",
111
+ {
112
+ ref: scrollRef,
113
+ id: listboxId,
114
+ role: "listbox",
115
+ "aria-label": l.list,
116
+ className: "scrollbar-subtle max-h-56 overflow-y-auto py-1",
117
+ children: items.map((item, index) => {
118
+ const isActive = index === selectedIndex;
119
+ const Icon = item.kind === "assistant" ? Bot : AtSign;
120
+ return /* @__PURE__ */ jsxs(
121
+ "div",
122
+ {
123
+ id: listOptionId(listboxId, index),
124
+ role: "option",
125
+ "aria-selected": isActive,
126
+ "data-mention-index": index,
127
+ className: cn(
128
+ "flex cursor-pointer items-center gap-2 px-3 py-1.5 text-sm pointer-coarse:min-h-11",
129
+ isActive ? "bg-accent text-accent-foreground" : "hover:bg-accent/50"
130
+ ),
131
+ onMouseEnter: () => setSelectedIndex(index),
132
+ onClick: () => onSelect(item),
133
+ "data-testid": prefixedTestId(itemTestIdPrefix, item.id, {
134
+ testId: `mention-item-${item.id}`
135
+ }),
136
+ children: [
137
+ /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
138
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 font-medium text-foreground", children: item.label }),
139
+ item.kind === "assistant" && /* @__PURE__ */ jsx("span", { className: "shrink-0 rounded bg-muted px-1 text-[10px] font-medium uppercase text-muted-foreground", children: l.assistantBadge }),
140
+ item.description && /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: item.description })
141
+ ]
142
+ },
143
+ item.id
144
+ );
145
+ })
146
+ }
147
+ )
148
+ ]
149
+ }
150
+ );
151
+ });
152
+ function MentionChip({
153
+ label,
154
+ kind,
155
+ id,
156
+ isSelf,
157
+ labels,
158
+ className,
159
+ testId
160
+ }) {
161
+ const l = { ...DEFAULT_MENTION_LABELS, ...labels };
162
+ return /* @__PURE__ */ jsxs(
163
+ "span",
164
+ {
165
+ "data-slot": "mention-chip",
166
+ "data-mention-kind": kind,
167
+ "data-mention-id": id,
168
+ title: l.chipTitle(label, kind),
169
+ className: cn(
170
+ "inline-flex items-baseline rounded px-1 font-medium",
171
+ isSelf ? "bg-primary/15 text-primary" : "bg-accent text-accent-foreground",
172
+ className
173
+ ),
174
+ "data-testid": testIdOr(testId, { testId: `mention-chip-${id ?? kind}` }),
175
+ children: [
176
+ "@",
177
+ label
178
+ ]
179
+ }
180
+ );
181
+ }
182
+
183
+ // src/chat/mentions.ts
184
+ var MAX_QUERY_LENGTH = 40;
185
+ function findMentionQuery(text, caret) {
186
+ const before = text.slice(0, caret);
187
+ const at = before.lastIndexOf("@");
188
+ if (at < 0) return null;
189
+ if (at > 0 && !/\s/.test(before[at - 1])) return null;
190
+ const query = before.slice(at + 1);
191
+ if (query.length > MAX_QUERY_LENGTH || query.includes("\n") || query.startsWith(" ")) return null;
192
+ return { start: at, query };
193
+ }
194
+ function filterMentionCandidates(candidates, query) {
195
+ const q = query.trim().toLowerCase();
196
+ if (!q) return candidates.slice();
197
+ const scored = [];
198
+ for (const c of candidates) {
199
+ const label = c.label.toLowerCase();
200
+ if (label.startsWith(q)) scored.push({ c, score: 0 });
201
+ else if (label.split(/\s+/).some((word) => word.startsWith(q))) scored.push({ c, score: 1 });
202
+ else if (c.description?.toLowerCase().startsWith(q)) scored.push({ c, score: 2 });
203
+ }
204
+ return scored.sort((a, b) => a.score - b.score).map((s) => s.c);
205
+ }
206
+ function insertMention(text, start, caret, label) {
207
+ const inserted = `@${label} `;
208
+ return {
209
+ text: text.slice(0, start) + inserted + text.slice(caret),
210
+ caret: start + inserted.length
211
+ };
212
+ }
213
+ function tokenRanges(text, label) {
214
+ const needle = `@${label}`;
215
+ const ranges = [];
216
+ let from = 0;
217
+ for (; ; ) {
218
+ const i = text.indexOf(needle, from);
219
+ if (i < 0) break;
220
+ const end = i + needle.length;
221
+ const startsToken = i === 0 || /\s|\(|\[/.test(text[i - 1]);
222
+ const endsToken = end === text.length || !/[\p{L}\p{N}_]/u.test(text[end]);
223
+ if (startsToken && endsToken) ranges.push([i, end]);
224
+ from = end;
225
+ }
226
+ return ranges;
227
+ }
228
+ function extractMentions(text, candidates) {
229
+ const taken = [];
230
+ const found = [];
231
+ const byLength = [...candidates].sort((a, b) => b.label.length - a.label.length);
232
+ for (const c of byLength) {
233
+ if (!c.label) continue;
234
+ for (const [from, to] of tokenRanges(text, c.label)) {
235
+ if (taken.some(([a, b]) => from < b && to > a)) continue;
236
+ taken.push([from, to]);
237
+ if (!found.some((f) => f.c.id === c.id)) found.push({ c, at: from });
238
+ }
239
+ }
240
+ return found.sort((a, b) => a.at - b.at).map((f) => f.c);
241
+ }
242
+ function toMessageMentions(mentioned) {
243
+ return mentioned.map(
244
+ (c) => c.kind === "assistant" ? { type: "assistant" } : { type: "user", user_ext_id: c.id }
245
+ );
246
+ }
247
+ var MENTION_HREF_PREFIX = "#mention-";
248
+ function escapeLinkText(label) {
249
+ return label.replace(/[[\]\\]/g, (ch) => `\\${ch}`);
250
+ }
251
+ function linkMentions(content, mentions, assistantLabels = []) {
252
+ if (!mentions || mentions.length === 0 || !content) return content;
253
+ const targets = [];
254
+ for (const m of mentions) {
255
+ const id = m.type === "assistant" ? "assistant" : m.user_ext_id;
256
+ if (!id) continue;
257
+ const labels = m.type === "assistant" ? [m.label ?? "", ...assistantLabels] : [m.label ?? ""];
258
+ for (const label of labels) if (label) targets.push({ label, id });
259
+ }
260
+ targets.sort((a, b) => b.label.length - a.label.length);
261
+ const replacements = [];
262
+ for (const { label, id } of targets) {
263
+ for (const [from, to] of tokenRanges(content, label)) {
264
+ if (replacements.some((r) => from < r.to && to > r.from)) continue;
265
+ replacements.push({
266
+ from,
267
+ to,
268
+ text: `[@${escapeLinkText(label)}](${MENTION_HREF_PREFIX}${id})`
269
+ });
270
+ }
271
+ }
272
+ replacements.sort((a, b) => b.from - a.from);
273
+ let out = content;
274
+ for (const r of replacements) out = out.slice(0, r.from) + r.text + out.slice(r.to);
275
+ return out;
276
+ }
277
+ function mentionFromHref(href) {
278
+ if (!href?.startsWith(MENTION_HREF_PREFIX)) return null;
279
+ const id = href.slice(MENTION_HREF_PREFIX.length);
280
+ if (!id) return null;
281
+ return { kind: id === "assistant" ? "assistant" : "user", id };
282
+ }
283
+
284
+ export { DEFAULT_MENTION_LABELS, MENTION_HREF_PREFIX, MentionChip, MentionMenu, extractMentions, filterMentionCandidates, findMentionQuery, insertMention, linkMentions, listOptionId, mentionFromHref, toMessageMentions, useListKeyNav };
285
+ //# sourceMappingURL=chunk-AA3PT6KR.js.map
286
+ //# sourceMappingURL=chunk-AA3PT6KR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/useListKeyNav.ts","../src/chat/MentionMenu.tsx","../src/chat/mentions.ts"],"names":["MentionMenu","useId","useEffect"],"mappings":"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","file":"chunk-AA3PT6KR.js","sourcesContent":["import { useCallback, useEffect, useId, useState, type KeyboardEvent } from 'react'\n\n/** Id of the option at `index` in the listbox `listboxId`. Rows and the combobox agree on it. */\nexport function listOptionId(listboxId: string, index: number): string {\n return `${listboxId}-option-${index}`\n}\n\nexport interface UseListKeyNavOptions<T> {\n /** Whether the list is currently shown. Keys fall through when false. */\n open: boolean\n items: ReadonlyArray<T>\n /** Enter / Tab on the highlighted row. */\n onPick: (item: T) => void\n /** Escape. */\n onDismiss?: () => void\n /** Changing this resets the highlight to the first row (e.g. the filter query). */\n resetKey?: unknown\n}\n\n/** ARIA attributes that make the text field the combobox that owns the list. */\nexport interface ListComboboxProps {\n role: 'combobox'\n 'aria-autocomplete': 'list'\n 'aria-haspopup': 'listbox'\n 'aria-expanded': true\n 'aria-controls': string\n 'aria-activedescendant': string\n}\n\nexport interface UseListKeyNav {\n /** Pass to the list (`listboxId`) so the field's `aria-controls` points at it. */\n listboxId: string\n /**\n * Spread onto the text field (`<input>` or `<textarea>`). Empty while the list is closed, so a\n * prompt that is usually just a text box is not announced as a collapsed combobox; it becomes\n * the combobox (expanded, with its active option) the moment the list opens.\n */\n comboboxProps: ListComboboxProps | Record<string, never>\n selectedIndex: number\n setSelectedIndex: (index: number) => void\n /** Call first in the textarea `onKeyDown`. Returns true when the key was consumed. */\n handleKeyDown: (event: KeyboardEvent<HTMLElement>) => boolean\n}\n\n/**\n * Headless keyboard navigation for an autocomplete list that sits over a text field:\n * ArrowUp/ArrowDown wrap, Enter/Tab pick, Escape dismisses. Only consumes keys while open.\n */\nexport function useListKeyNav<T>({\n open,\n items,\n onPick,\n onDismiss,\n resetKey,\n}: UseListKeyNavOptions<T>): UseListKeyNav {\n const [selectedIndex, setSelectedIndex] = useState(0)\n const listboxId = useId()\n const isExpanded = open && items.length > 0\n\n useEffect(() => {\n setSelectedIndex(0)\n }, [resetKey, items.length])\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLElement>): boolean => {\n if (!open || items.length === 0) return false\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n setSelectedIndex((i) => (i + 1) % items.length)\n return true\n case 'ArrowUp':\n event.preventDefault()\n setSelectedIndex((i) => (i - 1 + items.length) % items.length)\n return true\n case 'Enter':\n case 'Tab': {\n event.preventDefault()\n const pick = items[selectedIndex] ?? items[0]\n if (pick !== undefined) onPick(pick)\n return true\n }\n case 'Escape':\n event.preventDefault()\n onDismiss?.()\n return true\n default:\n return false\n }\n },\n [open, items, selectedIndex, onPick, onDismiss]\n )\n\n const comboboxProps: UseListKeyNav['comboboxProps'] = isExpanded\n ? {\n role: 'combobox',\n 'aria-autocomplete': 'list',\n 'aria-haspopup': 'listbox',\n 'aria-expanded': true,\n 'aria-controls': listboxId,\n 'aria-activedescendant': listOptionId(listboxId, selectedIndex),\n }\n : {}\n\n return { listboxId, comboboxProps, selectedIndex, setSelectedIndex, handleKeyDown }\n}\n","import { memo, useEffect, useId, useRef } from 'react'\nimport { AtSign, Bot } from 'lucide-react'\nimport { cn } from '@arbidocs/react/ui'\nimport { listOptionId } from '../lib/useListKeyNav'\nimport type { MentionCandidate, MentionKind } from './mentions'\nimport { prefixedTestId, testIdOr } from './test-id'\n\n/** Every fixed string of the mention menu and chips. Pass a partial `labels` to reword. */\nexport interface MentionLabels {\n hint: string\n /** Accessible name of the list. */\n list: string\n /** Polite count announced as the menu opens or narrows. */\n resultCount: (count: number) => string\n /** Badge next to the assistant row. */\n assistantBadge: string\n /** Chip tooltip: who a mention points at. */\n chipTitle: (label: string, kind: MentionKind) => string\n}\n\nexport const DEFAULT_MENTION_LABELS: MentionLabels = {\n hint: 'Enter or Tab to mention, Esc to dismiss',\n list: 'People and assistant to mention',\n resultCount: (count) => `${count} ${count === 1 ? 'match' : 'matches'}`,\n assistantBadge: 'AI',\n chipTitle: (label, kind) => (kind === 'assistant' ? `${label} (AI assistant)` : label),\n}\n\nexport interface MentionMenuProps {\n open: boolean\n /** The chat composer sits at the bottom (menu above); a top-of-page field opens below. */\n placement?: 'above' | 'below'\n items: ReadonlyArray<MentionCandidate>\n /** Highlighted row (see `useListKeyNav`). */\n selectedIndex: number\n setSelectedIndex: (i: number) => void\n /** Listbox id from `useListKeyNav`, so the textarea's `aria-controls` points here. */\n listboxId?: string\n onSelect: (item: MentionCandidate) => void\n className?: string\n labels?: Partial<MentionLabels>\n /** Test id of the menu (default `mention-menu`). */\n testId?: string\n /** Row prefix (`<prefix>-<id>`); default `mention-item`. */\n itemTestIdPrefix?: string\n}\n\n/**\n * `@mention` autocomplete that floats over the composer. Presentational: the host (or\n * {@link useMentionAutocomplete}) owns the query, the candidates and what picking does.\n */\nexport const MentionMenu = memo(function MentionMenu({\n open,\n placement = 'above',\n items,\n selectedIndex,\n setSelectedIndex,\n listboxId: listboxIdProp,\n onSelect,\n className,\n labels,\n testId,\n itemTestIdPrefix,\n}: MentionMenuProps) {\n const l = { ...DEFAULT_MENTION_LABELS, ...labels }\n const ownId = useId()\n const listboxId = listboxIdProp ?? ownId\n const scrollRef = useRef<HTMLDivElement>(null)\n useEffect(() => {\n if (!open) return\n const el = scrollRef.current?.querySelector<HTMLElement>(\n `[data-mention-index=\"${selectedIndex}\"]`\n )\n el?.scrollIntoView?.({ block: 'nearest' })\n }, [open, selectedIndex])\n\n if (!open || items.length === 0) return null\n\n return (\n <div\n data-slot=\"mention-menu\"\n className={cn(\n 'absolute left-0 right-0 z-30 max-h-64 overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-lg',\n placement === 'above' ? 'bottom-full mb-2' : 'top-full mt-2',\n className\n )}\n // mousedown would blur the textarea (closing the menu before the click lands).\n onMouseDown={(e) => e.preventDefault()}\n data-testid={testIdOr(testId, { testId: 'mention-menu' })}\n >\n <div className=\"border-b border-border/50 px-3 py-1.5 text-xs text-muted-foreground\">\n {l.hint}\n </div>\n <div className=\"sr-only\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n {l.resultCount(items.length)}\n </div>\n <div\n ref={scrollRef}\n id={listboxId}\n role=\"listbox\"\n aria-label={l.list}\n className=\"scrollbar-subtle max-h-56 overflow-y-auto py-1\"\n >\n {items.map((item, index) => {\n const isActive = index === selectedIndex\n const Icon = item.kind === 'assistant' ? Bot : AtSign\n return (\n <div\n key={item.id}\n id={listOptionId(listboxId, index)}\n role=\"option\"\n aria-selected={isActive}\n data-mention-index={index}\n className={cn(\n 'flex cursor-pointer items-center gap-2 px-3 py-1.5 text-sm pointer-coarse:min-h-11',\n isActive ? 'bg-accent text-accent-foreground' : 'hover:bg-accent/50'\n )}\n onMouseEnter={() => setSelectedIndex(index)}\n onClick={() => onSelect(item)}\n data-testid={prefixedTestId(itemTestIdPrefix, item.id, {\n testId: `mention-item-${item.id}`,\n })}\n >\n <Icon className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n <span className=\"shrink-0 font-medium text-foreground\">{item.label}</span>\n {item.kind === 'assistant' && (\n <span className=\"shrink-0 rounded bg-muted px-1 text-[10px] font-medium uppercase text-muted-foreground\">\n {l.assistantBadge}\n </span>\n )}\n {item.description && (\n <span className=\"truncate text-xs text-muted-foreground\">{item.description}</span>\n )}\n </div>\n )\n })}\n </div>\n </div>\n )\n})\n\nexport interface MentionChipProps {\n /** Text shown after the `@`. */\n label: string\n kind: MentionKind\n /** Mentioned user's id (or `assistant`); lands on `data-mention-id`. */\n id?: string\n /** Highlights a mention of the person reading. */\n isSelf?: boolean\n labels?: Partial<MentionLabels>\n className?: string\n /** Test id; default `mention-chip-<id>`. */\n testId?: string\n}\n\n/** An `@mention` inside message text. Inline, so it sits in a markdown paragraph. */\nexport function MentionChip({\n label,\n kind,\n id,\n isSelf,\n labels,\n className,\n testId,\n}: MentionChipProps) {\n const l = { ...DEFAULT_MENTION_LABELS, ...labels }\n return (\n <span\n data-slot=\"mention-chip\"\n data-mention-kind={kind}\n data-mention-id={id}\n title={l.chipTitle(label, kind)}\n className={cn(\n 'inline-flex items-baseline rounded px-1 font-medium',\n isSelf ? 'bg-primary/15 text-primary' : 'bg-accent text-accent-foreground',\n className\n )}\n data-testid={testIdOr(testId, { testId: `mention-chip-${id ?? kind}` })}\n >\n @{label}\n </span>\n )\n}\n","/**\n * @mentions in chat text: pure helpers shared by the composer (autocomplete, extraction) and\n * the message renderer (chips). No React, no SDK.\n *\n * A mention lives in two places: the readable `@Label` text in the message content, and a\n * structured entry on the message (`{ type, user_ext_id, label }`) that the server reads to share\n * the conversation and notify. The composer only ever inserts `@<label>` for a known candidate,\n * so the structured list can be recovered from the text at send time ({@link extractMentions});\n * the renderer turns each stored mention back into a chip ({@link linkMentions}).\n */\n\n/** Who can be mentioned: a workspace member, or the assistant. */\nexport type MentionKind = 'user' | 'assistant'\n\n/** One entry of the mention autocomplete. */\nexport interface MentionCandidate {\n /** User external id (`usr-…`) for a person; any stable key (e.g. `assistant`) for the assistant. */\n id: string\n kind: MentionKind\n /** Inserted as `@<label>`. For people this must be the server's display name (given + family). */\n label: string\n /** Secondary line in the menu (email, role, …). */\n description?: string\n}\n\n/** The structured mention a message carries (the API's `MessageMention`). */\nexport interface MessageMentionRef {\n type: MentionKind\n user_ext_id?: string | null\n label?: string | null\n}\n\n/** Longest text after `@` that still counts as a query in progress. */\nconst MAX_QUERY_LENGTH = 40\n\n/**\n * The `@query` the caret is in, if any: the `@` must start the text or follow whitespace, and the\n * query may contain spaces (names do) but no newline. Returns the index of the `@` and the query.\n */\nexport function findMentionQuery(\n text: string,\n caret: number\n): { start: number; query: string } | null {\n const before = text.slice(0, caret)\n const at = before.lastIndexOf('@')\n if (at < 0) return null\n if (at > 0 && !/\\s/.test(before[at - 1])) return null\n const query = before.slice(at + 1)\n if (query.length > MAX_QUERY_LENGTH || query.includes('\\n') || query.startsWith(' ')) return null\n return { start: at, query }\n}\n\n/** Candidates matching a query: label prefix (or word-prefix) matches, prefix matches first. */\nexport function filterMentionCandidates(\n candidates: ReadonlyArray<MentionCandidate>,\n query: string\n): MentionCandidate[] {\n const q = query.trim().toLowerCase()\n if (!q) return candidates.slice()\n const scored: Array<{ c: MentionCandidate; score: number }> = []\n for (const c of candidates) {\n const label = c.label.toLowerCase()\n if (label.startsWith(q)) scored.push({ c, score: 0 })\n else if (label.split(/\\s+/).some((word) => word.startsWith(q))) scored.push({ c, score: 1 })\n else if (c.description?.toLowerCase().startsWith(q)) scored.push({ c, score: 2 })\n }\n return scored.sort((a, b) => a.score - b.score).map((s) => s.c)\n}\n\n/** Replace the `@query` at `start…caret` with `@<label> `; returns the new text and caret. */\nexport function insertMention(\n text: string,\n start: number,\n caret: number,\n label: string\n): { text: string; caret: number } {\n const inserted = `@${label} `\n return {\n text: text.slice(0, start) + inserted + text.slice(caret),\n caret: start + inserted.length,\n }\n}\n\n/** Every `[from, to)` range where `@<label>` occurs as a whole token. */\nfunction tokenRanges(text: string, label: string): Array<[number, number]> {\n const needle = `@${label}`\n const ranges: Array<[number, number]> = []\n let from = 0\n for (;;) {\n const i = text.indexOf(needle, from)\n if (i < 0) break\n const end = i + needle.length\n const startsToken = i === 0 || /\\s|\\(|\\[/.test(text[i - 1])\n const endsToken = end === text.length || !/[\\p{L}\\p{N}_]/u.test(text[end])\n if (startsToken && endsToken) ranges.push([i, end])\n from = end\n }\n return ranges\n}\n\n/**\n * The candidates mentioned in `text`, in order of first appearance. Longer labels win overlaps,\n * so `@Bob Smith` is Bob Smith, not a separate `@Bob`.\n */\nexport function extractMentions(\n text: string,\n candidates: ReadonlyArray<MentionCandidate>\n): MentionCandidate[] {\n const taken: Array<[number, number]> = []\n const found: Array<{ c: MentionCandidate; at: number }> = []\n const byLength = [...candidates].sort((a, b) => b.label.length - a.label.length)\n for (const c of byLength) {\n if (!c.label) continue\n for (const [from, to] of tokenRanges(text, c.label)) {\n if (taken.some(([a, b]) => from < b && to > a)) continue\n taken.push([from, to])\n if (!found.some((f) => f.c.id === c.id)) found.push({ c, at: from })\n }\n }\n return found.sort((a, b) => a.at - b.at).map((f) => f.c)\n}\n\n/** The API payload for mentioned candidates. */\nexport function toMessageMentions(mentioned: ReadonlyArray<MentionCandidate>): MessageMentionRef[] {\n return mentioned.map((c) =>\n c.kind === 'assistant' ? { type: 'assistant' } : { type: 'user', user_ext_id: c.id }\n )\n}\n\n/** Fragment prefix of the links {@link linkMentions} writes. */\nexport const MENTION_HREF_PREFIX = '#mention-'\n\n/** Escape the characters that would end a markdown link text early. */\nfunction escapeLinkText(label: string): string {\n return label.replace(/[[\\]\\\\]/g, (ch) => `\\\\${ch}`)\n}\n\n/**\n * Turn each stored mention's `@<label>` in `content` into a markdown link\n * `[@<label>](#mention-<id>)`, so a markdown renderer can draw it as a chip (see\n * {@link mentionFromHref}). Text that no longer matches (a renamed user) stays plain text.\n * `assistantLabels` lists other names the assistant may have been mentioned by.\n */\nexport function linkMentions(\n content: string,\n mentions: ReadonlyArray<MessageMentionRef> | null | undefined,\n assistantLabels: ReadonlyArray<string> = []\n): string {\n if (!mentions || mentions.length === 0 || !content) return content\n const targets: Array<{ label: string; id: string }> = []\n for (const m of mentions) {\n const id = m.type === 'assistant' ? 'assistant' : m.user_ext_id\n if (!id) continue\n const labels = m.type === 'assistant' ? [m.label ?? '', ...assistantLabels] : [m.label ?? '']\n for (const label of labels) if (label) targets.push({ label, id })\n }\n targets.sort((a, b) => b.label.length - a.label.length)\n const replacements: Array<{ from: number; to: number; text: string }> = []\n for (const { label, id } of targets) {\n for (const [from, to] of tokenRanges(content, label)) {\n if (replacements.some((r) => from < r.to && to > r.from)) continue\n replacements.push({\n from,\n to,\n text: `[@${escapeLinkText(label)}](${MENTION_HREF_PREFIX}${id})`,\n })\n }\n }\n replacements.sort((a, b) => b.from - a.from)\n let out = content\n for (const r of replacements) out = out.slice(0, r.from) + r.text + out.slice(r.to)\n return out\n}\n\n/** The mention a `#mention-…` href points at, or null for any other href. */\nexport function mentionFromHref(\n href: string | null | undefined\n): { kind: MentionKind; id: string } | null {\n if (!href?.startsWith(MENTION_HREF_PREFIX)) return null\n const id = href.slice(MENTION_HREF_PREFIX.length)\n if (!id) return null\n return { kind: id === 'assistant' ? 'assistant' : 'user', id }\n}\n"]}
@@ -1,69 +1,15 @@
1
1
  "use client";
2
+ import { useAssistantDock, useChatSurface } from './chunk-JLPYOPCX.js';
2
3
  import { Toaster } from './chunk-BV7FEMSI.js';
3
- import { useAssistantDock, useChatSurface } from './chunk-UVIOQUJA.js';
4
4
  import { BlocksTooltipProvider } from './chunk-KZJAG7T3.js';
5
+ import { listOptionId, findMentionQuery, filterMentionCandidates, insertMention, useListKeyNav } from './chunk-AA3PT6KR.js';
5
6
  import { prefixedTestId, testIdOr } from './chunk-7F6CBP3Z.js';
6
- import { memo, useId, useRef, useEffect, forwardRef, useState, useCallback, useMemo, Fragment as Fragment$1 } from 'react';
7
- import { Globe, BookOpen, Bot, AtSign, ChevronsUpDown, Search, SquareSlash, MessagesSquare, Pin, X, Network, ArrowLeft, ArrowRight, Crown, Glasses, Zap, Sparkles, Check, Settings, Plus, Download, MessageCircleQuestion, Clock, Play, Pause, Loader2, MessageSquare, CheckCircle, Tag, User } from 'lucide-react';
7
+ import { memo, useId, useRef, useEffect, forwardRef, useState, useMemo, useCallback, Fragment as Fragment$1 } from 'react';
8
+ import { Globe, BookOpen, ChevronsUpDown, Search, SquareSlash, MessagesSquare, Bot, Pin, X, Network, ArrowLeft, ArrowRight, Crown, Glasses, Zap, Sparkles, Check, Settings, Plus, Download, MessageCircleQuestion, Clock, Play, Pause, Loader2, MessageSquare, CheckCircle, Tag, User } from 'lucide-react';
8
9
  import { cn, Button, HoverCardContent, Input, Badge, Tooltip, TooltipTrigger, TooltipContent, Popover, PopoverTrigger, PopoverContent, PassageCard, Tabs, TabsList, TabsTrigger, TabsContent } from '@arbidocs/react/ui';
9
10
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
10
11
  import DOMPurify from 'dompurify';
11
12
 
12
- function listOptionId(listboxId, index) {
13
- return `${listboxId}-option-${index}`;
14
- }
15
- function useListKeyNav({
16
- open,
17
- items,
18
- onPick,
19
- onDismiss,
20
- resetKey
21
- }) {
22
- const [selectedIndex, setSelectedIndex] = useState(0);
23
- const listboxId = useId();
24
- const isExpanded = open && items.length > 0;
25
- useEffect(() => {
26
- setSelectedIndex(0);
27
- }, [resetKey, items.length]);
28
- const handleKeyDown = useCallback(
29
- (event) => {
30
- if (!open || items.length === 0) return false;
31
- switch (event.key) {
32
- case "ArrowDown":
33
- event.preventDefault();
34
- setSelectedIndex((i) => (i + 1) % items.length);
35
- return true;
36
- case "ArrowUp":
37
- event.preventDefault();
38
- setSelectedIndex((i) => (i - 1 + items.length) % items.length);
39
- return true;
40
- case "Enter":
41
- case "Tab": {
42
- event.preventDefault();
43
- const pick = items[selectedIndex] ?? items[0];
44
- if (pick !== void 0) onPick(pick);
45
- return true;
46
- }
47
- case "Escape":
48
- event.preventDefault();
49
- onDismiss?.();
50
- return true;
51
- default:
52
- return false;
53
- }
54
- },
55
- [open, items, selectedIndex, onPick, onDismiss]
56
- );
57
- const comboboxProps = isExpanded ? {
58
- role: "combobox",
59
- "aria-autocomplete": "list",
60
- "aria-haspopup": "listbox",
61
- "aria-expanded": true,
62
- "aria-controls": listboxId,
63
- "aria-activedescendant": listOptionId(listboxId, selectedIndex)
64
- } : {};
65
- return { listboxId, comboboxProps, selectedIndex, setSelectedIndex, handleKeyDown };
66
- }
67
13
  var DEFAULT_SLASH_MENU_LABELS = {
68
14
  hint: "Enter or Tab to insert, Esc to dismiss",
69
15
  commands: "Commands",
@@ -221,226 +167,6 @@ var SlashMenu = memo(function SlashMenu2({
221
167
  }
222
168
  );
223
169
  });
224
- var DEFAULT_MENTION_LABELS = {
225
- hint: "Enter or Tab to mention, Esc to dismiss",
226
- list: "People and assistant to mention",
227
- resultCount: (count) => `${count} ${count === 1 ? "match" : "matches"}`,
228
- assistantBadge: "AI",
229
- chipTitle: (label, kind) => kind === "assistant" ? `${label} (AI assistant)` : label
230
- };
231
- var MentionMenu = memo(function MentionMenu2({
232
- open,
233
- placement = "above",
234
- items,
235
- selectedIndex,
236
- setSelectedIndex,
237
- listboxId: listboxIdProp,
238
- onSelect,
239
- className,
240
- labels,
241
- testId,
242
- itemTestIdPrefix
243
- }) {
244
- const l = { ...DEFAULT_MENTION_LABELS, ...labels };
245
- const ownId = useId();
246
- const listboxId = listboxIdProp ?? ownId;
247
- const scrollRef = useRef(null);
248
- useEffect(() => {
249
- if (!open) return;
250
- const el = scrollRef.current?.querySelector(
251
- `[data-mention-index="${selectedIndex}"]`
252
- );
253
- el?.scrollIntoView?.({ block: "nearest" });
254
- }, [open, selectedIndex]);
255
- if (!open || items.length === 0) return null;
256
- return /* @__PURE__ */ jsxs(
257
- "div",
258
- {
259
- "data-slot": "mention-menu",
260
- className: cn(
261
- "absolute left-0 right-0 z-30 max-h-64 overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-lg",
262
- placement === "above" ? "bottom-full mb-2" : "top-full mt-2",
263
- className
264
- ),
265
- onMouseDown: (e) => e.preventDefault(),
266
- "data-testid": testIdOr(testId, { testId: "mention-menu" }),
267
- children: [
268
- /* @__PURE__ */ jsx("div", { className: "border-b border-border/50 px-3 py-1.5 text-xs text-muted-foreground", children: l.hint }),
269
- /* @__PURE__ */ jsx("div", { className: "sr-only", role: "status", "aria-live": "polite", "aria-atomic": "true", children: l.resultCount(items.length) }),
270
- /* @__PURE__ */ jsx(
271
- "div",
272
- {
273
- ref: scrollRef,
274
- id: listboxId,
275
- role: "listbox",
276
- "aria-label": l.list,
277
- className: "scrollbar-subtle max-h-56 overflow-y-auto py-1",
278
- children: items.map((item, index) => {
279
- const isActive = index === selectedIndex;
280
- const Icon = item.kind === "assistant" ? Bot : AtSign;
281
- return /* @__PURE__ */ jsxs(
282
- "div",
283
- {
284
- id: listOptionId(listboxId, index),
285
- role: "option",
286
- "aria-selected": isActive,
287
- "data-mention-index": index,
288
- className: cn(
289
- "flex cursor-pointer items-center gap-2 px-3 py-1.5 text-sm pointer-coarse:min-h-11",
290
- isActive ? "bg-accent text-accent-foreground" : "hover:bg-accent/50"
291
- ),
292
- onMouseEnter: () => setSelectedIndex(index),
293
- onClick: () => onSelect(item),
294
- "data-testid": prefixedTestId(itemTestIdPrefix, item.id, {
295
- testId: `mention-item-${item.id}`
296
- }),
297
- children: [
298
- /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
299
- /* @__PURE__ */ jsx("span", { className: "shrink-0 font-medium text-foreground", children: item.label }),
300
- item.kind === "assistant" && /* @__PURE__ */ jsx("span", { className: "shrink-0 rounded bg-muted px-1 text-[10px] font-medium uppercase text-muted-foreground", children: l.assistantBadge }),
301
- item.description && /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: item.description })
302
- ]
303
- },
304
- item.id
305
- );
306
- })
307
- }
308
- )
309
- ]
310
- }
311
- );
312
- });
313
- function MentionChip({
314
- label,
315
- kind,
316
- id,
317
- isSelf,
318
- labels,
319
- className,
320
- testId
321
- }) {
322
- const l = { ...DEFAULT_MENTION_LABELS, ...labels };
323
- return /* @__PURE__ */ jsxs(
324
- "span",
325
- {
326
- "data-slot": "mention-chip",
327
- "data-mention-kind": kind,
328
- "data-mention-id": id,
329
- title: l.chipTitle(label, kind),
330
- className: cn(
331
- "inline-flex items-baseline rounded px-1 font-medium",
332
- isSelf ? "bg-primary/15 text-primary" : "bg-accent text-accent-foreground",
333
- className
334
- ),
335
- "data-testid": testIdOr(testId, { testId: `mention-chip-${id ?? kind}` }),
336
- children: [
337
- "@",
338
- label
339
- ]
340
- }
341
- );
342
- }
343
-
344
- // src/chat/mentions.ts
345
- var MAX_QUERY_LENGTH = 40;
346
- function findMentionQuery(text, caret) {
347
- const before = text.slice(0, caret);
348
- const at = before.lastIndexOf("@");
349
- if (at < 0) return null;
350
- if (at > 0 && !/\s/.test(before[at - 1])) return null;
351
- const query = before.slice(at + 1);
352
- if (query.length > MAX_QUERY_LENGTH || query.includes("\n") || query.startsWith(" ")) return null;
353
- return { start: at, query };
354
- }
355
- function filterMentionCandidates(candidates, query) {
356
- const q = query.trim().toLowerCase();
357
- if (!q) return candidates.slice();
358
- const scored = [];
359
- for (const c of candidates) {
360
- const label = c.label.toLowerCase();
361
- if (label.startsWith(q)) scored.push({ c, score: 0 });
362
- else if (label.split(/\s+/).some((word) => word.startsWith(q))) scored.push({ c, score: 1 });
363
- else if (c.description?.toLowerCase().startsWith(q)) scored.push({ c, score: 2 });
364
- }
365
- return scored.sort((a, b) => a.score - b.score).map((s) => s.c);
366
- }
367
- function insertMention(text, start, caret, label) {
368
- const inserted = `@${label} `;
369
- return {
370
- text: text.slice(0, start) + inserted + text.slice(caret),
371
- caret: start + inserted.length
372
- };
373
- }
374
- function tokenRanges(text, label) {
375
- const needle = `@${label}`;
376
- const ranges = [];
377
- let from = 0;
378
- for (; ; ) {
379
- const i = text.indexOf(needle, from);
380
- if (i < 0) break;
381
- const end = i + needle.length;
382
- const startsToken = i === 0 || /\s|\(|\[/.test(text[i - 1]);
383
- const endsToken = end === text.length || !/[\p{L}\p{N}_]/u.test(text[end]);
384
- if (startsToken && endsToken) ranges.push([i, end]);
385
- from = end;
386
- }
387
- return ranges;
388
- }
389
- function extractMentions(text, candidates) {
390
- const taken = [];
391
- const found = [];
392
- const byLength = [...candidates].sort((a, b) => b.label.length - a.label.length);
393
- for (const c of byLength) {
394
- if (!c.label) continue;
395
- for (const [from, to] of tokenRanges(text, c.label)) {
396
- if (taken.some(([a, b]) => from < b && to > a)) continue;
397
- taken.push([from, to]);
398
- if (!found.some((f) => f.c.id === c.id)) found.push({ c, at: from });
399
- }
400
- }
401
- return found.sort((a, b) => a.at - b.at).map((f) => f.c);
402
- }
403
- function toMessageMentions(mentioned) {
404
- return mentioned.map(
405
- (c) => c.kind === "assistant" ? { type: "assistant" } : { type: "user", user_ext_id: c.id }
406
- );
407
- }
408
- var MENTION_HREF_PREFIX = "#mention-";
409
- function escapeLinkText(label) {
410
- return label.replace(/[[\]\\]/g, (ch) => `\\${ch}`);
411
- }
412
- function linkMentions(content, mentions, assistantLabels = []) {
413
- if (!mentions || mentions.length === 0 || !content) return content;
414
- const targets = [];
415
- for (const m of mentions) {
416
- const id = m.type === "assistant" ? "assistant" : m.user_ext_id;
417
- if (!id) continue;
418
- const labels = m.type === "assistant" ? [m.label ?? "", ...assistantLabels] : [m.label ?? ""];
419
- for (const label of labels) if (label) targets.push({ label, id });
420
- }
421
- targets.sort((a, b) => b.label.length - a.label.length);
422
- const replacements = [];
423
- for (const { label, id } of targets) {
424
- for (const [from, to] of tokenRanges(content, label)) {
425
- if (replacements.some((r) => from < r.to && to > r.from)) continue;
426
- replacements.push({
427
- from,
428
- to,
429
- text: `[@${escapeLinkText(label)}](${MENTION_HREF_PREFIX}${id})`
430
- });
431
- }
432
- }
433
- replacements.sort((a, b) => b.from - a.from);
434
- let out = content;
435
- for (const r of replacements) out = out.slice(0, r.from) + r.text + out.slice(r.to);
436
- return out;
437
- }
438
- function mentionFromHref(href) {
439
- if (!href?.startsWith(MENTION_HREF_PREFIX)) return null;
440
- const id = href.slice(MENTION_HREF_PREFIX.length);
441
- if (!id) return null;
442
- return { kind: id === "assistant" ? "assistant" : "user", id };
443
- }
444
170
  function useMentionAutocomplete({
445
171
  value,
446
172
  onValueChange,
@@ -1959,6 +1685,6 @@ function AssistantToaster(props) {
1959
1685
  return /* @__PURE__ */ jsx(Toaster, { ...props, dockOpen: dock.isOpen && isDockAvailable });
1960
1686
  }
1961
1687
 
1962
- export { AssistantModeToggle, AssistantToaster, BranchControls, ComposerAccessories, ComposerTools, ContextActionButton, ContextCheckRow, ContextPanelContent, ContextPanelFooter, ContextPanelTabs, ContextPickerList, ContextPickerRow, ContextSearchInput, ContextSelectList, ContextSelectRow, Conversation, ConversationBody, ConversationHeader, ConversationHeaderActions, ConversationHistoryPane, ConversationLoading, ConversationMain, ConversationRoot, ConversationSearchView, ConversationSettingsButton, DEFAULT_ASSISTANT_MODE_TOGGLE_LABELS, DEFAULT_BRANCH_CONTROLS_LABELS, DEFAULT_CONVERSATION_LAYOUT_LABELS, DEFAULT_CONVERSATION_PARTS_LABELS, DEFAULT_CONVERSATION_SEARCH_LABELS, DEFAULT_LABEL_PIN_ROW_LABELS, DEFAULT_MENTION_LABELS, DEFAULT_MODEL_PRICING_TIP_LABELS, DEFAULT_PENDING_ANSWER_BAR_LABELS, DEFAULT_PINNED_DOC_CHIP_LABELS, DEFAULT_SLASH_MENU_LABELS, DEFAULT_THREAD_MAP_LABELS, LabelPinRow, MENTION_HREF_PREFIX, MentionChip, MentionMenu, ModelAutoIcon, ModelConfigureButton, ModelDot, ModelMenuDivider, ModelMenuMessage, ModelOption, ModelOptionLabel, ModelPickerPopover, ModelPickerTrigger, ModelPricingTip, ModelTierIcon, ModelTierOption, ModelTriggerContent, NewChatButton, NewPaChatButton, PaModeBadge, PendingAnswerBar, PinnedDocChip, SlashMenu, ThreadMap, ThreadMapDiagram, ThreadMapError, ThreadMapLoading, extractMentions, filterMentionCandidates, findMentionQuery, insertMention, isLongPastedText, linkMentions, mentionFromHref, toMessageMentions, useDismissibleCard, useListKeyNav, useLongPaste, useMentionAutocomplete };
1963
- //# sourceMappingURL=chunk-QJEJBLHI.js.map
1964
- //# sourceMappingURL=chunk-QJEJBLHI.js.map
1688
+ export { AssistantModeToggle, AssistantToaster, BranchControls, ComposerAccessories, ComposerTools, ContextActionButton, ContextCheckRow, ContextPanelContent, ContextPanelFooter, ContextPanelTabs, ContextPickerList, ContextPickerRow, ContextSearchInput, ContextSelectList, ContextSelectRow, Conversation, ConversationBody, ConversationHeader, ConversationHeaderActions, ConversationHistoryPane, ConversationLoading, ConversationMain, ConversationRoot, ConversationSearchView, ConversationSettingsButton, DEFAULT_ASSISTANT_MODE_TOGGLE_LABELS, DEFAULT_BRANCH_CONTROLS_LABELS, DEFAULT_CONVERSATION_LAYOUT_LABELS, DEFAULT_CONVERSATION_PARTS_LABELS, DEFAULT_CONVERSATION_SEARCH_LABELS, DEFAULT_LABEL_PIN_ROW_LABELS, DEFAULT_MODEL_PRICING_TIP_LABELS, DEFAULT_PENDING_ANSWER_BAR_LABELS, DEFAULT_PINNED_DOC_CHIP_LABELS, DEFAULT_SLASH_MENU_LABELS, DEFAULT_THREAD_MAP_LABELS, LabelPinRow, ModelAutoIcon, ModelConfigureButton, ModelDot, ModelMenuDivider, ModelMenuMessage, ModelOption, ModelOptionLabel, ModelPickerPopover, ModelPickerTrigger, ModelPricingTip, ModelTierIcon, ModelTierOption, ModelTriggerContent, NewChatButton, NewPaChatButton, PaModeBadge, PendingAnswerBar, PinnedDocChip, SlashMenu, ThreadMap, ThreadMapDiagram, ThreadMapError, ThreadMapLoading, isLongPastedText, useDismissibleCard, useLongPaste, useMentionAutocomplete };
1689
+ //# sourceMappingURL=chunk-CQ6CYPV4.js.map
1690
+ //# sourceMappingURL=chunk-CQ6CYPV4.js.map