@tutti-os/ui-rich-text 0.0.53 → 0.0.54

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.
@@ -0,0 +1,2083 @@
1
+ import {
2
+ findMentionPaletteEntry,
3
+ flattenMentionPaletteEntries,
4
+ mentionStatusBadgeClassName,
5
+ moveMentionPaletteHighlight,
6
+ nextMentionPaletteCategory,
7
+ resolveMentionFileThumbnailUrl,
8
+ resolveMentionFileVisualKind,
9
+ selectedMentionPaletteItem
10
+ } from "./chunk-HMAUQU6Q.js";
11
+
12
+ // src/at-panel/MentionPalette.tsx
13
+ import {
14
+ useEffect as useEffect2,
15
+ useRef as useRef2,
16
+ useState as useState2
17
+ } from "react";
18
+ import {
19
+ FolderFailedFilledIcon,
20
+ KeyboardFilledIcon
21
+ } from "@tutti-os/ui-system/icons";
22
+ import { UnderlineTabs } from "@tutti-os/ui-system/components";
23
+ import { cn as cn2 } from "@tutti-os/ui-system/utils";
24
+
25
+ // src/at-panel/mentionPaletteScrollbar.tsx
26
+ import {
27
+ useCallback,
28
+ useEffect,
29
+ useRef,
30
+ useState
31
+ } from "react";
32
+ import { cn } from "@tutti-os/ui-system/utils";
33
+ import { jsx } from "react/jsx-runtime";
34
+ var MENTION_PALETTE_SCROLLBAR_MIN_THUMB_HEIGHT = 24;
35
+ var MENTION_PALETTE_SCROLLBAR_HIDDEN_STATE = {
36
+ scrollable: false,
37
+ thumbHeight: 0,
38
+ thumbTop: 0
39
+ };
40
+ function MentionPaletteScrollbar({
41
+ scrollBodyRef,
42
+ className,
43
+ thumbClassName,
44
+ testId
45
+ }) {
46
+ "use memo";
47
+ const trackRef = useRef(null);
48
+ const dragStateRef = useRef(null);
49
+ const [scrollbarState, setScrollbarState] = useState({
50
+ scrollable: false,
51
+ thumbHeight: 0,
52
+ thumbTop: 0
53
+ });
54
+ const [dragging, setDragging] = useState(false);
55
+ const hideScrollbar = useCallback(() => {
56
+ setScrollbarState(
57
+ (previous) => previous.scrollable || previous.thumbHeight !== 0 || previous.thumbTop !== 0 ? MENTION_PALETTE_SCROLLBAR_HIDDEN_STATE : previous
58
+ );
59
+ }, []);
60
+ const syncScrollbarState = useCallback(() => {
61
+ const contentElement = scrollBodyRef.current;
62
+ if (!contentElement) {
63
+ hideScrollbar();
64
+ return;
65
+ }
66
+ const { scrollHeight, scrollTop, clientHeight } = contentElement;
67
+ const measuredTrackHeight = trackRef.current?.clientHeight ?? 0;
68
+ const trackHeight = measuredTrackHeight > 0 ? measuredTrackHeight : clientHeight;
69
+ const maxScrollTop = Math.max(0, scrollHeight - clientHeight);
70
+ if (clientHeight <= 0 || trackHeight <= 0 || maxScrollTop <= 0) {
71
+ hideScrollbar();
72
+ return;
73
+ }
74
+ const thumbHeight = Math.max(
75
+ MENTION_PALETTE_SCROLLBAR_MIN_THUMB_HEIGHT,
76
+ Math.round(clientHeight / scrollHeight * trackHeight)
77
+ );
78
+ const maxThumbTop = Math.max(0, trackHeight - thumbHeight);
79
+ const thumbTop = Math.round(scrollTop / maxScrollTop * maxThumbTop);
80
+ setScrollbarState(
81
+ (previous) => previous.scrollable && previous.thumbHeight === thumbHeight && previous.thumbTop === thumbTop ? previous : { scrollable: true, thumbHeight, thumbTop }
82
+ );
83
+ }, [hideScrollbar, scrollBodyRef]);
84
+ useEffect(() => {
85
+ const contentElement = scrollBodyRef.current;
86
+ if (!contentElement) {
87
+ hideScrollbar();
88
+ return;
89
+ }
90
+ syncScrollbarState();
91
+ contentElement.addEventListener("scroll", syncScrollbarState, {
92
+ passive: true
93
+ });
94
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(syncScrollbarState) : null;
95
+ resizeObserver?.observe(contentElement);
96
+ if (trackRef.current) {
97
+ resizeObserver?.observe(trackRef.current);
98
+ }
99
+ const animationFrameId = window.requestAnimationFrame(syncScrollbarState);
100
+ return () => {
101
+ contentElement.removeEventListener("scroll", syncScrollbarState);
102
+ resizeObserver?.disconnect();
103
+ window.cancelAnimationFrame(animationFrameId);
104
+ };
105
+ }, [hideScrollbar, scrollBodyRef, syncScrollbarState]);
106
+ useEffect(() => {
107
+ if (!dragging) {
108
+ return;
109
+ }
110
+ const handleMouseMove = (event) => {
111
+ const contentElement = scrollBodyRef.current;
112
+ const dragState = dragStateRef.current;
113
+ if (!contentElement || !dragState || dragState.maxThumbTop <= 0) {
114
+ return;
115
+ }
116
+ const delta = event.clientY - dragState.startClientY;
117
+ const nextThumbTop = dragState.startScrollTop / dragState.maxScrollTop * dragState.maxThumbTop + delta;
118
+ contentElement.scrollTop = Math.min(Math.max(0, nextThumbTop), dragState.maxThumbTop) / dragState.maxThumbTop * dragState.maxScrollTop;
119
+ syncScrollbarState();
120
+ };
121
+ const handleMouseUp = () => {
122
+ dragStateRef.current = null;
123
+ setDragging(false);
124
+ };
125
+ window.addEventListener("mousemove", handleMouseMove);
126
+ window.addEventListener("mouseup", handleMouseUp);
127
+ return () => {
128
+ window.removeEventListener("mousemove", handleMouseMove);
129
+ window.removeEventListener("mouseup", handleMouseUp);
130
+ };
131
+ }, [dragging, scrollBodyRef, syncScrollbarState]);
132
+ const scrollContentToThumbTop = (thumbTop) => {
133
+ const contentElement = scrollBodyRef.current;
134
+ const trackElement = trackRef.current;
135
+ if (!contentElement || !trackElement) {
136
+ return;
137
+ }
138
+ const maxScrollTop = Math.max(
139
+ 0,
140
+ contentElement.scrollHeight - contentElement.clientHeight
141
+ );
142
+ const maxThumbTop = Math.max(
143
+ 0,
144
+ trackElement.clientHeight - scrollbarState.thumbHeight
145
+ );
146
+ if (maxScrollTop <= 0 || maxThumbTop <= 0) {
147
+ return;
148
+ }
149
+ contentElement.scrollTop = Math.min(Math.max(0, thumbTop), maxThumbTop) / maxThumbTop * maxScrollTop;
150
+ syncScrollbarState();
151
+ };
152
+ const handleTrackMouseDown = (event) => {
153
+ if (event.button !== 0 || !scrollbarState.scrollable || event.target !== event.currentTarget) {
154
+ return;
155
+ }
156
+ event.preventDefault();
157
+ const trackRect = event.currentTarget.getBoundingClientRect();
158
+ scrollContentToThumbTop(
159
+ event.clientY - trackRect.top - scrollbarState.thumbHeight / 2
160
+ );
161
+ };
162
+ const handleThumbMouseDown = (event) => {
163
+ if (event.button !== 0 || !scrollbarState.scrollable) {
164
+ return;
165
+ }
166
+ const contentElement = scrollBodyRef.current;
167
+ const trackElement = trackRef.current;
168
+ if (!contentElement || !trackElement) {
169
+ return;
170
+ }
171
+ event.preventDefault();
172
+ event.stopPropagation();
173
+ dragStateRef.current = {
174
+ maxScrollTop: Math.max(
175
+ 0,
176
+ contentElement.scrollHeight - contentElement.clientHeight
177
+ ),
178
+ maxThumbTop: Math.max(
179
+ 0,
180
+ trackElement.clientHeight - scrollbarState.thumbHeight
181
+ ),
182
+ startClientY: event.clientY,
183
+ startScrollTop: contentElement.scrollTop
184
+ };
185
+ setDragging(true);
186
+ };
187
+ if (!scrollbarState.scrollable && !dragging) {
188
+ return /* @__PURE__ */ jsx("div", { ref: trackRef, className: "hidden", "aria-hidden": "true" });
189
+ }
190
+ return /* @__PURE__ */ jsx(
191
+ "div",
192
+ {
193
+ ref: trackRef,
194
+ className: cn("group/status-scrollbar", className),
195
+ "data-scrollable": scrollbarState.scrollable ? "true" : "false",
196
+ "data-dragging": dragging ? "true" : "false",
197
+ "data-testid": testId,
198
+ "aria-hidden": "true",
199
+ onMouseDown: handleTrackMouseDown,
200
+ children: /* @__PURE__ */ jsx(
201
+ "div",
202
+ {
203
+ className: thumbClassName,
204
+ onMouseDown: handleThumbMouseDown,
205
+ style: {
206
+ height: `${scrollbarState.thumbHeight}px`,
207
+ transform: `translateY(${scrollbarState.thumbTop}px)`
208
+ }
209
+ }
210
+ )
211
+ }
212
+ );
213
+ }
214
+
215
+ // src/at-panel/MentionPalette.tsx
216
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
217
+ var DEFAULT_THEME = {
218
+ classNames: {
219
+ palette: "rich-text-at-mention-palette",
220
+ header: "rich-text-at-mention-palette-header",
221
+ footer: "rich-text-at-mention-palette-footer",
222
+ tabs: "rich-text-at-mention-palette-tabs",
223
+ scrollRegion: "rich-text-at-mention-palette-scroll-region",
224
+ scrollbar: "rich-text-at-mention-palette-scrollbar",
225
+ scrollbarThumb: "rich-text-at-mention-palette-scrollbar-thumb",
226
+ hint: "rich-text-at-mention-palette-hint",
227
+ hintItem: "rich-text-at-mention-palette-hint-item",
228
+ hintButton: "rich-text-at-mention-palette-hint-button",
229
+ hintSeparator: "rich-text-at-mention-palette-hint-separator",
230
+ shortcut: "rich-text-at-mention-palette-shortcut",
231
+ shortcutArrow: "rich-text-at-mention-palette-shortcut--arrow",
232
+ shortcutButton: "rich-text-at-mention-palette-shortcut-button",
233
+ shortcutGroup: "rich-text-at-mention-palette-shortcut-group"
234
+ },
235
+ testIds: {
236
+ emptyState: "rich-text-at-mention-palette-empty-state",
237
+ hint: "rich-text-at-mention-palette-hint",
238
+ scrollbar: "rich-text-at-mention-palette-scrollbar",
239
+ loadingSpinner: "rich-text-at-mention-loading-spinner"
240
+ },
241
+ groupDividerAttribute: "data-rich-text-at-mention-group-divider"
242
+ };
243
+ function resolveMentionPaletteTheme(theme) {
244
+ return {
245
+ classNames: { ...DEFAULT_THEME.classNames, ...theme?.classNames },
246
+ testIds: { ...DEFAULT_THEME.testIds, ...theme?.testIds },
247
+ groupDividerAttribute: theme?.groupDividerAttribute ?? DEFAULT_THEME.groupDividerAttribute
248
+ };
249
+ }
250
+ function mentionPaletteRootClassName(theme) {
251
+ return cn2(
252
+ DEFAULT_THEME.classNames.palette,
253
+ theme.classNames.palette,
254
+ paletteStyles.palette
255
+ );
256
+ }
257
+ var paletteStyles = {
258
+ palette: "rich-text-at-mention-palette__shell",
259
+ header: "rich-text-at-mention-palette__header",
260
+ footer: "rich-text-at-mention-palette__footer",
261
+ scrollShell: "rich-text-at-mention-palette__scroll-shell",
262
+ scrollBody: "rich-text-at-mention-palette__scroll-body",
263
+ groups: "rich-text-at-mention-palette__groups",
264
+ group: "rich-text-at-mention-palette__group",
265
+ groupDivider: "rich-text-at-mention-palette__group-divider",
266
+ groupLabel: "rich-text-at-mention-palette__group-label",
267
+ groupItems: "rich-text-at-mention-palette__group-items",
268
+ groupEmpty: "rich-text-at-mention-palette__group-empty",
269
+ rowButton: "rich-text-at-mention-palette__row-button",
270
+ expandButton: "rich-text-at-mention-palette__expand-button",
271
+ emptyState: "rich-text-at-mention-palette__empty-state",
272
+ emptyStateInner: "rich-text-at-mention-palette__empty-state-inner",
273
+ emptyStateIcon: "rich-text-at-mention-palette__empty-state-icon",
274
+ emptyStateText: "rich-text-at-mention-palette__empty-state-text",
275
+ loading: "rich-text-at-mention-palette__loading",
276
+ loadingSpinner: "rich-text-at-mention-palette__loading-spinner"
277
+ };
278
+ var MENTION_PALETTE_LOADING_MIN_VISIBLE_MS = 320;
279
+ function MentionPalette(props) {
280
+ "use memo";
281
+ const {
282
+ state,
283
+ highlightedKey,
284
+ getItemKey,
285
+ renderItem,
286
+ labels,
287
+ hintLabels,
288
+ maxHeightPx,
289
+ onHighlightChange,
290
+ onSelectItem,
291
+ onSelectCategory,
292
+ onSelectFilter,
293
+ onExpandGroup,
294
+ onCycleFilter,
295
+ onMoveSelection,
296
+ onNavigateHierarchy,
297
+ renderListFooter,
298
+ loadingBanner,
299
+ scrollHighlightedIntoViewCentered = false,
300
+ theme: themeProp
301
+ } = props;
302
+ const theme = resolveMentionPaletteTheme(themeProp);
303
+ const highlightedOptionRef = useRef2(null);
304
+ const scrollBodyRef = useRef2(null);
305
+ const loadingVisibleUntilRef = useRef2(0);
306
+ const loadingHideTimerRef = useRef2(
307
+ null
308
+ );
309
+ const [loadingIndicatorVisible, setLoadingIndicatorVisible] = useState2(
310
+ state.status === "loading"
311
+ );
312
+ const interactiveEntries = flattenMentionPaletteEntries(
313
+ state,
314
+ (item, groupId) => getItemKey(item, findGroup(state.groups, groupId))
315
+ );
316
+ const hasInteractiveEntries = interactiveEntries.some(
317
+ (entry) => entry.type === "item" || entry.type === "expand"
318
+ );
319
+ const showLoadingState = loadingIndicatorVisible && (!hasInteractiveEntries || state.mode === "browse");
320
+ const showLoadingBanner = Boolean(loadingBanner) && loadingIndicatorVisible && hasInteractiveEntries && state.mode === "results";
321
+ useEffect2(() => {
322
+ const highlightedElement = highlightedOptionRef.current;
323
+ if (!highlightedElement) {
324
+ return;
325
+ }
326
+ const scrollContainer = scrollBodyRef.current;
327
+ if (!scrollContainer || !scrollContainer.contains(highlightedElement)) {
328
+ highlightedElement.scrollIntoView({ block: "nearest" });
329
+ return;
330
+ }
331
+ if (scrollHighlightedIntoViewCentered) {
332
+ centerElementInScrollContainer(scrollContainer, highlightedElement);
333
+ return;
334
+ }
335
+ scrollElementIntoScrollContainerNearest(
336
+ scrollContainer,
337
+ highlightedElement
338
+ );
339
+ }, [highlightedKey, scrollHighlightedIntoViewCentered]);
340
+ useEffect2(() => {
341
+ if (loadingHideTimerRef.current !== null) {
342
+ clearTimeout(loadingHideTimerRef.current);
343
+ loadingHideTimerRef.current = null;
344
+ }
345
+ if (state.status === "loading") {
346
+ loadingVisibleUntilRef.current = Date.now() + MENTION_PALETTE_LOADING_MIN_VISIBLE_MS;
347
+ setLoadingIndicatorVisible(true);
348
+ return;
349
+ }
350
+ const remainingMs = loadingVisibleUntilRef.current - Date.now();
351
+ if (remainingMs <= 0) {
352
+ setLoadingIndicatorVisible(false);
353
+ return;
354
+ }
355
+ loadingHideTimerRef.current = setTimeout(() => {
356
+ loadingHideTimerRef.current = null;
357
+ setLoadingIndicatorVisible(false);
358
+ }, remainingMs);
359
+ return () => {
360
+ if (loadingHideTimerRef.current !== null) {
361
+ clearTimeout(loadingHideTimerRef.current);
362
+ loadingHideTimerRef.current = null;
363
+ }
364
+ };
365
+ }, [state.status]);
366
+ const paletteMaxHeightStyle = maxHeightPx > 0 ? { maxHeight: `${maxHeightPx}px` } : void 0;
367
+ if (state.status === "error") {
368
+ return /* @__PURE__ */ jsx2(
369
+ "div",
370
+ {
371
+ className: mentionPaletteRootClassName(theme),
372
+ style: paletteMaxHeightStyle,
373
+ role: "listbox",
374
+ "aria-label": labels.listbox ?? labels.tabHint,
375
+ children: /* @__PURE__ */ jsx2(
376
+ MentionPaletteEmptyState,
377
+ {
378
+ icon: "folder-failed",
379
+ label: labels.error,
380
+ testId: theme.testIds.emptyState
381
+ }
382
+ )
383
+ }
384
+ );
385
+ }
386
+ const isBrowse = state.mode === "browse";
387
+ let body;
388
+ if (showLoadingState) {
389
+ body = /* @__PURE__ */ jsx2(
390
+ MentionPaletteLoading,
391
+ {
392
+ label: labels.loading,
393
+ spinnerTestId: theme.testIds.loadingSpinner
394
+ }
395
+ );
396
+ } else if (state.groups.length === 0) {
397
+ body = /* @__PURE__ */ jsx2(
398
+ MentionPaletteEmptyState,
399
+ {
400
+ icon: isBrowse ? "keyboard" : "folder-failed",
401
+ label: labels.empty,
402
+ testId: theme.testIds.emptyState
403
+ }
404
+ );
405
+ } else {
406
+ body = /* @__PURE__ */ jsx2(
407
+ MentionPaletteGroups,
408
+ {
409
+ state,
410
+ highlightedKey,
411
+ highlightedOptionRef,
412
+ getItemKey,
413
+ renderItem,
414
+ onHighlightChange,
415
+ onSelectItem,
416
+ onExpandGroup,
417
+ renderListFooter,
418
+ groupDividerAttribute: theme.groupDividerAttribute
419
+ }
420
+ );
421
+ }
422
+ return /* @__PURE__ */ jsxs(
423
+ "div",
424
+ {
425
+ className: mentionPaletteRootClassName(theme),
426
+ style: paletteMaxHeightStyle,
427
+ role: "listbox",
428
+ "aria-label": labels.listbox ?? labels.tabHint,
429
+ children: [
430
+ /* @__PURE__ */ jsxs("div", { className: cn2(theme.classNames.header, paletteStyles.header), children: [
431
+ /* @__PURE__ */ jsx2(
432
+ UnderlineTabs,
433
+ {
434
+ tabs: state.categories.map((category) => ({
435
+ value: category.id,
436
+ label: category.label
437
+ })),
438
+ value: state.filter,
439
+ onValueChange: isBrowse ? onSelectCategory : onSelectFilter,
440
+ className: theme.classNames.tabs,
441
+ preventMouseDownDefault: true
442
+ }
443
+ ),
444
+ showLoadingBanner ? loadingBanner : null
445
+ ] }),
446
+ /* @__PURE__ */ jsxs("div", { className: paletteStyles.scrollShell, children: [
447
+ /* @__PURE__ */ jsx2(
448
+ "div",
449
+ {
450
+ ref: scrollBodyRef,
451
+ className: cn2(
452
+ theme.classNames.scrollRegion,
453
+ paletteStyles.scrollBody
454
+ ),
455
+ children: body
456
+ }
457
+ ),
458
+ /* @__PURE__ */ jsx2(
459
+ MentionPaletteScrollbar,
460
+ {
461
+ scrollBodyRef,
462
+ className: theme.classNames.scrollbar,
463
+ thumbClassName: theme.classNames.scrollbarThumb,
464
+ testId: theme.testIds.scrollbar
465
+ }
466
+ )
467
+ ] }),
468
+ /* @__PURE__ */ jsx2("div", { className: cn2(theme.classNames.footer, paletteStyles.footer), children: /* @__PURE__ */ jsx2(
469
+ MentionPaletteHint,
470
+ {
471
+ ariaLabel: labels.tabHint,
472
+ cycleFilterLabel: hintLabels.cycleFilter,
473
+ moveSelectionLabel: hintLabels.moveSelection,
474
+ navigateHierarchyLabel: hintLabels.navigateHierarchy,
475
+ onCycleFilter,
476
+ onMoveSelection,
477
+ onNavigateHierarchy,
478
+ classNames: theme.classNames,
479
+ testId: theme.testIds.hint
480
+ }
481
+ ) })
482
+ ]
483
+ }
484
+ );
485
+ }
486
+ function centerElementInScrollContainer(container, element) {
487
+ const containerRect = container.getBoundingClientRect();
488
+ const elementRect = element.getBoundingClientRect();
489
+ const currentScrollTop = container.scrollTop;
490
+ const elementTop = elementRect.top - containerRect.top + currentScrollTop;
491
+ const centeredScrollTop = elementTop - (container.clientHeight - elementRect.height) / 2;
492
+ const maxScrollTop = Math.max(
493
+ 0,
494
+ container.scrollHeight - container.clientHeight
495
+ );
496
+ const nextScrollTop = Math.min(Math.max(0, centeredScrollTop), maxScrollTop);
497
+ container.scrollTo({ top: nextScrollTop, behavior: "auto" });
498
+ }
499
+ function scrollElementIntoScrollContainerNearest(container, element) {
500
+ const containerRect = container.getBoundingClientRect();
501
+ const elementRect = element.getBoundingClientRect();
502
+ let nextScrollTop = container.scrollTop;
503
+ if (elementRect.top < containerRect.top) {
504
+ nextScrollTop -= containerRect.top - elementRect.top;
505
+ } else if (elementRect.bottom > containerRect.bottom) {
506
+ nextScrollTop += elementRect.bottom - containerRect.bottom;
507
+ } else {
508
+ return;
509
+ }
510
+ const maxScrollTop = Math.max(
511
+ 0,
512
+ container.scrollHeight - container.clientHeight
513
+ );
514
+ container.scrollTo({
515
+ top: Math.min(Math.max(0, nextScrollTop), maxScrollTop),
516
+ behavior: "auto"
517
+ });
518
+ }
519
+ function findGroup(groups, groupId) {
520
+ const group = groups.find((candidate) => candidate.id === groupId);
521
+ if (!group) {
522
+ throw new Error(`MentionPalette: unknown group id "${groupId}"`);
523
+ }
524
+ return group;
525
+ }
526
+ function MentionPaletteGroups({
527
+ state,
528
+ highlightedKey,
529
+ highlightedOptionRef,
530
+ getItemKey,
531
+ renderItem,
532
+ onHighlightChange,
533
+ onSelectItem,
534
+ onExpandGroup,
535
+ renderListFooter,
536
+ groupDividerAttribute
537
+ }) {
538
+ return /* @__PURE__ */ jsxs("div", { className: paletteStyles.groups, children: [
539
+ state.groups.map((group, index) => {
540
+ const showGroupDivider = index > 0 && !group.hideTopDivider;
541
+ return /* @__PURE__ */ jsxs(
542
+ "section",
543
+ {
544
+ className: cn2(paletteStyles.group, group.sectionClassName),
545
+ children: [
546
+ showGroupDivider ? /* @__PURE__ */ jsx2(
547
+ "div",
548
+ {
549
+ className: paletteStyles.groupDivider,
550
+ ...{ [groupDividerAttribute]: "true" },
551
+ "aria-hidden": "true"
552
+ }
553
+ ) : null,
554
+ group.label ? /* @__PURE__ */ jsx2("div", { className: paletteStyles.groupLabel, children: group.label }) : null,
555
+ /* @__PURE__ */ jsxs("div", { className: paletteStyles.groupItems, children: [
556
+ group.items.length === 0 && group.emptyLabel ? /* @__PURE__ */ jsx2("div", { className: paletteStyles.groupEmpty, children: group.emptyLabel }) : null,
557
+ group.items.map((item) => {
558
+ const entryKey = `${group.id}:${getItemKey(item, group)}`;
559
+ const isHighlighted = entryKey === highlightedKey;
560
+ return /* @__PURE__ */ jsx2(
561
+ "button",
562
+ {
563
+ ref: isHighlighted ? highlightedOptionRef : null,
564
+ type: "button",
565
+ className: paletteStyles.rowButton,
566
+ role: "option",
567
+ "aria-selected": isHighlighted,
568
+ "data-highlighted": isHighlighted ? "" : void 0,
569
+ onPointerMove: () => {
570
+ if (!isHighlighted) {
571
+ onHighlightChange(entryKey);
572
+ }
573
+ },
574
+ onMouseDown: (event) => event.preventDefault(),
575
+ onClick: () => onSelectItem(item, group),
576
+ children: renderItem(item, { active: isHighlighted, group })
577
+ },
578
+ entryKey
579
+ );
580
+ }),
581
+ group.hasMore ? /* @__PURE__ */ jsx2(
582
+ "button",
583
+ {
584
+ ref: `expand:${group.id}` === highlightedKey ? highlightedOptionRef : null,
585
+ type: "button",
586
+ className: paletteStyles.expandButton,
587
+ "data-highlighted": `expand:${group.id}` === highlightedKey ? "" : void 0,
588
+ onPointerMove: () => {
589
+ if (`expand:${group.id}` !== highlightedKey) {
590
+ onHighlightChange(`expand:${group.id}`);
591
+ }
592
+ },
593
+ onMouseDown: (event) => event.preventDefault(),
594
+ onClick: () => onExpandGroup(group.id),
595
+ children: group.expandLabel ?? `+${Math.max(0, group.totalCount - group.visibleCount)}`
596
+ },
597
+ `expand:${group.id}`
598
+ ) : null
599
+ ] })
600
+ ]
601
+ },
602
+ group.id
603
+ );
604
+ }),
605
+ renderListFooter?.()
606
+ ] });
607
+ }
608
+ function MentionPaletteEmptyState({
609
+ icon = "folder-failed",
610
+ label,
611
+ testId
612
+ }) {
613
+ "use memo";
614
+ const EmptyStateIcon = icon === "keyboard" ? KeyboardFilledIcon : FolderFailedFilledIcon;
615
+ return /* @__PURE__ */ jsx2(
616
+ "div",
617
+ {
618
+ className: paletteStyles.emptyState,
619
+ "data-empty-state-icon": icon,
620
+ "data-testid": testId,
621
+ children: /* @__PURE__ */ jsxs("div", { className: paletteStyles.emptyStateInner, children: [
622
+ /* @__PURE__ */ jsx2(
623
+ EmptyStateIcon,
624
+ {
625
+ className: paletteStyles.emptyStateIcon,
626
+ "aria-hidden": "true"
627
+ }
628
+ ),
629
+ /* @__PURE__ */ jsx2("span", { className: paletteStyles.emptyStateText, children: label })
630
+ ] })
631
+ }
632
+ );
633
+ }
634
+ function MentionPaletteLoading({
635
+ label,
636
+ spinnerTestId
637
+ }) {
638
+ "use memo";
639
+ return /* @__PURE__ */ jsxs("div", { className: paletteStyles.loading, children: [
640
+ /* @__PURE__ */ jsx2(
641
+ "span",
642
+ {
643
+ "aria-hidden": "true",
644
+ className: paletteStyles.loadingSpinner,
645
+ "data-testid": spinnerTestId
646
+ }
647
+ ),
648
+ /* @__PURE__ */ jsx2("span", { children: label })
649
+ ] });
650
+ }
651
+ function MentionPaletteHint({
652
+ ariaLabel,
653
+ cycleFilterLabel,
654
+ moveSelectionLabel,
655
+ navigateHierarchyLabel,
656
+ onCycleFilter,
657
+ onMoveSelection,
658
+ onNavigateHierarchy,
659
+ classNames,
660
+ testId
661
+ }) {
662
+ "use memo";
663
+ const showHierarchyHints = Boolean(
664
+ navigateHierarchyLabel && onNavigateHierarchy
665
+ );
666
+ return /* @__PURE__ */ jsxs(
667
+ "div",
668
+ {
669
+ className: classNames.hint,
670
+ "aria-label": ariaLabel,
671
+ "data-testid": testId,
672
+ children: [
673
+ /* @__PURE__ */ jsxs("span", { className: classNames.hintItem, children: [
674
+ /* @__PURE__ */ jsxs("span", { className: classNames.shortcutGroup, children: [
675
+ /* @__PURE__ */ jsx2(
676
+ "button",
677
+ {
678
+ className: cn2(classNames.shortcut, classNames.shortcutButton),
679
+ type: "button",
680
+ "aria-label": `Tab ${cycleFilterLabel}`,
681
+ onMouseDown: (event) => event.preventDefault(),
682
+ onClick: () => onCycleFilter(1),
683
+ children: "Tab"
684
+ }
685
+ ),
686
+ !showHierarchyHints ? /* @__PURE__ */ jsxs(Fragment, { children: [
687
+ /* @__PURE__ */ jsx2(
688
+ "button",
689
+ {
690
+ className: cn2(
691
+ classNames.shortcut,
692
+ classNames.shortcutArrow,
693
+ classNames.shortcutButton
694
+ ),
695
+ type: "button",
696
+ "aria-label": `\u2190 ${cycleFilterLabel}`,
697
+ onMouseDown: (event) => event.preventDefault(),
698
+ onClick: () => onCycleFilter(-1),
699
+ children: "\u2190"
700
+ }
701
+ ),
702
+ /* @__PURE__ */ jsx2(
703
+ "button",
704
+ {
705
+ className: cn2(
706
+ classNames.shortcut,
707
+ classNames.shortcutArrow,
708
+ classNames.shortcutButton
709
+ ),
710
+ type: "button",
711
+ "aria-label": `\u2192 ${cycleFilterLabel}`,
712
+ onMouseDown: (event) => event.preventDefault(),
713
+ onClick: () => onCycleFilter(1),
714
+ children: "\u2192"
715
+ }
716
+ )
717
+ ] }) : null
718
+ ] }),
719
+ /* @__PURE__ */ jsx2("span", { children: cycleFilterLabel })
720
+ ] }),
721
+ showHierarchyHints ? /* @__PURE__ */ jsxs(Fragment, { children: [
722
+ /* @__PURE__ */ jsx2("span", { className: classNames.hintSeparator, "aria-hidden": "true", children: "\uFF5C" }),
723
+ /* @__PURE__ */ jsxs("span", { className: classNames.hintItem, children: [
724
+ /* @__PURE__ */ jsxs("span", { className: classNames.shortcutGroup, children: [
725
+ /* @__PURE__ */ jsx2(
726
+ "button",
727
+ {
728
+ className: cn2(
729
+ classNames.shortcut,
730
+ classNames.shortcutArrow,
731
+ classNames.shortcutButton
732
+ ),
733
+ type: "button",
734
+ "aria-label": `\u2190 ${navigateHierarchyLabel}`,
735
+ onMouseDown: (event) => event.preventDefault(),
736
+ onClick: () => onNavigateHierarchy?.(-1),
737
+ children: "\u2190"
738
+ }
739
+ ),
740
+ /* @__PURE__ */ jsx2(
741
+ "button",
742
+ {
743
+ className: cn2(
744
+ classNames.shortcut,
745
+ classNames.shortcutArrow,
746
+ classNames.shortcutButton
747
+ ),
748
+ type: "button",
749
+ "aria-label": `\u2192 ${navigateHierarchyLabel}`,
750
+ onMouseDown: (event) => event.preventDefault(),
751
+ onClick: () => onNavigateHierarchy?.(1),
752
+ children: "\u2192"
753
+ }
754
+ )
755
+ ] }),
756
+ /* @__PURE__ */ jsx2("span", { children: navigateHierarchyLabel })
757
+ ] })
758
+ ] }) : null,
759
+ /* @__PURE__ */ jsx2("span", { className: classNames.hintSeparator, "aria-hidden": "true", children: "\uFF5C" }),
760
+ /* @__PURE__ */ jsxs("span", { className: classNames.hintItem, children: [
761
+ /* @__PURE__ */ jsxs("span", { className: classNames.shortcutGroup, children: [
762
+ /* @__PURE__ */ jsx2(
763
+ "button",
764
+ {
765
+ className: cn2(
766
+ classNames.shortcut,
767
+ classNames.shortcutArrow,
768
+ classNames.shortcutButton
769
+ ),
770
+ type: "button",
771
+ "aria-label": `\u2191 ${moveSelectionLabel}`,
772
+ onMouseDown: (event) => event.preventDefault(),
773
+ onClick: () => onMoveSelection(-1),
774
+ children: "\u2191"
775
+ }
776
+ ),
777
+ /* @__PURE__ */ jsx2(
778
+ "button",
779
+ {
780
+ className: cn2(
781
+ classNames.shortcut,
782
+ classNames.shortcutArrow,
783
+ classNames.shortcutButton
784
+ ),
785
+ type: "button",
786
+ "aria-label": `\u2193 ${moveSelectionLabel}`,
787
+ onMouseDown: (event) => event.preventDefault(),
788
+ onClick: () => onMoveSelection(1),
789
+ children: "\u2193"
790
+ }
791
+ )
792
+ ] }),
793
+ /* @__PURE__ */ jsx2("span", { children: moveSelectionLabel })
794
+ ] })
795
+ ]
796
+ }
797
+ );
798
+ }
799
+
800
+ // src/at-panel/mentionPaletteStateAdapter.ts
801
+ function createMentionPaletteStateAdapter(input) {
802
+ const getPaletteItemKey = (item, group) => input.getItemKey(item, group.id);
803
+ const selectCategory = (categoryId) => {
804
+ input.callbacks?.onActiveCategoryIdChange?.(categoryId);
805
+ };
806
+ const expandGroup = (groupId) => {
807
+ input.callbacks?.onExpandGroup?.(groupId);
808
+ };
809
+ const moveSelection = (delta) => {
810
+ const nextKey = moveMentionPaletteHighlight({
811
+ state: input.state,
812
+ currentKey: input.highlightedKey,
813
+ delta,
814
+ getItemKey: input.getItemKey
815
+ });
816
+ if (nextKey !== null) {
817
+ input.callbacks?.onHighlightChange?.(nextKey);
818
+ }
819
+ return nextKey;
820
+ };
821
+ const cycleCategory = (delta) => {
822
+ const cycleCategories = resolveCycleCategories(input.categoryCycleOrder);
823
+ const categories = cycleCategories.length ? cycleCategories : input.state.categories;
824
+ if (categories.length === 0) {
825
+ return null;
826
+ }
827
+ const nextCategoryId = nextMentionPaletteCategory(
828
+ categories,
829
+ input.state.filter,
830
+ delta
831
+ );
832
+ selectCategory(nextCategoryId);
833
+ return nextCategoryId;
834
+ };
835
+ const selectedItem = selectedMentionPaletteItem({
836
+ state: input.state,
837
+ key: input.highlightedKey,
838
+ getItemKey: input.getItemKey
839
+ });
840
+ const commitHighlighted = () => {
841
+ const activeEntry = findMentionPaletteEntry({
842
+ state: input.state,
843
+ key: input.highlightedKey,
844
+ getItemKey: input.getItemKey
845
+ });
846
+ const fallbackCategoryId = categoryIdFromKey(input.highlightedKey);
847
+ if (!activeEntry) {
848
+ if (fallbackCategoryId !== null && input.state.categories.some(
849
+ (category) => category.id === fallbackCategoryId
850
+ )) {
851
+ selectCategory(fallbackCategoryId);
852
+ return { type: "category", categoryId: fallbackCategoryId };
853
+ }
854
+ return { type: "none" };
855
+ }
856
+ if (activeEntry.type === "category" && activeEntry.categoryId) {
857
+ selectCategory(activeEntry.categoryId);
858
+ return { type: "category", categoryId: activeEntry.categoryId };
859
+ }
860
+ if (activeEntry.type === "expand" && activeEntry.groupId) {
861
+ expandGroup(activeEntry.groupId);
862
+ return { type: "expand", groupId: activeEntry.groupId };
863
+ }
864
+ if (activeEntry.type === "item") {
865
+ const item = selectedMentionPaletteItem({
866
+ state: input.state,
867
+ key: activeEntry.key,
868
+ getItemKey: input.getItemKey
869
+ });
870
+ if (item !== null) {
871
+ input.callbacks?.onSelectItem?.(item);
872
+ return { type: "item", item };
873
+ }
874
+ }
875
+ return { type: "none" };
876
+ };
877
+ return {
878
+ selectedItem,
879
+ paletteProps: {
880
+ state: input.state,
881
+ highlightedKey: input.highlightedKey,
882
+ getItemKey: getPaletteItemKey,
883
+ onHighlightChange: (key) => {
884
+ input.callbacks?.onHighlightChange?.(key);
885
+ },
886
+ onSelectItem: (item) => {
887
+ input.callbacks?.onSelectItem?.(item);
888
+ },
889
+ onSelectCategory: selectCategory,
890
+ onSelectFilter: selectCategory,
891
+ onExpandGroup: expandGroup,
892
+ onCycleFilter: cycleCategory,
893
+ onMoveSelection: moveSelection
894
+ },
895
+ moveSelection,
896
+ selectCategory,
897
+ expandGroup,
898
+ cycleCategory,
899
+ commitHighlighted
900
+ };
901
+ }
902
+ function resolveCycleCategories(categories) {
903
+ return categories?.map(
904
+ (category) => typeof category === "string" ? { id: category } : category
905
+ ) ?? [];
906
+ }
907
+ function categoryIdFromKey(key) {
908
+ const prefix = "category:";
909
+ return key?.startsWith(prefix) ? key.slice(prefix.length) : null;
910
+ }
911
+
912
+ // src/at-panel/MentionPaletteFromState.tsx
913
+ import { jsx as jsx3 } from "react/jsx-runtime";
914
+ function MentionPaletteFromState(props) {
915
+ "use memo";
916
+ const {
917
+ labels,
918
+ hintLabels,
919
+ maxHeightPx,
920
+ renderItem,
921
+ renderListFooter,
922
+ loadingBanner,
923
+ scrollHighlightedIntoViewCentered,
924
+ theme,
925
+ onNavigateHierarchy,
926
+ ...adapterInput
927
+ } = props;
928
+ const adapter = createMentionPaletteStateAdapter(adapterInput);
929
+ return /* @__PURE__ */ jsx3(
930
+ MentionPalette,
931
+ {
932
+ ...adapter.paletteProps,
933
+ labels,
934
+ hintLabels,
935
+ maxHeightPx,
936
+ renderItem,
937
+ renderListFooter,
938
+ loadingBanner,
939
+ scrollHighlightedIntoViewCentered,
940
+ theme,
941
+ onNavigateHierarchy
942
+ }
943
+ );
944
+ }
945
+
946
+ // src/at-panel/MentionPaletteControls.tsx
947
+ import { CheckIcon } from "@tutti-os/ui-system/icons";
948
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
949
+ function MentionPaletteSelectIndicator(props) {
950
+ return /* @__PURE__ */ jsx4(
951
+ "span",
952
+ {
953
+ "aria-hidden": "true",
954
+ className: "rich-text-at-mention-select-indicator",
955
+ "data-selected": props.selected ? "true" : "false",
956
+ children: /* @__PURE__ */ jsx4(CheckIcon, { size: 13 })
957
+ }
958
+ );
959
+ }
960
+ function MentionPaletteMultiSelectFooter(props) {
961
+ return /* @__PURE__ */ jsxs2("div", { className: "rich-text-at-mention-multiselect-footer", children: [
962
+ /* @__PURE__ */ jsx4("span", { className: "rich-text-at-mention-multiselect-footer__count", children: props.countLabel }),
963
+ /* @__PURE__ */ jsxs2("div", { className: "rich-text-at-mention-multiselect-footer__actions", children: [
964
+ /* @__PURE__ */ jsx4(
965
+ "button",
966
+ {
967
+ type: "button",
968
+ className: "rich-text-at-mention-multiselect-footer__button rich-text-at-mention-multiselect-footer__button--secondary",
969
+ onMouseDown: (event) => event.preventDefault(),
970
+ onClick: props.onCancel,
971
+ children: props.cancelLabel
972
+ }
973
+ ),
974
+ /* @__PURE__ */ jsx4(
975
+ "button",
976
+ {
977
+ type: "button",
978
+ disabled: props.count === 0,
979
+ className: "rich-text-at-mention-multiselect-footer__button rich-text-at-mention-multiselect-footer__button--primary",
980
+ onMouseDown: (event) => event.preventDefault(),
981
+ onClick: props.onConfirm,
982
+ children: props.confirmLabel
983
+ }
984
+ )
985
+ ] })
986
+ ] });
987
+ }
988
+
989
+ // src/at-panel/MentionRow.tsx
990
+ import {
991
+ ArrowLeftIcon,
992
+ ArrowRightIcon,
993
+ Badge,
994
+ FileCodeIcon,
995
+ FileTextIcon,
996
+ FolderIcon,
997
+ ImageFileIcon,
998
+ ProductIcon,
999
+ StatusDot,
1000
+ VideoFileIcon,
1001
+ cn as cn3
1002
+ } from "@tutti-os/ui-system";
1003
+
1004
+ // src/at-panel/mentionRowDataAttributes.ts
1005
+ var MENTION_ROW_DATA_ATTRIBUTES = {
1006
+ shared: {
1007
+ agentAvatar: "data-rich-text-at-mention-agent-avatar",
1008
+ appIcon: "data-rich-text-at-mention-app-icon",
1009
+ fileEntryKind: "data-rich-text-at-mention-file-entry-kind",
1010
+ fileThumb: "data-rich-text-at-mention-file-thumb",
1011
+ fileVisualKind: "data-rich-text-at-mention-file-visual-kind",
1012
+ navigation: "data-rich-text-at-mention-navigation",
1013
+ navigateInto: "data-rich-text-at-mention-navigate-into",
1014
+ openReferences: "data-rich-text-at-mention-open-references",
1015
+ statusTag: "data-rich-text-at-mention-status-tag",
1016
+ userAvatar: "data-rich-text-at-mention-user-avatar"
1017
+ },
1018
+ agent: {
1019
+ agentAvatar: "data-agent-mention-agent-avatar",
1020
+ appIcon: "data-agent-mention-app-icon",
1021
+ fileEntryKind: "data-agent-file-entry-kind",
1022
+ fileThumb: "data-agent-mention-file-thumb",
1023
+ fileVisualKind: "data-agent-file-visual-kind",
1024
+ navigation: "data-agent-mention-navigation",
1025
+ navigateInto: "data-agent-mention-navigate-into",
1026
+ openReferences: "data-agent-mention-open-references",
1027
+ statusTag: "data-agent-mention-status-tag",
1028
+ userAvatar: "data-agent-mention-user-avatar"
1029
+ }
1030
+ };
1031
+ function mentionRowRootDataAttributes(mode, kind) {
1032
+ return mode === "agent" ? {
1033
+ "data-agent-file-mention": "true",
1034
+ "data-agent-mention-kind": kind
1035
+ } : {
1036
+ "data-rich-text-at-mention-row": "true",
1037
+ "data-rich-text-at-mention-kind": kind
1038
+ };
1039
+ }
1040
+ function mentionRowDataAttribute(mode, key, value) {
1041
+ return {
1042
+ [MENTION_ROW_DATA_ATTRIBUTES[mode][key]]: value
1043
+ };
1044
+ }
1045
+
1046
+ // src/at-panel/MentionRow.tsx
1047
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1048
+ var MENTION_FILE_VISUAL_KIND_ICON = {
1049
+ back: ArrowLeftIcon,
1050
+ folder: FolderIcon,
1051
+ document: FileTextIcon,
1052
+ // The agent maps markdown to `product-filled.svg`; ProductIcon is the
1053
+ // matching boundary-safe ui-system glyph.
1054
+ markdown: ProductIcon,
1055
+ code: FileCodeIcon,
1056
+ image: ImageFileIcon,
1057
+ video: VideoFileIcon
1058
+ };
1059
+ var DEFAULT_MENTION_ROW_CLASS_NAMES = {
1060
+ fileIcon: "rich-text-at-mention-file-icon",
1061
+ fileThumb: "rich-text-at-mention-file-thumb",
1062
+ kindIcon: "rich-text-at-mention-kind-icon",
1063
+ avatarImgUserPlaceholder: "rich-text-at-mention-avatar-img--user-placeholder"
1064
+ };
1065
+ function resolveMentionRowClassNames(classNames) {
1066
+ return {
1067
+ fileIcon: classNames?.fileIcon ?? DEFAULT_MENTION_ROW_CLASS_NAMES.fileIcon,
1068
+ fileThumb: classNames?.fileThumb ?? DEFAULT_MENTION_ROW_CLASS_NAMES.fileThumb,
1069
+ kindIcon: classNames?.kindIcon ?? DEFAULT_MENTION_ROW_CLASS_NAMES.kindIcon,
1070
+ avatarImgUserPlaceholder: classNames?.avatarImgUserPlaceholder ?? DEFAULT_MENTION_ROW_CLASS_NAMES.avatarImgUserPlaceholder
1071
+ };
1072
+ }
1073
+ function resolveMentionRowRenderOptions(options) {
1074
+ if (isMentionRowRenderOptions(options)) {
1075
+ return {
1076
+ classNames: options.classNames,
1077
+ dataAttributeMode: options.dataAttributeMode ?? "shared",
1078
+ onOpenReferences: options.onOpenReferences,
1079
+ openReferencesLabel: options.openReferencesLabel,
1080
+ onNavigateInto: options.onNavigateInto,
1081
+ navigateIntoLabel: options.navigateIntoLabel
1082
+ };
1083
+ }
1084
+ return {
1085
+ classNames: options,
1086
+ dataAttributeMode: "shared"
1087
+ };
1088
+ }
1089
+ function isMentionRowRenderOptions(options) {
1090
+ return options !== void 0 && ("classNames" in options || "dataAttributeMode" in options || "onOpenReferences" in options || "onNavigateInto" in options);
1091
+ }
1092
+ function renderMentionRow(item, options) {
1093
+ const {
1094
+ classNames,
1095
+ dataAttributeMode,
1096
+ onOpenReferences,
1097
+ openReferencesLabel,
1098
+ onNavigateInto,
1099
+ navigateIntoLabel
1100
+ } = resolveMentionRowRenderOptions(options);
1101
+ const resolved = resolveMentionRowClassNames(classNames);
1102
+ const referencesButton = onOpenReferences ? /* @__PURE__ */ jsx5(
1103
+ MentionOpenReferencesButton,
1104
+ {
1105
+ label: openReferencesLabel,
1106
+ onOpenReferences,
1107
+ dataAttributeMode
1108
+ }
1109
+ ) : null;
1110
+ if (item.kind === "file") {
1111
+ return /* @__PURE__ */ jsx5(
1112
+ MentionFileRow,
1113
+ {
1114
+ item,
1115
+ classNames: resolved,
1116
+ dataAttributeMode,
1117
+ navigateIntoLabel,
1118
+ onNavigateInto
1119
+ }
1120
+ );
1121
+ }
1122
+ if (item.kind === "plain") {
1123
+ return /* @__PURE__ */ jsxs3("span", { className: "rich-text-at-mention-row rich-text-at-mention-row--plain", children: [
1124
+ item.leading,
1125
+ /* @__PURE__ */ jsxs3("span", { className: "rich-text-at-mention-row__text-stack", children: [
1126
+ /* @__PURE__ */ jsx5("span", { className: "rich-text-at-mention-row__title", children: item.label }),
1127
+ item.description ? /* @__PURE__ */ jsx5("span", { className: "rich-text-at-mention-row__description", children: item.description }) : null
1128
+ ] })
1129
+ ] });
1130
+ }
1131
+ if (item.kind === "session") {
1132
+ return /* @__PURE__ */ jsxs3("span", { className: "rich-text-at-mention-row rich-text-at-mention-row--session", children: [
1133
+ /* @__PURE__ */ jsxs3("span", { className: "rich-text-at-mention-row__leading", children: [
1134
+ /* @__PURE__ */ jsx5(
1135
+ MentionSessionAvatarStack,
1136
+ {
1137
+ item,
1138
+ classNames: resolved,
1139
+ dataAttributeMode
1140
+ }
1141
+ ),
1142
+ /* @__PURE__ */ jsx5("span", { className: "rich-text-at-mention-row__session-title", children: /* @__PURE__ */ jsx5(MentionSessionTitle, { item }) })
1143
+ ] }),
1144
+ item.statusTag ? /* @__PURE__ */ jsx5(
1145
+ MentionStatusBadge,
1146
+ {
1147
+ statusTag: item.statusTag,
1148
+ dataAttributeMode
1149
+ }
1150
+ ) : null
1151
+ ] });
1152
+ }
1153
+ if (item.kind === "app") {
1154
+ return /* @__PURE__ */ jsxs3("span", { className: "rich-text-at-mention-row rich-text-at-mention-row--app", children: [
1155
+ /* @__PURE__ */ jsx5(
1156
+ MentionWorkspaceAppIcon,
1157
+ {
1158
+ iconUrl: item.iconUrl,
1159
+ kindIconClassName: resolved.kindIcon,
1160
+ dataAttributeMode
1161
+ }
1162
+ ),
1163
+ /* @__PURE__ */ jsxs3("span", { className: "rich-text-at-mention-row__app-text", children: [
1164
+ /* @__PURE__ */ jsx5("span", { className: "rich-text-at-mention-row__app-name", children: item.name }),
1165
+ item.description ? /* @__PURE__ */ jsx5("span", { className: "rich-text-at-mention-row__app-description", children: item.description }) : null
1166
+ ] }),
1167
+ referencesButton
1168
+ ] });
1169
+ }
1170
+ if (item.kind === "app-factory") {
1171
+ return /* @__PURE__ */ jsx5("span", { className: "rich-text-at-mention-row__text-stack", children: /* @__PURE__ */ jsx5("span", { className: "rich-text-at-mention-row__title", children: item.name }) });
1172
+ }
1173
+ return /* @__PURE__ */ jsxs3("span", { className: "rich-text-at-mention-row rich-text-at-mention-row--issue", children: [
1174
+ /* @__PURE__ */ jsxs3("span", { className: "rich-text-at-mention-row__text-stack rich-text-at-mention-row__text-stack--fill", children: [
1175
+ /* @__PURE__ */ jsx5("span", { className: "rich-text-at-mention-row__inline", children: /* @__PURE__ */ jsx5("span", { className: "rich-text-at-mention-row__title", children: item.title }) }),
1176
+ item.creatorName ? /* @__PURE__ */ jsx5("span", { className: "rich-text-at-mention-row__description", children: item.creatorName }) : null
1177
+ ] }),
1178
+ item.statusTag ? /* @__PURE__ */ jsx5(
1179
+ MentionStatusBadge,
1180
+ {
1181
+ statusTag: item.statusTag,
1182
+ dataAttributeMode
1183
+ }
1184
+ ) : null,
1185
+ referencesButton
1186
+ ] });
1187
+ }
1188
+ function MentionOpenReferencesButton({
1189
+ label,
1190
+ onOpenReferences,
1191
+ dataAttributeMode
1192
+ }) {
1193
+ const resolvedLabel = label?.trim() || "\u67E5\u770B\u4EA7\u7269";
1194
+ return /* @__PURE__ */ jsx5(
1195
+ "span",
1196
+ {
1197
+ role: "button",
1198
+ tabIndex: -1,
1199
+ "aria-label": resolvedLabel,
1200
+ title: resolvedLabel,
1201
+ className: "rich-text-at-mention-row__open-references",
1202
+ ...mentionRowDataAttribute(dataAttributeMode, "openReferences", "true"),
1203
+ onMouseDown: (event) => {
1204
+ event.preventDefault();
1205
+ event.stopPropagation();
1206
+ },
1207
+ onClick: (event) => {
1208
+ event.preventDefault();
1209
+ event.stopPropagation();
1210
+ onOpenReferences();
1211
+ },
1212
+ children: resolvedLabel
1213
+ }
1214
+ );
1215
+ }
1216
+ function MentionNavigateIntoButton({
1217
+ label,
1218
+ onNavigateInto,
1219
+ dataAttributeMode
1220
+ }) {
1221
+ return /* @__PURE__ */ jsx5(
1222
+ "span",
1223
+ {
1224
+ role: "button",
1225
+ tabIndex: -1,
1226
+ "aria-label": label,
1227
+ title: label,
1228
+ className: "rich-text-at-mention-row__navigate-into",
1229
+ ...mentionRowDataAttribute(dataAttributeMode, "navigateInto", "true"),
1230
+ onMouseDown: (event) => {
1231
+ event.preventDefault();
1232
+ event.stopPropagation();
1233
+ },
1234
+ onClick: (event) => {
1235
+ event.preventDefault();
1236
+ event.stopPropagation();
1237
+ onNavigateInto();
1238
+ },
1239
+ children: /* @__PURE__ */ jsx5(ArrowRightIcon, { size: 16 })
1240
+ }
1241
+ );
1242
+ }
1243
+ function MentionFileRow({
1244
+ item,
1245
+ classNames,
1246
+ dataAttributeMode,
1247
+ navigateIntoLabel,
1248
+ onNavigateInto
1249
+ }) {
1250
+ return /* @__PURE__ */ jsxs3(
1251
+ "span",
1252
+ {
1253
+ className: "rich-text-at-mention-row rich-text-at-mention-row--file",
1254
+ ...mentionRowRootDataAttributes(dataAttributeMode, "file"),
1255
+ ...item.entryKind ? mentionRowDataAttribute(
1256
+ dataAttributeMode,
1257
+ "fileEntryKind",
1258
+ item.entryKind
1259
+ ) : {},
1260
+ ...mentionRowDataAttribute(
1261
+ dataAttributeMode,
1262
+ "fileVisualKind",
1263
+ item.visualKind
1264
+ ),
1265
+ ...item.mentionNavigation ? mentionRowDataAttribute(
1266
+ dataAttributeMode,
1267
+ "navigation",
1268
+ item.mentionNavigation
1269
+ ) : {},
1270
+ children: [
1271
+ /* @__PURE__ */ jsx5(
1272
+ MentionFileIcon,
1273
+ {
1274
+ item,
1275
+ classNames,
1276
+ dataAttributeMode
1277
+ }
1278
+ ),
1279
+ /* @__PURE__ */ jsxs3("span", { className: "rich-text-at-mention-row__file-text", children: [
1280
+ /* @__PURE__ */ jsx5("span", { className: "rich-text-at-mention-row__title", children: item.name }),
1281
+ item.childCountLabel ? /* @__PURE__ */ jsx5("span", { className: "rich-text-at-mention-row__file-count", children: item.childCountLabel }) : null
1282
+ ] }),
1283
+ onNavigateInto ? /* @__PURE__ */ jsx5(
1284
+ MentionNavigateIntoButton,
1285
+ {
1286
+ label: navigateIntoLabel,
1287
+ onNavigateInto,
1288
+ dataAttributeMode
1289
+ }
1290
+ ) : null
1291
+ ]
1292
+ }
1293
+ );
1294
+ }
1295
+ function MentionFileIcon({
1296
+ item,
1297
+ classNames,
1298
+ dataAttributeMode
1299
+ }) {
1300
+ const thumbnailUrl = item.visualKind === "image" ? item.thumbnailUrl?.trim() || "" : "";
1301
+ if (thumbnailUrl) {
1302
+ return /* @__PURE__ */ jsx5(
1303
+ "span",
1304
+ {
1305
+ className: classNames.fileThumb,
1306
+ ...mentionRowDataAttribute(dataAttributeMode, "fileThumb", "true"),
1307
+ "aria-hidden": "true",
1308
+ children: /* @__PURE__ */ jsx5(
1309
+ "img",
1310
+ {
1311
+ src: thumbnailUrl,
1312
+ alt: "",
1313
+ className: "rich-text-at-mention-row__media",
1314
+ decoding: "async",
1315
+ loading: "lazy",
1316
+ draggable: false
1317
+ }
1318
+ )
1319
+ }
1320
+ );
1321
+ }
1322
+ if (item.visualKind === "back") {
1323
+ return /* @__PURE__ */ jsx5(
1324
+ "span",
1325
+ {
1326
+ className: cn3(
1327
+ classNames.fileIcon,
1328
+ "rich-text-at-mention-file-icon--glyph"
1329
+ ),
1330
+ ...mentionRowDataAttribute(
1331
+ dataAttributeMode,
1332
+ "fileVisualKind",
1333
+ item.visualKind
1334
+ ),
1335
+ "aria-hidden": "true",
1336
+ children: /* @__PURE__ */ jsx5(ArrowLeftIcon, { size: 16 })
1337
+ }
1338
+ );
1339
+ }
1340
+ const usesDefaultFileIcon = classNames.fileIcon === DEFAULT_MENTION_ROW_CLASS_NAMES.fileIcon;
1341
+ if (usesDefaultFileIcon) {
1342
+ const Icon = MENTION_FILE_VISUAL_KIND_ICON[item.visualKind];
1343
+ return /* @__PURE__ */ jsx5(
1344
+ "span",
1345
+ {
1346
+ className: cn3(
1347
+ classNames.fileIcon,
1348
+ "rich-text-at-mention-file-icon--glyph"
1349
+ ),
1350
+ ...mentionRowDataAttribute(
1351
+ dataAttributeMode,
1352
+ "fileVisualKind",
1353
+ item.visualKind
1354
+ ),
1355
+ "aria-hidden": "true",
1356
+ children: /* @__PURE__ */ jsx5(Icon, { size: 16 })
1357
+ }
1358
+ );
1359
+ }
1360
+ return /* @__PURE__ */ jsx5(
1361
+ "span",
1362
+ {
1363
+ className: classNames.fileIcon,
1364
+ ...mentionRowDataAttribute(
1365
+ dataAttributeMode,
1366
+ "fileVisualKind",
1367
+ item.visualKind
1368
+ ),
1369
+ "aria-hidden": "true"
1370
+ }
1371
+ );
1372
+ }
1373
+ function MentionWorkspaceAppIcon({
1374
+ iconUrl,
1375
+ kindIconClassName,
1376
+ dataAttributeMode
1377
+ }) {
1378
+ const normalizedIconUrl = iconUrl?.trim() ?? "";
1379
+ return /* @__PURE__ */ jsx5(
1380
+ "span",
1381
+ {
1382
+ className: "rich-text-at-mention-app-icon",
1383
+ ...mentionRowDataAttribute(dataAttributeMode, "appIcon", "true"),
1384
+ "data-workspace-app-icon": "true",
1385
+ "aria-hidden": "true",
1386
+ children: normalizedIconUrl ? /* @__PURE__ */ jsx5(
1387
+ "img",
1388
+ {
1389
+ src: normalizedIconUrl,
1390
+ alt: "",
1391
+ className: "rich-text-at-mention-row__media",
1392
+ decoding: "async",
1393
+ loading: "lazy",
1394
+ draggable: false
1395
+ }
1396
+ ) : /* @__PURE__ */ jsx5(
1397
+ "span",
1398
+ {
1399
+ className: cn3(
1400
+ kindIconClassName,
1401
+ "rich-text-at-mention-kind-icon--app"
1402
+ )
1403
+ }
1404
+ )
1405
+ }
1406
+ );
1407
+ }
1408
+ function MentionSessionAvatarStack({
1409
+ item,
1410
+ classNames,
1411
+ dataAttributeMode
1412
+ }) {
1413
+ const showUserAvatar = item.showUserAvatar !== false;
1414
+ if (!showUserAvatar) {
1415
+ return /* @__PURE__ */ jsx5(
1416
+ "span",
1417
+ {
1418
+ className: cn3(
1419
+ "rich-text-at-mention-avatar-stack",
1420
+ "rich-text-at-mention-avatar-stack--agent-only"
1421
+ ),
1422
+ "aria-hidden": "true",
1423
+ children: /* @__PURE__ */ jsx5(
1424
+ "span",
1425
+ {
1426
+ className: "rich-text-at-mention-avatar rich-text-at-mention-avatar--agent",
1427
+ ...mentionRowDataAttribute(dataAttributeMode, "agentAvatar", "true"),
1428
+ children: /* @__PURE__ */ jsx5(
1429
+ "img",
1430
+ {
1431
+ src: item.agentIconUrl,
1432
+ alt: "",
1433
+ className: "rich-text-at-mention-row__media",
1434
+ decoding: "async",
1435
+ loading: "lazy",
1436
+ draggable: false
1437
+ }
1438
+ )
1439
+ }
1440
+ )
1441
+ }
1442
+ );
1443
+ }
1444
+ const userAvatarUrl = item.userAvatarUrl?.trim() ?? "";
1445
+ const placeholderUrl = item.userAvatarPlaceholderUrl;
1446
+ const userImageUrl = userAvatarUrl || placeholderUrl;
1447
+ return /* @__PURE__ */ jsxs3("span", { className: "rich-text-at-mention-avatar-stack", "aria-hidden": "true", children: [
1448
+ /* @__PURE__ */ jsx5(
1449
+ "span",
1450
+ {
1451
+ className: "rich-text-at-mention-avatar rich-text-at-mention-avatar--user",
1452
+ ...mentionRowDataAttribute(dataAttributeMode, "userAvatar", "true"),
1453
+ children: /* @__PURE__ */ jsx5(
1454
+ "img",
1455
+ {
1456
+ src: userImageUrl,
1457
+ alt: "",
1458
+ className: cn3(
1459
+ "rich-text-at-mention-row__media",
1460
+ !userAvatarUrl && classNames.avatarImgUserPlaceholder
1461
+ ),
1462
+ decoding: "async",
1463
+ loading: "lazy",
1464
+ referrerPolicy: "no-referrer",
1465
+ draggable: false,
1466
+ onError: (event) => {
1467
+ if (event.currentTarget.dataset.fallbackAvatarApplied === "true") {
1468
+ return;
1469
+ }
1470
+ event.currentTarget.dataset.fallbackAvatarApplied = "true";
1471
+ event.currentTarget.src = placeholderUrl;
1472
+ event.currentTarget.classList.add(
1473
+ classNames.avatarImgUserPlaceholder
1474
+ );
1475
+ }
1476
+ }
1477
+ )
1478
+ }
1479
+ ),
1480
+ /* @__PURE__ */ jsx5(
1481
+ "span",
1482
+ {
1483
+ className: "rich-text-at-mention-avatar rich-text-at-mention-avatar--agent",
1484
+ ...mentionRowDataAttribute(dataAttributeMode, "agentAvatar", "true"),
1485
+ children: /* @__PURE__ */ jsx5(
1486
+ "img",
1487
+ {
1488
+ src: item.agentIconUrl,
1489
+ alt: "",
1490
+ className: "rich-text-at-mention-row__media",
1491
+ decoding: "async",
1492
+ loading: "lazy",
1493
+ draggable: false
1494
+ }
1495
+ )
1496
+ }
1497
+ )
1498
+ ] });
1499
+ }
1500
+ function MentionSessionTitle({
1501
+ item
1502
+ }) {
1503
+ return /* @__PURE__ */ jsxs3(Fragment2, { children: [
1504
+ /* @__PURE__ */ jsx5("span", { className: "rich-text-at-mention-row__session-participant", children: item.participant }),
1505
+ /* @__PURE__ */ jsx5("span", { className: "rich-text-at-mention-row__session-summary", children: item.summary ?? "" })
1506
+ ] });
1507
+ }
1508
+ function MentionStatusBadge({
1509
+ statusTag,
1510
+ dataAttributeMode
1511
+ }) {
1512
+ if (statusTag.variant === "issue") {
1513
+ return /* @__PURE__ */ jsx5(
1514
+ Badge,
1515
+ {
1516
+ variant: "secondary",
1517
+ size: "sm",
1518
+ className: cn3(
1519
+ "rich-text-at-mention-status rich-text-at-mention-status--issue",
1520
+ mentionStatusBadgeClassName({
1521
+ tone: statusTag.tone,
1522
+ variant: "issue"
1523
+ })
1524
+ ),
1525
+ ...mentionRowDataAttribute(dataAttributeMode, "statusTag", "true"),
1526
+ ...statusTag.dataStatus ? { "data-status": statusTag.dataStatus } : {},
1527
+ "data-tone": statusTag.tone,
1528
+ children: statusTag.label
1529
+ }
1530
+ );
1531
+ }
1532
+ return /* @__PURE__ */ jsxs3(
1533
+ Badge,
1534
+ {
1535
+ variant: "secondary",
1536
+ size: "sm",
1537
+ className: cn3(
1538
+ "rich-text-at-mention-status rich-text-at-mention-status--activity",
1539
+ mentionStatusBadgeClassName({
1540
+ tone: statusTag.tone,
1541
+ variant: "activity"
1542
+ })
1543
+ ),
1544
+ ...mentionRowDataAttribute(dataAttributeMode, "statusTag", "true"),
1545
+ ...statusTag.dataStatus ? { "data-status": statusTag.dataStatus } : {},
1546
+ "data-tone": statusTag.tone,
1547
+ title: statusTag.label,
1548
+ children: [
1549
+ /* @__PURE__ */ jsx5(
1550
+ StatusDot,
1551
+ {
1552
+ tone: statusTag.tone === "purple" ? "neutral" : statusTag.tone,
1553
+ pulse: statusTag.pulse ?? false,
1554
+ size: "xs",
1555
+ title: statusTag.label
1556
+ }
1557
+ ),
1558
+ /* @__PURE__ */ jsx5("span", { children: statusTag.label })
1559
+ ]
1560
+ }
1561
+ );
1562
+ }
1563
+
1564
+ // src/at-panel/mentionReferenceIcon.ts
1565
+ import React from "react";
1566
+ var REFERENCE_LEADING_STYLE = {
1567
+ alignItems: "center",
1568
+ background: "var(--bg-block, var(--block, #0000000a))",
1569
+ borderRadius: "8px",
1570
+ color: "var(--rich-text-at-mention-text-secondary, currentColor)",
1571
+ display: "inline-grid",
1572
+ flex: "0 0 32px",
1573
+ height: "32px",
1574
+ justifyItems: "center",
1575
+ overflow: "hidden",
1576
+ width: "32px"
1577
+ };
1578
+ var REFERENCE_IMAGE_STYLE = {
1579
+ display: "block",
1580
+ height: "100%",
1581
+ objectFit: "cover",
1582
+ width: "100%"
1583
+ };
1584
+ var REFERENCE_ICON_STYLE = {
1585
+ display: "block",
1586
+ height: "18px",
1587
+ width: "18px"
1588
+ };
1589
+ function resolveMentionReferenceImageUrl(input) {
1590
+ return input.thumbnailUrl?.trim() || input.iconUrl?.trim() || void 0;
1591
+ }
1592
+ function renderMentionReferenceLeading(input) {
1593
+ const imageUrl = resolveMentionReferenceImageUrl(input);
1594
+ return React.createElement(
1595
+ "span",
1596
+ {
1597
+ "aria-hidden": "true",
1598
+ className: input.className,
1599
+ "data-rich-text-at-mention-reference-kind": input.kind,
1600
+ "data-rich-text-at-mention-reference-leading": "true",
1601
+ style: REFERENCE_LEADING_STYLE
1602
+ },
1603
+ imageUrl ? React.createElement("img", {
1604
+ alt: "",
1605
+ className: input.imageClassName,
1606
+ decoding: "async",
1607
+ draggable: false,
1608
+ loading: "lazy",
1609
+ src: imageUrl,
1610
+ style: REFERENCE_IMAGE_STYLE
1611
+ }) : renderMentionReferenceFallbackIcon(input)
1612
+ );
1613
+ }
1614
+ function renderMentionReferenceFallbackIcon(input) {
1615
+ return React.createElement(
1616
+ "svg",
1617
+ {
1618
+ "aria-hidden": "true",
1619
+ fill: "none",
1620
+ stroke: "currentColor",
1621
+ strokeLinecap: "round",
1622
+ strokeLinejoin: "round",
1623
+ strokeWidth: 1.8,
1624
+ style: REFERENCE_ICON_STYLE,
1625
+ viewBox: "0 0 24 24"
1626
+ },
1627
+ ...referenceIconPaths(input)
1628
+ );
1629
+ }
1630
+ function referenceIconPaths(input) {
1631
+ if (input.kind === "workspace-app") {
1632
+ return [
1633
+ React.createElement("rect", {
1634
+ height: 7,
1635
+ key: "a",
1636
+ rx: 1.5,
1637
+ width: 7,
1638
+ x: 4,
1639
+ y: 4
1640
+ }),
1641
+ React.createElement("rect", {
1642
+ height: 7,
1643
+ key: "b",
1644
+ rx: 1.5,
1645
+ width: 7,
1646
+ x: 13,
1647
+ y: 4
1648
+ }),
1649
+ React.createElement("rect", {
1650
+ height: 7,
1651
+ key: "c",
1652
+ rx: 1.5,
1653
+ width: 7,
1654
+ x: 4,
1655
+ y: 13
1656
+ }),
1657
+ React.createElement("rect", {
1658
+ height: 7,
1659
+ key: "d",
1660
+ rx: 1.5,
1661
+ width: 7,
1662
+ x: 13,
1663
+ y: 13
1664
+ })
1665
+ ];
1666
+ }
1667
+ if (input.kind === "workspace-issue") {
1668
+ return [
1669
+ React.createElement("circle", { cx: 12, cy: 12, key: "a", r: 8 }),
1670
+ React.createElement("path", { d: "M8.5 12.5 11 15l4.5-5", key: "b" })
1671
+ ];
1672
+ }
1673
+ if (input.kind === "agent-session") {
1674
+ return [
1675
+ React.createElement("path", {
1676
+ d: "M6 9.5a5 5 0 0 1 5-5h2a5 5 0 0 1 5 5v2.5a5 5 0 0 1-5 5h-1.5L8 19v-2.4A5 5 0 0 1 6 12z",
1677
+ key: "a"
1678
+ })
1679
+ ];
1680
+ }
1681
+ if (input.kind === "file" || input.kind === "agent-generated-file" || input.fileVisualKind != null) {
1682
+ if (input.fileVisualKind === "folder" || input.fileVisualKind === "back") {
1683
+ return [
1684
+ React.createElement("path", {
1685
+ d: "M3.5 7.5h6l2 2h9v8a2 2 0 0 1-2 2h-13a2 2 0 0 1-2-2z",
1686
+ key: "a"
1687
+ })
1688
+ ];
1689
+ }
1690
+ if (input.fileVisualKind === "image") {
1691
+ return [
1692
+ React.createElement("rect", {
1693
+ height: 14,
1694
+ key: "a",
1695
+ rx: 2,
1696
+ width: 16,
1697
+ x: 4,
1698
+ y: 5
1699
+ }),
1700
+ React.createElement("path", {
1701
+ d: "m7 16 3.5-3.5 2.5 2.5 2-2 2 3",
1702
+ key: "b"
1703
+ }),
1704
+ React.createElement("circle", { cx: 9, cy: 9, key: "c", r: 1 })
1705
+ ];
1706
+ }
1707
+ if (input.fileVisualKind === "video") {
1708
+ return [
1709
+ React.createElement("rect", {
1710
+ height: 14,
1711
+ key: "a",
1712
+ rx: 2,
1713
+ width: 16,
1714
+ x: 4,
1715
+ y: 5
1716
+ }),
1717
+ React.createElement("path", {
1718
+ d: "m10 9 5 3-5 3z",
1719
+ key: "b"
1720
+ })
1721
+ ];
1722
+ }
1723
+ return [
1724
+ React.createElement("path", {
1725
+ d: "M7 3.5h7l4 4V20a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V4.5a1 1 0 0 1 1-1z",
1726
+ key: "a"
1727
+ }),
1728
+ React.createElement("path", { d: "M14 3.5v4h4", key: "b" })
1729
+ ];
1730
+ }
1731
+ return [
1732
+ React.createElement("path", {
1733
+ d: "M9.5 14.5 14.5 9.5",
1734
+ key: "a"
1735
+ }),
1736
+ React.createElement("path", {
1737
+ d: "M8.5 10.5 7 12a3.5 3.5 0 0 0 5 5l1.5-1.5",
1738
+ key: "b"
1739
+ }),
1740
+ React.createElement("path", {
1741
+ d: "M15.5 13.5 17 12a3.5 3.5 0 0 0-5-5l-1.5 1.5",
1742
+ key: "c"
1743
+ })
1744
+ ];
1745
+ }
1746
+
1747
+ // src/at-panel/mentionTriggerRowItem.ts
1748
+ var SUPPORTED_MENTION_TRIGGER_ROW_PROVIDER_IDS = /* @__PURE__ */ new Set([
1749
+ "agent-generated-file",
1750
+ "agent-session",
1751
+ "file",
1752
+ "workspace-app",
1753
+ "workspace-issue"
1754
+ ]);
1755
+ function richTextTriggerQueryMatchToMentionRowItem(match, options = {}) {
1756
+ const presentation = mentionPresentation(match);
1757
+ const label = resolveMentionRowLabel(match);
1758
+ const description = resolveMentionRowDescription(match, options);
1759
+ const providerKind = mentionReferenceProviderKind(match.providerId);
1760
+ const iconUrl = resolveMentionRowIconUrl(match, presentation, options);
1761
+ const thumbnailUrl = resolveMentionRowThumbnailUrl(
1762
+ match,
1763
+ presentation,
1764
+ options
1765
+ );
1766
+ const fileVisualKind = isFileMentionProvider(match.providerId) ? resolveMentionRowFileVisualKind(match, options) : void 0;
1767
+ const customLeading = options.renderLeading?.({
1768
+ description,
1769
+ fileVisualKind,
1770
+ iconUrl,
1771
+ label,
1772
+ match,
1773
+ providerKind,
1774
+ thumbnailUrl
1775
+ });
1776
+ if (customLeading !== void 0) {
1777
+ return mentionRowPlainItem({
1778
+ description,
1779
+ label,
1780
+ leading: customLeading
1781
+ });
1782
+ }
1783
+ if (match.providerId === "workspace-app") {
1784
+ return {
1785
+ kind: "app",
1786
+ description,
1787
+ iconUrl,
1788
+ name: label
1789
+ };
1790
+ }
1791
+ if (match.providerId === "workspace-issue") {
1792
+ return {
1793
+ kind: "issue",
1794
+ creatorName: description,
1795
+ statusTag: resolveMentionRowStatusTag(match, "issue", options),
1796
+ title: label
1797
+ };
1798
+ }
1799
+ if (isFileMentionProvider(match.providerId)) {
1800
+ const entryKind = resolveFileEntryKind(match, options);
1801
+ const mentionNavigation = resolveFileMentionNavigation(match, options);
1802
+ const visualKind = fileVisualKind ?? "document";
1803
+ return {
1804
+ kind: "file",
1805
+ childCountLabel: normalizedText(options.getChildCountLabel?.(match)),
1806
+ entryKind,
1807
+ mentionNavigation,
1808
+ name: label,
1809
+ thumbnailUrl: resolveMentionFileThumbnailUrl({
1810
+ thumbnailUrl,
1811
+ visualKind
1812
+ }) ?? null,
1813
+ visualKind
1814
+ };
1815
+ }
1816
+ if (match.providerId === "agent-session") {
1817
+ const session = agentSessionMentionRowItem(match, {
1818
+ description,
1819
+ iconUrl,
1820
+ options,
1821
+ presentation
1822
+ });
1823
+ if (session) {
1824
+ return session;
1825
+ }
1826
+ }
1827
+ return mentionRowPlainItem({
1828
+ description,
1829
+ label,
1830
+ leading: renderMentionReferenceLeading({
1831
+ fileVisualKind,
1832
+ iconUrl,
1833
+ kind: providerKind,
1834
+ label,
1835
+ thumbnailUrl
1836
+ })
1837
+ });
1838
+ }
1839
+ function isMentionTriggerRowProviderId(providerId) {
1840
+ return SUPPORTED_MENTION_TRIGGER_ROW_PROVIDER_IDS.has(providerId);
1841
+ }
1842
+ function workspaceAppIconFallbackUrlFromTriggerMatch(match) {
1843
+ const appId = mentionEntityId(match) || match.key.trim();
1844
+ if (!appId) {
1845
+ return null;
1846
+ }
1847
+ return `tutti://workspace-apps/${encodeURIComponent(appId)}/icon.png`;
1848
+ }
1849
+ function mentionRowStatusTagFromPresentation(match, input) {
1850
+ const presentation = mentionPresentation(match);
1851
+ const label = normalizedText(presentation?.statusLabel);
1852
+ if (!label) {
1853
+ return null;
1854
+ }
1855
+ const dataStatus = normalizedText(presentation?.statusDataStatus) ?? normalizedText(presentation?.status);
1856
+ return {
1857
+ dataStatus: dataStatus ?? void 0,
1858
+ label,
1859
+ pulse: presentation?.statusPulse === "true",
1860
+ tone: input.tone ?? "neutral",
1861
+ variant: input.variant
1862
+ };
1863
+ }
1864
+ function agentSessionMentionRowItem(match, input) {
1865
+ const agentIconUrl = normalizedText(input.options.getAgentIconUrl?.(match)) ?? normalizedText(input.presentation?.agentIconUrl) ?? input.iconUrl;
1866
+ const userAvatarPlaceholderUrl = normalizedText(input.options.getUserAvatarPlaceholderUrl?.(match)) ?? normalizedText(input.presentation?.userAvatarPlaceholderUrl);
1867
+ if (!agentIconUrl || !userAvatarPlaceholderUrl) {
1868
+ return null;
1869
+ }
1870
+ return {
1871
+ kind: "session",
1872
+ agentIconUrl,
1873
+ participant: normalizedText(input.presentation?.participant) ?? resolveMentionRowLabel(match),
1874
+ statusTag: resolveMentionRowStatusTag(match, "activity", input.options),
1875
+ summary: input.description,
1876
+ userAvatarPlaceholderUrl,
1877
+ userAvatarUrl: normalizedText(input.options.getUserAvatarUrl?.(match)) ?? null
1878
+ };
1879
+ }
1880
+ function mentionRowPlainItem(input) {
1881
+ return {
1882
+ kind: "plain",
1883
+ description: input.description,
1884
+ label: input.label,
1885
+ leading: input.leading
1886
+ };
1887
+ }
1888
+ function resolveMentionRowLabel(match) {
1889
+ return normalizedText(
1890
+ match.insertResult.kind === "mention" ? match.insertResult.mention.label : match.insertResult.kind === "markdown-link" ? match.insertResult.label : void 0
1891
+ ) ?? normalizedText(match.label) ?? match.key;
1892
+ }
1893
+ function resolveMentionRowDescription(match, options) {
1894
+ const presentation = mentionPresentation(match);
1895
+ return normalizedText(options.getDescription?.(match)) ?? normalizedText(presentation?.description) ?? normalizedText(presentation?.subtitle) ?? normalizedText(match.subtitle) ?? null;
1896
+ }
1897
+ function resolveMentionRowIconUrl(match, presentation, options) {
1898
+ return normalizedText(presentation?.iconUrl) ?? normalizedText(match.iconUrl) ?? (match.providerId === "workspace-app" ? normalizedText(options.getWorkspaceAppIconFallbackUrl?.(match)) ?? workspaceAppIconFallbackUrlFromTriggerMatch(match) : null) ?? null;
1899
+ }
1900
+ function resolveMentionRowThumbnailUrl(match, presentation, options) {
1901
+ return normalizedText(options.getFileThumbnailUrl?.(match)) ?? normalizedText(presentation?.thumbnailUrl) ?? normalizedText(readStringProperty(match.item, "thumbnailUrl")) ?? (isFileMentionProvider(match.providerId) ? normalizedText(match.iconUrl) : null) ?? null;
1902
+ }
1903
+ function resolveMentionRowFileVisualKind(match, options) {
1904
+ const override = options.getFileVisualKind?.(match);
1905
+ if (override) {
1906
+ return override;
1907
+ }
1908
+ return resolveMentionFileVisualKind({
1909
+ baseVisualKind: inferMentionFileVisualKind(
1910
+ markdownLinkHref(match) ?? normalizedText(match.subtitle) ?? normalizedText(match.label) ?? match.key
1911
+ ),
1912
+ entryKind: resolveFileEntryKind(match, options),
1913
+ mentionNavigation: resolveFileMentionNavigation(match, options)
1914
+ });
1915
+ }
1916
+ function resolveMentionRowStatusTag(match, variant, options) {
1917
+ return options.getStatusTag?.(match, variant) ?? mentionRowStatusTagFromPresentation(match, { variant });
1918
+ }
1919
+ function resolveFileEntryKind(match, options) {
1920
+ return normalizedText(options.getFileEntryKind?.(match)) ?? normalizedText(readStringProperty(match.item, "entryKind")) ?? normalizedText(readStringProperty(match.item, "kind")) ?? null;
1921
+ }
1922
+ function resolveFileMentionNavigation(match, options) {
1923
+ return normalizedText(options.getFileMentionNavigation?.(match)) ?? normalizedText(readStringProperty(match.item, "mentionNavigation")) ?? null;
1924
+ }
1925
+ function mentionReferenceProviderKind(providerId) {
1926
+ if (providerId === "workspace-app") {
1927
+ return "workspace-app";
1928
+ }
1929
+ if (providerId === "workspace-issue") {
1930
+ return "workspace-issue";
1931
+ }
1932
+ if (providerId === "agent-session") {
1933
+ return "agent-session";
1934
+ }
1935
+ if (providerId === "agent-generated-file") {
1936
+ return "agent-generated-file";
1937
+ }
1938
+ if (providerId === "file") {
1939
+ return "file";
1940
+ }
1941
+ return "generic";
1942
+ }
1943
+ function isFileMentionProvider(providerId) {
1944
+ return providerId === "file" || providerId === "agent-generated-file";
1945
+ }
1946
+ function mentionPresentation(match) {
1947
+ return match.insertResult.kind === "mention" ? match.insertResult.mention.presentation ?? null : null;
1948
+ }
1949
+ function mentionEntityId(match) {
1950
+ return match.insertResult.kind === "mention" ? normalizedText(match.insertResult.mention.entityId) ?? null : null;
1951
+ }
1952
+ function markdownLinkHref(match) {
1953
+ return match.insertResult.kind === "markdown-link" ? normalizedText(match.insertResult.href) ?? null : null;
1954
+ }
1955
+ function inferMentionFileVisualKind(pathOrName) {
1956
+ const normalized = pathOrName.trim().toLowerCase();
1957
+ if (!normalized) {
1958
+ return "document";
1959
+ }
1960
+ if (normalized.endsWith("/")) {
1961
+ return "folder";
1962
+ }
1963
+ const extension = normalized.match(/\.([a-z0-9]+)(?:[?#].*)?$/)?.[1] ?? "";
1964
+ if (["apng", "avif", "gif", "jpeg", "jpg", "png", "svg", "webp"].includes(
1965
+ extension
1966
+ )) {
1967
+ return "image";
1968
+ }
1969
+ if (["avi", "m4v", "mkv", "mov", "mp4", "webm"].includes(extension)) {
1970
+ return "video";
1971
+ }
1972
+ if (["markdown", "md", "mdx"].includes(extension)) {
1973
+ return "markdown";
1974
+ }
1975
+ if ([
1976
+ "c",
1977
+ "cpp",
1978
+ "css",
1979
+ "go",
1980
+ "h",
1981
+ "hpp",
1982
+ "html",
1983
+ "java",
1984
+ "js",
1985
+ "jsx",
1986
+ "json",
1987
+ "kt",
1988
+ "php",
1989
+ "py",
1990
+ "rb",
1991
+ "rs",
1992
+ "sh",
1993
+ "sql",
1994
+ "swift",
1995
+ "toml",
1996
+ "ts",
1997
+ "tsx",
1998
+ "xml",
1999
+ "yaml",
2000
+ "yml"
2001
+ ].includes(extension)) {
2002
+ return "code";
2003
+ }
2004
+ return "document";
2005
+ }
2006
+ function readStringProperty(value, key) {
2007
+ if (value === null || typeof value !== "object") {
2008
+ return void 0;
2009
+ }
2010
+ const record = value;
2011
+ return typeof record[key] === "string" ? record[key] : void 0;
2012
+ }
2013
+ function normalizedText(value) {
2014
+ const normalized = value?.trim() ?? "";
2015
+ return normalized || null;
2016
+ }
2017
+
2018
+ // src/at-panel/useAtPanelKeyboard.ts
2019
+ function makeAtPanelKeyDown(actions) {
2020
+ return (event) => {
2021
+ if (event.key === "ArrowDown") {
2022
+ event.preventDefault();
2023
+ actions.moveSelection(1);
2024
+ return true;
2025
+ }
2026
+ if (event.key === "ArrowUp") {
2027
+ event.preventDefault();
2028
+ actions.moveSelection(-1);
2029
+ return true;
2030
+ }
2031
+ if (event.key === "Escape") {
2032
+ event.preventDefault();
2033
+ actions.close();
2034
+ return true;
2035
+ }
2036
+ if (event.key === "Tab" && actions.cycleFilter) {
2037
+ event.preventDefault();
2038
+ actions.cycleFilter(event.shiftKey ? -1 : 1);
2039
+ return true;
2040
+ }
2041
+ if (event.key === "ArrowRight" && actions.navigateHierarchy) {
2042
+ if (actions.navigateHierarchy(1)) {
2043
+ event.preventDefault();
2044
+ return true;
2045
+ }
2046
+ return false;
2047
+ }
2048
+ if (event.key === "ArrowLeft" && actions.navigateHierarchy) {
2049
+ if (actions.navigateHierarchy(-1)) {
2050
+ event.preventDefault();
2051
+ return true;
2052
+ }
2053
+ return false;
2054
+ }
2055
+ if (event.key === "Enter") {
2056
+ event.preventDefault();
2057
+ actions.commitSelection();
2058
+ return true;
2059
+ }
2060
+ return false;
2061
+ };
2062
+ }
2063
+ function useAtPanelKeyboard(actions) {
2064
+ return makeAtPanelKeyDown(actions);
2065
+ }
2066
+
2067
+ export {
2068
+ MentionPalette,
2069
+ createMentionPaletteStateAdapter,
2070
+ MentionPaletteFromState,
2071
+ MentionPaletteSelectIndicator,
2072
+ MentionPaletteMultiSelectFooter,
2073
+ renderMentionRow,
2074
+ resolveMentionReferenceImageUrl,
2075
+ renderMentionReferenceLeading,
2076
+ richTextTriggerQueryMatchToMentionRowItem,
2077
+ isMentionTriggerRowProviderId,
2078
+ workspaceAppIconFallbackUrlFromTriggerMatch,
2079
+ mentionRowStatusTagFromPresentation,
2080
+ makeAtPanelKeyDown,
2081
+ useAtPanelKeyboard
2082
+ };
2083
+ //# sourceMappingURL=chunk-SH3QPYB2.js.map