@tangle-network/sandbox-ui 0.85.1 → 0.87.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat.d.ts +51 -2
- package/dist/chat.js +5 -1
- package/dist/{chunk-666CEEHW.js → chunk-R5OVYRIY.js} +61 -15
- package/dist/chunk-WGOOQAW3.js +12 -0
- package/dist/globals.css +9 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -1
- package/dist/mention-editor-6PITHKPR.js +490 -0
- package/dist/styles.css +9 -0
- package/package.json +5 -2
package/dist/chat.d.ts
CHANGED
|
@@ -307,6 +307,38 @@ declare function validateComposerFiles(files: File[] | FileList, config: Compose
|
|
|
307
307
|
rejected: ComposerFileRejection[];
|
|
308
308
|
};
|
|
309
309
|
|
|
310
|
+
/** A selectable item in the `@`-mention popover. Generic — file semantics live
|
|
311
|
+
* in the consuming app, not here. */
|
|
312
|
+
interface MentionItem {
|
|
313
|
+
id: string;
|
|
314
|
+
label: string;
|
|
315
|
+
detail?: string;
|
|
316
|
+
kind?: string;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Opt-in `@`-mention support. Present ⇒ the composer swaps its textarea for a
|
|
320
|
+
* TipTap rich input that renders mentions as atomic pills; absent ⇒ exactly
|
|
321
|
+
* today's plain-textarea behavior. Data-agnostic: the app supplies items.
|
|
322
|
+
*/
|
|
323
|
+
interface AgentComposerMention {
|
|
324
|
+
/** The character that opens the popover. Default "@". */
|
|
325
|
+
trigger?: string;
|
|
326
|
+
/** Async provider called with the query typed after the trigger. */
|
|
327
|
+
fetchItems: (query: string) => Promise<MentionItem[]>;
|
|
328
|
+
/** Fired with the mentions currently in the document whenever they change. */
|
|
329
|
+
onMentionsChange?: (mentions: MentionItem[]) => void;
|
|
330
|
+
/** Custom row renderer for a popover item. */
|
|
331
|
+
renderItem?: (item: MentionItem) => React.ReactNode;
|
|
332
|
+
/** Shown when a fetch resolves to zero items. Default "No matches". */
|
|
333
|
+
emptyText?: string;
|
|
334
|
+
/**
|
|
335
|
+
* Extra classes merged onto the suggestion popover's root element
|
|
336
|
+
* (`role="listbox"`), applied last so they win over the component's own —
|
|
337
|
+
* the supported way to retheme the popover instead of targeting it by its
|
|
338
|
+
* ARIA attributes.
|
|
339
|
+
*/
|
|
340
|
+
popoverClassName?: string;
|
|
341
|
+
}
|
|
310
342
|
/** A staged attachment shown as a chip above the input. */
|
|
311
343
|
interface ComposerFile {
|
|
312
344
|
id: string;
|
|
@@ -406,6 +438,12 @@ interface AgentComposerProps {
|
|
|
406
438
|
* message body becomes. Default false.
|
|
407
439
|
*/
|
|
408
440
|
canSubmitAttachmentsOnly?: boolean;
|
|
441
|
+
/**
|
|
442
|
+
* Opt-in `@`-mentions. When set, the textarea is replaced by a rich input
|
|
443
|
+
* that renders mentions as atomic pills and serializes them to `@<id>` in
|
|
444
|
+
* `value`. Omit for exactly today's plain-textarea behavior.
|
|
445
|
+
*/
|
|
446
|
+
mention?: AgentComposerMention;
|
|
409
447
|
/** Cmd/Ctrl+L focuses the input and shows a hint. Default false. */
|
|
410
448
|
focusShortcut?: boolean;
|
|
411
449
|
/** Minimum textarea rows before it grows. Default 2. */
|
|
@@ -432,7 +470,18 @@ interface AgentComposerProps {
|
|
|
432
470
|
* button (`busy` + `onCancel`), a `controls` override slot for bespoke
|
|
433
471
|
* picker rows, and a Cmd/Ctrl+L focus shortcut.
|
|
434
472
|
*/
|
|
435
|
-
declare function AgentComposer({ value, onChange, onSubmit, placeholder, disabled, busy, onCancel, harness, profile, model, reasoning, context, controls, trailing, onAttach, onAttachFolder, attachments, onRemoveFile, onRetryFile, accept, onRejectFiles, dropTitle, dropDescription, canSubmitWhileBusy, canSubmitAttachmentsOnly, focusShortcut, minRows, maxHeight, className, sendLabel, autoFocus, }: AgentComposerProps): react_jsx_runtime.JSX.Element;
|
|
473
|
+
declare function AgentComposer({ value, onChange, onSubmit, placeholder, disabled, busy, onCancel, harness, profile, model, reasoning, context, controls, trailing, onAttach, onAttachFolder, attachments, onRemoveFile, onRetryFile, accept, onRejectFiles, dropTitle, dropDescription, canSubmitWhileBusy, canSubmitAttachmentsOnly, mention, focusShortcut, minRows, maxHeight, className, sendLabel, autoFocus, }: AgentComposerProps): react_jsx_runtime.JSX.Element;
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Visual contract for a rendered mention pill. The TipTap editor extension in
|
|
477
|
+
* `mention-editor.tsx` styles its inline atom node with this, and any other
|
|
478
|
+
* surface that renders a resolved mention (e.g. a sent-message transcript)
|
|
479
|
+
* should use the same constant so the two stay one visual contract instead of
|
|
480
|
+
* silently drifting apart. Kept in its own module (no TipTap import) so
|
|
481
|
+
* importing it never pulls the lazily-loaded editor chunk into a consumer's
|
|
482
|
+
* bundle.
|
|
483
|
+
*/
|
|
484
|
+
declare const MENTION_PILL_CLASS: string;
|
|
436
485
|
|
|
437
486
|
/**
|
|
438
487
|
* Whether `harness` can run `modelId`. Router-backed harnesses, and provider-less
|
|
@@ -546,4 +595,4 @@ declare function createFetchTransport(opts: {
|
|
|
546
595
|
fetchImpl?: typeof fetch;
|
|
547
596
|
}): ArtifactAgentDockTransport;
|
|
548
597
|
|
|
549
|
-
export { AgentComposer, type AgentComposerProps, type AgentProfileCapability, type AgentProfileDraft, type AgentProfileOption, AgentProfilePicker, type AgentProfilePickerProps, AgentSessionControls, type AgentSessionControlsProps, type AgentSessionHarnessControl, type AgentSessionModelControl, type AgentSessionProfileControl, type AgentSessionReasoningControl, ArtifactAgentDock, type ArtifactAgentDockProps, type ArtifactAgentDockTransport, type ArtifactDockMessage, type ArtifactDockStreamEvent, type ArtifactKind, type ArtifactScope, type ComposerFile, type ComposerFileRejection, type ComposerFileValidationConfig, DEFAULT_REASONING_LEVEL_OPTIONS, HARNESS_REASONING_OPTIONS, type ReasoningLevel, type ReasoningLevelOption, ReasoningLevelPicker, type ReasoningLevelPickerProps, createFetchTransport, isAcceptedType, isModelCompatibleWithHarness, snapModelToHarness, validateComposerFiles };
|
|
598
|
+
export { AgentComposer, type AgentComposerMention, type AgentComposerProps, type AgentProfileCapability, type AgentProfileDraft, type AgentProfileOption, AgentProfilePicker, type AgentProfilePickerProps, AgentSessionControls, type AgentSessionControlsProps, type AgentSessionHarnessControl, type AgentSessionModelControl, type AgentSessionProfileControl, type AgentSessionReasoningControl, ArtifactAgentDock, type ArtifactAgentDockProps, type ArtifactAgentDockTransport, type ArtifactDockMessage, type ArtifactDockStreamEvent, type ArtifactKind, type ArtifactScope, type ComposerFile, type ComposerFileRejection, type ComposerFileValidationConfig, DEFAULT_REASONING_LEVEL_OPTIONS, HARNESS_REASONING_OPTIONS, MENTION_PILL_CLASS, type MentionItem, type ReasoningLevel, type ReasoningLevelOption, ReasoningLevelPicker, type ReasoningLevelPickerProps, createFetchTransport, isAcceptedType, isModelCompatibleWithHarness, snapModelToHarness, validateComposerFiles };
|
package/dist/chat.js
CHANGED
|
@@ -19,8 +19,11 @@ import {
|
|
|
19
19
|
snapHarnessToModel,
|
|
20
20
|
snapModelToHarness,
|
|
21
21
|
validateComposerFiles
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-R5OVYRIY.js";
|
|
23
23
|
import "./chunk-TK46XFLM.js";
|
|
24
|
+
import {
|
|
25
|
+
MENTION_PILL_CLASS
|
|
26
|
+
} from "./chunk-WGOOQAW3.js";
|
|
24
27
|
import "./chunk-2T6KBMLN.js";
|
|
25
28
|
import "./chunk-R4AR3TKR.js";
|
|
26
29
|
import "./chunk-I3G23XHN.js";
|
|
@@ -36,6 +39,7 @@ export {
|
|
|
36
39
|
ChatMessage,
|
|
37
40
|
DEFAULT_REASONING_LEVEL_OPTIONS,
|
|
38
41
|
HARNESS_REASONING_OPTIONS,
|
|
42
|
+
MENTION_PILL_CLASS,
|
|
39
43
|
MessageList,
|
|
40
44
|
ReasoningLevelPicker,
|
|
41
45
|
ThinkingIndicator,
|
|
@@ -1147,6 +1147,7 @@ function imageExtension(file) {
|
|
|
1147
1147
|
const fromName = /\.([a-z0-9]+)$/i.exec(file.name)?.[1];
|
|
1148
1148
|
return fromName?.toLowerCase() ?? "png";
|
|
1149
1149
|
}
|
|
1150
|
+
var MentionEditor = React4.lazy(() => import("./mention-editor-6PITHKPR.js"));
|
|
1150
1151
|
function AgentComposer({
|
|
1151
1152
|
value,
|
|
1152
1153
|
onChange,
|
|
@@ -1173,6 +1174,7 @@ function AgentComposer({
|
|
|
1173
1174
|
dropDescription = "They attach to your next message.",
|
|
1174
1175
|
canSubmitWhileBusy = false,
|
|
1175
1176
|
canSubmitAttachmentsOnly = false,
|
|
1177
|
+
mention,
|
|
1176
1178
|
focusShortcut = false,
|
|
1177
1179
|
minRows = 2,
|
|
1178
1180
|
maxHeight = 200,
|
|
@@ -1181,6 +1183,7 @@ function AgentComposer({
|
|
|
1181
1183
|
autoFocus
|
|
1182
1184
|
}) {
|
|
1183
1185
|
const textareaRef = React4.useRef(null);
|
|
1186
|
+
const richFocusRef = React4.useRef(null);
|
|
1184
1187
|
const fileInputRef = React4.useRef(null);
|
|
1185
1188
|
const folderInputRef = React4.useRef(null);
|
|
1186
1189
|
const [dragOver, setDragOver] = React4.useState(false);
|
|
@@ -1201,17 +1204,21 @@ function AgentComposer({
|
|
|
1201
1204
|
function onKeyDown(event) {
|
|
1202
1205
|
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "l") {
|
|
1203
1206
|
event.preventDefault();
|
|
1204
|
-
|
|
1207
|
+
if (mention) richFocusRef.current?.();
|
|
1208
|
+
else textareaRef.current?.focus();
|
|
1205
1209
|
}
|
|
1206
1210
|
}
|
|
1207
1211
|
document.addEventListener("keydown", onKeyDown);
|
|
1208
1212
|
return () => document.removeEventListener("keydown", onKeyDown);
|
|
1209
|
-
}, [focusShortcut, disabled]);
|
|
1213
|
+
}, [focusShortcut, disabled, mention]);
|
|
1210
1214
|
const canSend = !disabled && (!busy || canSubmitWhileBusy) && (value.trim().length > 0 || canSubmitAttachmentsOnly && attachments.length > 0);
|
|
1211
1215
|
const submit = () => {
|
|
1212
1216
|
if (!canSend) return;
|
|
1213
1217
|
onSubmit();
|
|
1214
1218
|
};
|
|
1219
|
+
const registerRichFocus = React4.useCallback((focus) => {
|
|
1220
|
+
richFocusRef.current = focus;
|
|
1221
|
+
}, []);
|
|
1215
1222
|
const dropEnabled = Boolean(onAttach);
|
|
1216
1223
|
const handleDragEnter = (event) => {
|
|
1217
1224
|
if (!dropEnabled) return;
|
|
@@ -1254,23 +1261,28 @@ function AgentComposer({
|
|
|
1254
1261
|
const files = event.dataTransfer?.files;
|
|
1255
1262
|
if (files?.length) deliverFiles(Array.from(files));
|
|
1256
1263
|
};
|
|
1264
|
+
const renamePastedFiles = (files) => files.map((file) => {
|
|
1265
|
+
if (file.type.startsWith("image/") && isGenericImageName(file.name)) {
|
|
1266
|
+
pastedImageCount.current += 1;
|
|
1267
|
+
return new File(
|
|
1268
|
+
[file],
|
|
1269
|
+
`pasted-image-${pastedImageCount.current}.${imageExtension(file)}`,
|
|
1270
|
+
{ type: file.type }
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
1273
|
+
return file;
|
|
1274
|
+
});
|
|
1257
1275
|
const handlePaste = (event) => {
|
|
1258
1276
|
if (!onAttach) return;
|
|
1259
1277
|
const clipboardFiles = event.clipboardData?.files;
|
|
1260
1278
|
if (!clipboardFiles || clipboardFiles.length === 0) return;
|
|
1261
1279
|
event.preventDefault();
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
{ type: file.type }
|
|
1269
|
-
);
|
|
1270
|
-
}
|
|
1271
|
-
return file;
|
|
1272
|
-
});
|
|
1273
|
-
deliverFiles(files);
|
|
1280
|
+
deliverFiles(renamePastedFiles(Array.from(clipboardFiles)));
|
|
1281
|
+
};
|
|
1282
|
+
const handleEditorPasteFiles = (clipboardFiles) => {
|
|
1283
|
+
if (!onAttach || clipboardFiles.length === 0) return false;
|
|
1284
|
+
deliverFiles(renamePastedFiles(Array.from(clipboardFiles)));
|
|
1285
|
+
return true;
|
|
1274
1286
|
};
|
|
1275
1287
|
const folderChips = attachments.filter((file) => file.kind === "folder");
|
|
1276
1288
|
const fileChips = attachments.filter((file) => file.kind !== "folder");
|
|
@@ -1353,7 +1365,41 @@ function AgentComposer({
|
|
|
1353
1365
|
file.id
|
|
1354
1366
|
);
|
|
1355
1367
|
}) }),
|
|
1356
|
-
/* @__PURE__ */ jsx4(
|
|
1368
|
+
mention ? /* @__PURE__ */ jsx4(
|
|
1369
|
+
React4.Suspense,
|
|
1370
|
+
{
|
|
1371
|
+
fallback: /* @__PURE__ */ jsx4(
|
|
1372
|
+
"textarea",
|
|
1373
|
+
{
|
|
1374
|
+
rows: minRows,
|
|
1375
|
+
value,
|
|
1376
|
+
disabled: true,
|
|
1377
|
+
placeholder,
|
|
1378
|
+
"aria-label": "Message input",
|
|
1379
|
+
className: cn(
|
|
1380
|
+
"w-full resize-none bg-transparent px-2 py-1 text-sm leading-relaxed text-foreground outline-none",
|
|
1381
|
+
"placeholder:text-muted-foreground"
|
|
1382
|
+
)
|
|
1383
|
+
}
|
|
1384
|
+
),
|
|
1385
|
+
children: /* @__PURE__ */ jsx4(
|
|
1386
|
+
MentionEditor,
|
|
1387
|
+
{
|
|
1388
|
+
value,
|
|
1389
|
+
onChange,
|
|
1390
|
+
onSubmit: submit,
|
|
1391
|
+
placeholder,
|
|
1392
|
+
disabled,
|
|
1393
|
+
autoFocus,
|
|
1394
|
+
minRows,
|
|
1395
|
+
maxHeight,
|
|
1396
|
+
mention,
|
|
1397
|
+
registerFocus: registerRichFocus,
|
|
1398
|
+
onPasteFiles: onAttach ? handleEditorPasteFiles : void 0
|
|
1399
|
+
}
|
|
1400
|
+
)
|
|
1401
|
+
}
|
|
1402
|
+
) : /* @__PURE__ */ jsx4(
|
|
1357
1403
|
"textarea",
|
|
1358
1404
|
{
|
|
1359
1405
|
ref: textareaRef,
|
package/dist/globals.css
CHANGED
|
@@ -1292,6 +1292,9 @@
|
|
|
1292
1292
|
.min-w-\[15rem\] {
|
|
1293
1293
|
min-width: 15rem;
|
|
1294
1294
|
}
|
|
1295
|
+
.min-w-\[16rem\] {
|
|
1296
|
+
min-width: 16rem;
|
|
1297
|
+
}
|
|
1295
1298
|
.min-w-\[20px\] {
|
|
1296
1299
|
min-width: 20px;
|
|
1297
1300
|
}
|
|
@@ -2034,6 +2037,12 @@
|
|
|
2034
2037
|
border-color: color-mix(in oklab, var(--color-primary) 30%, transparent);
|
|
2035
2038
|
}
|
|
2036
2039
|
}
|
|
2040
|
+
.border-primary\/40 {
|
|
2041
|
+
border-color: color-mix(in srgb, hsl(var(--primary)) 40%, transparent);
|
|
2042
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
2043
|
+
border-color: color-mix(in oklab, var(--color-primary) 40%, transparent);
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2037
2046
|
.border-primary\/50 {
|
|
2038
2047
|
border-color: color-mix(in srgb, hsl(var(--primary)) 50%, transparent);
|
|
2039
2048
|
@supports (color: color-mix(in lab, red, red)) {
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { AgentWorkbench, ApprovalConfidenceStat, ApprovalItem, ApprovalQueue, Ap
|
|
|
5
5
|
export { ArtifactView, BRAND_PRISM_THEME, CodeSurface, CodeSurfaceProps, CodeView, CodeViewProps, DiffStats, DiffView, DiffViewProps, FileBreadcrumb, FileBreadcrumbProps, GitStatusBadge, PanelHeader, PanelHeaderProps, PillTabItem, PillTabs, PillTabsProps, PreviewView, PreviewViewProps, SandboxArtifact, SandboxArtifactPane, SandboxArtifactPaneProps, SandboxArtifactTerminal, buildUnifiedPatch, computeDiffStats, resolveLanguage } from './workbench.js';
|
|
6
6
|
export { OpenUIAction, OpenUIActionsNode, OpenUIArtifactRenderer, OpenUIArtifactRendererProps, OpenUIBadgeNode, OpenUICardNode, OpenUICodeNode, OpenUIComponentNode, OpenUIGridNode, OpenUIHeadingNode, OpenUIKeyValueNode, OpenUIMarkdownNode, OpenUIPrimitive, OpenUISeparatorNode, OpenUIStackNode, OpenUIStatNode, OpenUITableNode, OpenUITextNode } from '@tangle-network/ui/openui';
|
|
7
7
|
export { AgentTimeline, AgentTimelineArtifactItem, AgentTimelineCustomItem, AgentTimelineItem, AgentTimelineMessageItem, AgentTimelineProps, AgentTimelineStatusItem, AgentTimelineTone, AgentTimelineToolGroupItem, AgentTimelineToolItem, ChatContainer, ChatContainerProps, ChatMessage, ChatMessageProps, MessageList, MessageListProps, MessageRole, ThinkingIndicator, ThinkingIndicatorProps, UserMessage, UserMessageProps } from '@tangle-network/ui/chat';
|
|
8
|
-
export { AgentComposer, AgentComposerProps, AgentProfileCapability, AgentProfileDraft, AgentProfileOption, AgentProfilePicker, AgentProfilePickerProps, AgentSessionControls, AgentSessionControlsProps, AgentSessionHarnessControl, AgentSessionModelControl, AgentSessionProfileControl, AgentSessionReasoningControl, ArtifactAgentDock, ArtifactAgentDockProps, ArtifactAgentDockTransport, ArtifactDockMessage, ArtifactDockStreamEvent, ArtifactKind, ArtifactScope, ComposerFile, ComposerFileRejection, ComposerFileValidationConfig, DEFAULT_REASONING_LEVEL_OPTIONS, HARNESS_REASONING_OPTIONS, ReasoningLevel, ReasoningLevelOption, ReasoningLevelPicker, ReasoningLevelPickerProps, createFetchTransport, isAcceptedType, isModelCompatibleWithHarness, snapModelToHarness, validateComposerFiles } from './chat.js';
|
|
8
|
+
export { AgentComposer, AgentComposerMention, AgentComposerProps, AgentProfileCapability, AgentProfileDraft, AgentProfileOption, AgentProfilePicker, AgentProfilePickerProps, AgentSessionControls, AgentSessionControlsProps, AgentSessionHarnessControl, AgentSessionModelControl, AgentSessionProfileControl, AgentSessionReasoningControl, ArtifactAgentDock, ArtifactAgentDockProps, ArtifactAgentDockTransport, ArtifactDockMessage, ArtifactDockStreamEvent, ArtifactKind, ArtifactScope, ComposerFile, ComposerFileRejection, ComposerFileValidationConfig, DEFAULT_REASONING_LEVEL_OPTIONS, HARNESS_REASONING_OPTIONS, MENTION_PILL_CLASS, MentionItem, ReasoningLevel, ReasoningLevelOption, ReasoningLevelPicker, ReasoningLevelPickerProps, createFetchTransport, isAcceptedType, isModelCompatibleWithHarness, snapModelToHarness, validateComposerFiles } from './chat.js';
|
|
9
9
|
export { ExpandedToolDetail, ExpandedToolDetailProps, InlineThinkingItem, InlineThinkingItemProps, InlineToolItem, InlineToolItemProps, LiveDuration, RunGroup, RunGroupProps } from '@tangle-network/ui/run';
|
|
10
10
|
export { CommandPreview, DiffPreview, GlobResultsPreview, GrepResultsPreview, QuestionPreview, WebSearchPreview, WriteFilePreview } from '@tangle-network/ui/tool-previews';
|
|
11
11
|
export { FileArtifactPane, FileArtifactPaneProps, FileNode, FilePreview, FilePreviewProps, FileTabData, FileTabs, FileTabsProps, FileTree, FileTreeProps, FileTreeVisibilityOptions, RichFileTree, RichFileTreeGitEntry, RichFileTreeGitStatus, RichFileTreeProps, RichFileTreeThemeVars, filterFileTree } from '@tangle-network/ui/files';
|
package/dist/index.js
CHANGED
|
@@ -91,7 +91,7 @@ import {
|
|
|
91
91
|
snapHarnessToModel,
|
|
92
92
|
snapModelToHarness,
|
|
93
93
|
validateComposerFiles
|
|
94
|
-
} from "./chunk-
|
|
94
|
+
} from "./chunk-R5OVYRIY.js";
|
|
95
95
|
import {
|
|
96
96
|
TOOL_CATEGORY_ICONS,
|
|
97
97
|
cn,
|
|
@@ -105,6 +105,9 @@ import {
|
|
|
105
105
|
timeAgo,
|
|
106
106
|
truncateText
|
|
107
107
|
} from "./chunk-TK46XFLM.js";
|
|
108
|
+
import {
|
|
109
|
+
MENTION_PILL_CLASS
|
|
110
|
+
} from "./chunk-WGOOQAW3.js";
|
|
108
111
|
import {
|
|
109
112
|
ExpandedToolDetail,
|
|
110
113
|
InlineThinkingItem,
|
|
@@ -455,6 +458,7 @@ export {
|
|
|
455
458
|
LiveDuration,
|
|
456
459
|
LoginLayout,
|
|
457
460
|
Logo,
|
|
461
|
+
MENTION_PILL_CLASS,
|
|
458
462
|
Markdown,
|
|
459
463
|
MessageList,
|
|
460
464
|
MetricAreaChart,
|
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MENTION_PILL_CLASS
|
|
3
|
+
} from "./chunk-WGOOQAW3.js";
|
|
4
|
+
import {
|
|
5
|
+
cn
|
|
6
|
+
} from "./chunk-EI44GEQ5.js";
|
|
7
|
+
import "./chunk-U67V476Y.js";
|
|
8
|
+
|
|
9
|
+
// src/chat/mention-editor.tsx
|
|
10
|
+
import { mergeAttributes } from "@tiptap/core";
|
|
11
|
+
import Mention from "@tiptap/extension-mention";
|
|
12
|
+
import { EditorContent, useEditor } from "@tiptap/react";
|
|
13
|
+
import StarterKit from "@tiptap/starter-kit";
|
|
14
|
+
import { exitSuggestion } from "@tiptap/suggestion";
|
|
15
|
+
import * as React2 from "react";
|
|
16
|
+
|
|
17
|
+
// src/chat/mention-list.tsx
|
|
18
|
+
import { Loader2 } from "lucide-react";
|
|
19
|
+
import * as React from "react";
|
|
20
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
21
|
+
var MentionList = React.forwardRef(
|
|
22
|
+
function MentionList2({
|
|
23
|
+
items,
|
|
24
|
+
loading,
|
|
25
|
+
error,
|
|
26
|
+
emptyText = "No matches",
|
|
27
|
+
renderItem,
|
|
28
|
+
onSelect,
|
|
29
|
+
className
|
|
30
|
+
}, ref) {
|
|
31
|
+
const [selected, setSelected] = React.useState(0);
|
|
32
|
+
const selectedRef = React.useRef(0);
|
|
33
|
+
const move = (next) => {
|
|
34
|
+
selectedRef.current = next;
|
|
35
|
+
setSelected(next);
|
|
36
|
+
};
|
|
37
|
+
React.useEffect(() => {
|
|
38
|
+
move(0);
|
|
39
|
+
}, [items]);
|
|
40
|
+
React.useImperativeHandle(
|
|
41
|
+
ref,
|
|
42
|
+
() => ({
|
|
43
|
+
onKeyDown(event) {
|
|
44
|
+
const count = items.length;
|
|
45
|
+
switch (event.key) {
|
|
46
|
+
case "ArrowDown":
|
|
47
|
+
if (count > 0) move((selectedRef.current + 1) % count);
|
|
48
|
+
return true;
|
|
49
|
+
case "ArrowUp":
|
|
50
|
+
if (count > 0) move((selectedRef.current - 1 + count) % count);
|
|
51
|
+
return true;
|
|
52
|
+
case "Enter":
|
|
53
|
+
case "Tab":
|
|
54
|
+
if (count > 0) onSelect(items[selectedRef.current]);
|
|
55
|
+
return true;
|
|
56
|
+
default:
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}),
|
|
61
|
+
[items, onSelect]
|
|
62
|
+
);
|
|
63
|
+
return /* @__PURE__ */ jsxs(
|
|
64
|
+
"div",
|
|
65
|
+
{
|
|
66
|
+
role: "listbox",
|
|
67
|
+
"aria-label": "File mentions",
|
|
68
|
+
className: cn(
|
|
69
|
+
"max-h-64 min-w-[16rem] max-w-sm overflow-y-auto rounded-xl border border-border",
|
|
70
|
+
"bg-popover text-popover-foreground p-1 shadow-lg",
|
|
71
|
+
className
|
|
72
|
+
),
|
|
73
|
+
children: [
|
|
74
|
+
loading && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-2.5 py-2 text-muted-foreground text-sm", children: [
|
|
75
|
+
/* @__PURE__ */ jsx(Loader2, { className: "size-3.5 animate-spin" }),
|
|
76
|
+
"Searching\u2026"
|
|
77
|
+
] }),
|
|
78
|
+
!loading && error && /* @__PURE__ */ jsx("div", { className: "px-2.5 py-2 text-[var(--status-error,#ff4d6d)] text-sm", children: "Couldn\u2019t load matches" }),
|
|
79
|
+
!loading && !error && items.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-2.5 py-2 text-muted-foreground text-sm", children: emptyText }),
|
|
80
|
+
!loading && !error && items.map((item, index) => {
|
|
81
|
+
const active = index === selected;
|
|
82
|
+
return /* @__PURE__ */ jsx(
|
|
83
|
+
"button",
|
|
84
|
+
{
|
|
85
|
+
type: "button",
|
|
86
|
+
role: "option",
|
|
87
|
+
"aria-selected": active,
|
|
88
|
+
onMouseDown: (event) => {
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
onSelect(item);
|
|
91
|
+
},
|
|
92
|
+
onMouseEnter: () => move(index),
|
|
93
|
+
className: cn(
|
|
94
|
+
"flex w-full items-center gap-2 rounded-lg px-2.5 py-1.5 text-left text-sm",
|
|
95
|
+
active ? "bg-primary/10 text-foreground" : "text-muted-foreground"
|
|
96
|
+
),
|
|
97
|
+
children: renderItem ? renderItem(item) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
98
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-foreground", children: item.label }),
|
|
99
|
+
item.detail && /* @__PURE__ */ jsx("span", { className: "ml-auto truncate text-muted-foreground text-xs", children: item.detail })
|
|
100
|
+
] })
|
|
101
|
+
},
|
|
102
|
+
item.id
|
|
103
|
+
);
|
|
104
|
+
})
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// src/chat/mention-serialize.ts
|
|
112
|
+
var MENTION_TYPE = "mention";
|
|
113
|
+
function serializeMentionDoc(doc) {
|
|
114
|
+
return (doc.content ?? []).map(serializeBlock).join("\n");
|
|
115
|
+
}
|
|
116
|
+
function serializeBlock(node) {
|
|
117
|
+
if (node.type === "paragraph") {
|
|
118
|
+
return (node.content ?? []).map(serializeInline).join("");
|
|
119
|
+
}
|
|
120
|
+
return "";
|
|
121
|
+
}
|
|
122
|
+
function serializeInline(node) {
|
|
123
|
+
if (node.type === "text") return node.text ?? "";
|
|
124
|
+
if (node.type === "hardBreak") return "\n";
|
|
125
|
+
if (node.type === MENTION_TYPE) return `@${node.attrs?.id ?? ""}`;
|
|
126
|
+
return "";
|
|
127
|
+
}
|
|
128
|
+
function collectMentions(doc) {
|
|
129
|
+
const out = [];
|
|
130
|
+
const walk = (node) => {
|
|
131
|
+
if (node.type === MENTION_TYPE && node.attrs?.id) {
|
|
132
|
+
out.push({
|
|
133
|
+
id: node.attrs.id,
|
|
134
|
+
label: node.attrs.label ?? node.attrs.id,
|
|
135
|
+
kind: node.attrs.kind ?? void 0
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
for (const child of node.content ?? []) walk(child);
|
|
139
|
+
};
|
|
140
|
+
walk(doc);
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
function parseMentionValue(value, known) {
|
|
144
|
+
const lines = value.split("\n");
|
|
145
|
+
return {
|
|
146
|
+
type: "doc",
|
|
147
|
+
content: lines.map((line) => ({
|
|
148
|
+
type: "paragraph",
|
|
149
|
+
content: parseLine(line, known)
|
|
150
|
+
}))
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function parseLine(line, known) {
|
|
154
|
+
const content = [];
|
|
155
|
+
let text = "";
|
|
156
|
+
const flush = () => {
|
|
157
|
+
if (text) {
|
|
158
|
+
content.push({ type: "text", text });
|
|
159
|
+
text = "";
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
let i = 0;
|
|
163
|
+
while (i < line.length) {
|
|
164
|
+
if (line[i] === "@") {
|
|
165
|
+
const id = matchKnownId(line, i + 1, known);
|
|
166
|
+
if (id) {
|
|
167
|
+
const item = known.get(id);
|
|
168
|
+
flush();
|
|
169
|
+
content.push({
|
|
170
|
+
type: MENTION_TYPE,
|
|
171
|
+
attrs: {
|
|
172
|
+
id: item.id,
|
|
173
|
+
label: item.label,
|
|
174
|
+
kind: item.kind ?? null
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
i += 1 + id.length;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
text += line[i];
|
|
182
|
+
i += 1;
|
|
183
|
+
}
|
|
184
|
+
flush();
|
|
185
|
+
return content;
|
|
186
|
+
}
|
|
187
|
+
function matchKnownId(line, pos, known) {
|
|
188
|
+
let best = null;
|
|
189
|
+
for (const id of known.keys()) {
|
|
190
|
+
if (id.length === 0) continue;
|
|
191
|
+
if (best !== null && id.length <= best.length) continue;
|
|
192
|
+
if (!line.startsWith(id, pos)) continue;
|
|
193
|
+
const after = pos + id.length;
|
|
194
|
+
if (after < line.length && !/\s/.test(line[after])) continue;
|
|
195
|
+
best = id;
|
|
196
|
+
}
|
|
197
|
+
return best;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/chat/mention-editor.tsx
|
|
201
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
202
|
+
function buildComposerStarterKit() {
|
|
203
|
+
return StarterKit.configure({
|
|
204
|
+
blockquote: false,
|
|
205
|
+
bold: false,
|
|
206
|
+
bulletList: false,
|
|
207
|
+
code: false,
|
|
208
|
+
codeBlock: false,
|
|
209
|
+
dropcursor: false,
|
|
210
|
+
heading: false,
|
|
211
|
+
horizontalRule: false,
|
|
212
|
+
italic: false,
|
|
213
|
+
link: false,
|
|
214
|
+
listItem: false,
|
|
215
|
+
listKeymap: false,
|
|
216
|
+
orderedList: false,
|
|
217
|
+
strike: false,
|
|
218
|
+
trailingNode: false,
|
|
219
|
+
underline: false
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
function buildMentionExtension(trigger, suggestion) {
|
|
223
|
+
return Mention.extend({
|
|
224
|
+
addAttributes() {
|
|
225
|
+
return {
|
|
226
|
+
id: {
|
|
227
|
+
default: null,
|
|
228
|
+
parseHTML: (element) => element.getAttribute("data-id"),
|
|
229
|
+
renderHTML: (attrs) => attrs.id ? { "data-id": attrs.id } : {}
|
|
230
|
+
},
|
|
231
|
+
label: {
|
|
232
|
+
default: null,
|
|
233
|
+
parseHTML: (element) => element.getAttribute("data-label"),
|
|
234
|
+
renderHTML: (attrs) => attrs.label ? { "data-label": attrs.label } : {}
|
|
235
|
+
},
|
|
236
|
+
kind: {
|
|
237
|
+
default: null,
|
|
238
|
+
parseHTML: (element) => element.getAttribute("data-kind"),
|
|
239
|
+
renderHTML: (attrs) => attrs.kind ? { "data-kind": attrs.kind } : {}
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
}).configure({
|
|
244
|
+
HTMLAttributes: { class: MENTION_PILL_CLASS },
|
|
245
|
+
renderText: ({ node }) => `${trigger}${node.attrs.id}`,
|
|
246
|
+
renderHTML: ({ options, node }) => [
|
|
247
|
+
"span",
|
|
248
|
+
mergeAttributes(options.HTMLAttributes, {
|
|
249
|
+
title: node.attrs.id ?? void 0
|
|
250
|
+
}),
|
|
251
|
+
`${trigger}${node.attrs.label ?? node.attrs.id}`
|
|
252
|
+
],
|
|
253
|
+
suggestion
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
var EDITOR_CLASS = cn(
|
|
257
|
+
"w-full whitespace-pre-wrap break-words bg-transparent px-2 py-1",
|
|
258
|
+
"text-foreground text-sm leading-relaxed outline-none"
|
|
259
|
+
);
|
|
260
|
+
var FETCH_DEBOUNCE_MS = 100;
|
|
261
|
+
function mentionsKey(mentions) {
|
|
262
|
+
return mentions.map((item) => item.id).join("\0");
|
|
263
|
+
}
|
|
264
|
+
function MentionEditor({
|
|
265
|
+
value,
|
|
266
|
+
onChange,
|
|
267
|
+
onSubmit,
|
|
268
|
+
placeholder,
|
|
269
|
+
disabled,
|
|
270
|
+
autoFocus,
|
|
271
|
+
minRows,
|
|
272
|
+
maxHeight,
|
|
273
|
+
mention,
|
|
274
|
+
registerFocus,
|
|
275
|
+
onPasteFiles
|
|
276
|
+
}) {
|
|
277
|
+
const trigger = mention.trigger ?? "@";
|
|
278
|
+
const [anchor, setAnchor] = React2.useState(null);
|
|
279
|
+
const [open, setOpen] = React2.useState(false);
|
|
280
|
+
const [query, setQuery] = React2.useState("");
|
|
281
|
+
const [items, setItems] = React2.useState([]);
|
|
282
|
+
const [loading, setLoading] = React2.useState(false);
|
|
283
|
+
const [errored, setErrored] = React2.useState(false);
|
|
284
|
+
const openRef = React2.useRef(false);
|
|
285
|
+
const commandRef = React2.useRef(null);
|
|
286
|
+
const listRef = React2.useRef(null);
|
|
287
|
+
const knownRef = React2.useRef(/* @__PURE__ */ new Map());
|
|
288
|
+
const requestIdRef = React2.useRef(0);
|
|
289
|
+
const clientRectRef = React2.useRef(null);
|
|
290
|
+
const lastMentionsKeyRef = React2.useRef("");
|
|
291
|
+
const onSubmitRef = React2.useRef(onSubmit);
|
|
292
|
+
onSubmitRef.current = onSubmit;
|
|
293
|
+
const onChangeRef = React2.useRef(onChange);
|
|
294
|
+
onChangeRef.current = onChange;
|
|
295
|
+
const onMentionsChangeRef = React2.useRef(mention.onMentionsChange);
|
|
296
|
+
onMentionsChangeRef.current = mention.onMentionsChange;
|
|
297
|
+
const onPasteFilesRef = React2.useRef(onPasteFiles);
|
|
298
|
+
onPasteFilesRef.current = onPasteFiles;
|
|
299
|
+
const fetchItemsRef = React2.useRef(mention.fetchItems);
|
|
300
|
+
fetchItemsRef.current = mention.fetchItems;
|
|
301
|
+
const editor = useEditor(
|
|
302
|
+
{
|
|
303
|
+
immediatelyRender: false,
|
|
304
|
+
editable: !disabled,
|
|
305
|
+
autofocus: autoFocus ? "end" : false,
|
|
306
|
+
content: parseMentionValue(value, knownRef.current),
|
|
307
|
+
editorProps: {
|
|
308
|
+
attributes: {
|
|
309
|
+
class: EDITOR_CLASS,
|
|
310
|
+
role: "textbox",
|
|
311
|
+
"aria-multiline": "true",
|
|
312
|
+
"aria-label": "Message input"
|
|
313
|
+
},
|
|
314
|
+
handleKeyDown: (_view, event) => {
|
|
315
|
+
if (event.key !== "Enter" || event.shiftKey) return false;
|
|
316
|
+
if (event.isComposing || event.keyCode === 229) return false;
|
|
317
|
+
if (openRef.current) return false;
|
|
318
|
+
event.preventDefault();
|
|
319
|
+
onSubmitRef.current();
|
|
320
|
+
return true;
|
|
321
|
+
},
|
|
322
|
+
handlePaste: (_view, event) => {
|
|
323
|
+
const files = event.clipboardData?.files;
|
|
324
|
+
if (files && files.length > 0 && onPasteFilesRef.current) {
|
|
325
|
+
return onPasteFilesRef.current(files);
|
|
326
|
+
}
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
onUpdate: ({ editor: editor2 }) => {
|
|
331
|
+
const json = editor2.getJSON();
|
|
332
|
+
onChangeRef.current(serializeMentionDoc(json));
|
|
333
|
+
const mentions = collectMentions(json);
|
|
334
|
+
for (const item of mentions) knownRef.current.set(item.id, item);
|
|
335
|
+
lastMentionsKeyRef.current = mentionsKey(mentions);
|
|
336
|
+
onMentionsChangeRef.current?.(mentions);
|
|
337
|
+
},
|
|
338
|
+
extensions: [
|
|
339
|
+
buildComposerStarterKit(),
|
|
340
|
+
buildMentionExtension(trigger, {
|
|
341
|
+
char: trigger,
|
|
342
|
+
allowSpaces: false,
|
|
343
|
+
// Items are fetched by this component (so it can model loading and
|
|
344
|
+
// error states), not by the suggestion plugin.
|
|
345
|
+
items: () => [],
|
|
346
|
+
command: ({ editor: editor2, range, props }) => {
|
|
347
|
+
const item = props;
|
|
348
|
+
editor2.chain().focus().insertContentAt(range, [
|
|
349
|
+
{
|
|
350
|
+
type: "mention",
|
|
351
|
+
attrs: {
|
|
352
|
+
id: item.id,
|
|
353
|
+
label: item.label,
|
|
354
|
+
kind: item.kind ?? null
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
{ type: "text", text: " " }
|
|
358
|
+
]).run();
|
|
359
|
+
knownRef.current.set(item.id, item);
|
|
360
|
+
},
|
|
361
|
+
render: () => ({
|
|
362
|
+
onStart: (props) => {
|
|
363
|
+
openRef.current = true;
|
|
364
|
+
commandRef.current = props.command;
|
|
365
|
+
clientRectRef.current = props.clientRect ?? null;
|
|
366
|
+
setOpen(true);
|
|
367
|
+
setQuery(props.query);
|
|
368
|
+
setAnchor(props.clientRect?.() ?? null);
|
|
369
|
+
},
|
|
370
|
+
onUpdate: (props) => {
|
|
371
|
+
commandRef.current = props.command;
|
|
372
|
+
clientRectRef.current = props.clientRect ?? null;
|
|
373
|
+
setQuery(props.query);
|
|
374
|
+
setAnchor(props.clientRect?.() ?? null);
|
|
375
|
+
},
|
|
376
|
+
onKeyDown: (props) => {
|
|
377
|
+
if (props.event.key === "Escape") {
|
|
378
|
+
exitSuggestion(props.view);
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
return listRef.current?.onKeyDown(props.event) ?? false;
|
|
382
|
+
},
|
|
383
|
+
onExit: () => {
|
|
384
|
+
openRef.current = false;
|
|
385
|
+
commandRef.current = null;
|
|
386
|
+
setOpen(false);
|
|
387
|
+
setItems([]);
|
|
388
|
+
setLoading(false);
|
|
389
|
+
setErrored(false);
|
|
390
|
+
}
|
|
391
|
+
})
|
|
392
|
+
})
|
|
393
|
+
]
|
|
394
|
+
},
|
|
395
|
+
[]
|
|
396
|
+
);
|
|
397
|
+
React2.useEffect(() => {
|
|
398
|
+
if (!open) return;
|
|
399
|
+
const requestId = requestIdRef.current += 1;
|
|
400
|
+
setLoading(true);
|
|
401
|
+
setErrored(false);
|
|
402
|
+
const timer = setTimeout(() => {
|
|
403
|
+
Promise.resolve(fetchItemsRef.current(query)).then((results) => {
|
|
404
|
+
if (requestId !== requestIdRef.current) return;
|
|
405
|
+
for (const item of results) knownRef.current.set(item.id, item);
|
|
406
|
+
setItems(results);
|
|
407
|
+
setLoading(false);
|
|
408
|
+
}).catch(() => {
|
|
409
|
+
if (requestId !== requestIdRef.current) return;
|
|
410
|
+
setErrored(true);
|
|
411
|
+
setLoading(false);
|
|
412
|
+
});
|
|
413
|
+
}, FETCH_DEBOUNCE_MS);
|
|
414
|
+
return () => clearTimeout(timer);
|
|
415
|
+
}, [open, query]);
|
|
416
|
+
React2.useEffect(() => {
|
|
417
|
+
if (!open) return;
|
|
418
|
+
const reposition = () => setAnchor(clientRectRef.current?.() ?? null);
|
|
419
|
+
window.addEventListener("scroll", reposition, true);
|
|
420
|
+
window.addEventListener("resize", reposition);
|
|
421
|
+
return () => {
|
|
422
|
+
window.removeEventListener("scroll", reposition, true);
|
|
423
|
+
window.removeEventListener("resize", reposition);
|
|
424
|
+
};
|
|
425
|
+
}, [open]);
|
|
426
|
+
React2.useEffect(() => {
|
|
427
|
+
if (!editor) return;
|
|
428
|
+
if (serializeMentionDoc(editor.getJSON()) === value) return;
|
|
429
|
+
const doc = parseMentionValue(value, knownRef.current);
|
|
430
|
+
editor.commands.setContent(doc, { emitUpdate: false });
|
|
431
|
+
const mentions = collectMentions(doc);
|
|
432
|
+
const key = mentionsKey(mentions);
|
|
433
|
+
if (key !== lastMentionsKeyRef.current) {
|
|
434
|
+
lastMentionsKeyRef.current = key;
|
|
435
|
+
onMentionsChangeRef.current?.(mentions);
|
|
436
|
+
}
|
|
437
|
+
}, [editor, value]);
|
|
438
|
+
React2.useEffect(() => {
|
|
439
|
+
editor?.setEditable(!disabled);
|
|
440
|
+
}, [editor, disabled]);
|
|
441
|
+
React2.useEffect(() => {
|
|
442
|
+
if (!editor || !registerFocus) return;
|
|
443
|
+
registerFocus(() => editor.commands.focus());
|
|
444
|
+
}, [editor, registerFocus]);
|
|
445
|
+
return /* @__PURE__ */ jsxs2("div", { className: "relative", children: [
|
|
446
|
+
/* @__PURE__ */ jsxs2(
|
|
447
|
+
"div",
|
|
448
|
+
{
|
|
449
|
+
className: "overflow-y-auto",
|
|
450
|
+
style: { maxHeight, minHeight: `${minRows * 1.5}rem` },
|
|
451
|
+
children: [
|
|
452
|
+
/* @__PURE__ */ jsx2(EditorContent, { editor }),
|
|
453
|
+
value.length === 0 && /* @__PURE__ */ jsx2("div", { className: "pointer-events-none absolute top-1 left-2 text-muted-foreground text-sm leading-relaxed", children: placeholder })
|
|
454
|
+
]
|
|
455
|
+
}
|
|
456
|
+
),
|
|
457
|
+
open && /* @__PURE__ */ jsx2(
|
|
458
|
+
"div",
|
|
459
|
+
{
|
|
460
|
+
className: "absolute z-20",
|
|
461
|
+
style: anchorStyle(anchor),
|
|
462
|
+
"data-testid": "mention-popover",
|
|
463
|
+
children: /* @__PURE__ */ jsx2(
|
|
464
|
+
MentionList,
|
|
465
|
+
{
|
|
466
|
+
ref: listRef,
|
|
467
|
+
items,
|
|
468
|
+
loading,
|
|
469
|
+
error: errored,
|
|
470
|
+
emptyText: mention.emptyText,
|
|
471
|
+
renderItem: mention.renderItem,
|
|
472
|
+
onSelect: (item) => commandRef.current?.(item),
|
|
473
|
+
className: mention.popoverClassName
|
|
474
|
+
}
|
|
475
|
+
)
|
|
476
|
+
}
|
|
477
|
+
)
|
|
478
|
+
] });
|
|
479
|
+
}
|
|
480
|
+
function anchorStyle(rect) {
|
|
481
|
+
if (!rect || rect.left === 0 && rect.bottom === 0) {
|
|
482
|
+
return { position: "absolute", top: "100%", left: 0 };
|
|
483
|
+
}
|
|
484
|
+
return { position: "fixed", top: rect.bottom + 4, left: rect.left };
|
|
485
|
+
}
|
|
486
|
+
export {
|
|
487
|
+
buildComposerStarterKit,
|
|
488
|
+
buildMentionExtension,
|
|
489
|
+
MentionEditor as default
|
|
490
|
+
};
|
package/dist/styles.css
CHANGED
|
@@ -1292,6 +1292,9 @@
|
|
|
1292
1292
|
.min-w-\[15rem\] {
|
|
1293
1293
|
min-width: 15rem;
|
|
1294
1294
|
}
|
|
1295
|
+
.min-w-\[16rem\] {
|
|
1296
|
+
min-width: 16rem;
|
|
1297
|
+
}
|
|
1295
1298
|
.min-w-\[20px\] {
|
|
1296
1299
|
min-width: 20px;
|
|
1297
1300
|
}
|
|
@@ -2034,6 +2037,12 @@
|
|
|
2034
2037
|
border-color: color-mix(in oklab, var(--color-primary) 30%, transparent);
|
|
2035
2038
|
}
|
|
2036
2039
|
}
|
|
2040
|
+
.border-primary\/40 {
|
|
2041
|
+
border-color: color-mix(in srgb, hsl(var(--primary)) 40%, transparent);
|
|
2042
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
2043
|
+
border-color: color-mix(in oklab, var(--color-primary) 40%, transparent);
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2037
2046
|
.border-primary\/50 {
|
|
2038
2047
|
border-color: color-mix(in srgb, hsl(var(--primary)) 50%, transparent);
|
|
2039
2048
|
@supports (color: color-mix(in lab, red, red)) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/sandbox-ui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Unified UI component library for Tangle Sandbox
|
|
3
|
+
"version": "0.87.0",
|
|
4
|
+
"description": "Unified UI component library for Tangle Sandbox — primitives, chat, dashboard, terminal, editor, and workspace components",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/tangle-network/sandbox-ui"
|
|
@@ -192,8 +192,11 @@
|
|
|
192
192
|
"@tiptap/core": "^3.20.4",
|
|
193
193
|
"@tiptap/extension-collaboration": "^3.20.4",
|
|
194
194
|
"@tiptap/extension-collaboration-caret": "^3.7.0",
|
|
195
|
+
"@tiptap/extension-mention": "^3.22.5",
|
|
196
|
+
"@tiptap/pm": "^3.22.5",
|
|
195
197
|
"@tiptap/react": "^3.20.4",
|
|
196
198
|
"@tiptap/starter-kit": "^3.20.4",
|
|
199
|
+
"@tiptap/suggestion": "^3.22.5",
|
|
197
200
|
"@types/react": "^19.1.2",
|
|
198
201
|
"@types/react-dom": "^19.1.2",
|
|
199
202
|
"@xterm/addon-fit": "^0.11.0",
|