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