@nextclaw/agent-chat-ui 0.2.2 → 0.2.4
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/index.d.ts +21 -3
- package/dist/index.js +226 -85
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react from 'react';
|
|
2
2
|
import { RefObject } from 'react';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
4
|
|
|
4
5
|
type ChatTexts = {
|
|
5
6
|
slashLoadingLabel: string;
|
|
@@ -128,6 +129,7 @@ type ChatInputBarProps = {
|
|
|
128
129
|
placeholder: string;
|
|
129
130
|
disabled: boolean;
|
|
130
131
|
onNodesChange: (nodes: ChatComposerNode[]) => void;
|
|
132
|
+
onFilesAdd?: (files: File[]) => Promise<void> | void;
|
|
131
133
|
onSlashQueryChange?: (query: string | null) => void;
|
|
132
134
|
};
|
|
133
135
|
slashMenu: Pick<ChatSlashMenuProps, 'isLoading' | 'items' | 'texts'>;
|
|
@@ -155,6 +157,14 @@ type ChatMessagePartViewModel = {
|
|
|
155
157
|
} | {
|
|
156
158
|
type: 'tool-card';
|
|
157
159
|
card: ChatToolPartViewModel;
|
|
160
|
+
} | {
|
|
161
|
+
type: 'file';
|
|
162
|
+
file: {
|
|
163
|
+
label: string;
|
|
164
|
+
mimeType: string;
|
|
165
|
+
dataUrl?: string;
|
|
166
|
+
isImage: boolean;
|
|
167
|
+
};
|
|
158
168
|
} | {
|
|
159
169
|
type: 'unknown';
|
|
160
170
|
label: string;
|
|
@@ -182,7 +192,15 @@ type ChatMessageListProps = {
|
|
|
182
192
|
className?: string;
|
|
183
193
|
};
|
|
184
194
|
|
|
185
|
-
|
|
195
|
+
type ChatInputBarHandle = {
|
|
196
|
+
insertFileToken: (tokenKey: string, label: string) => void;
|
|
197
|
+
insertFileTokens: (tokens: Array<{
|
|
198
|
+
tokenKey: string;
|
|
199
|
+
label: string;
|
|
200
|
+
}>) => void;
|
|
201
|
+
focusComposer: () => void;
|
|
202
|
+
};
|
|
203
|
+
declare const ChatInputBar: react.ForwardRefExoticComponent<ChatInputBarProps & react.RefAttributes<ChatInputBarHandle>>;
|
|
186
204
|
|
|
187
205
|
declare function ChatMessageList(props: ChatMessageListProps): react_jsx_runtime.JSX.Element;
|
|
188
206
|
|
|
@@ -246,4 +264,4 @@ declare function resolveChatComposerSlashTrigger(nodes: ChatComposerNode[], sele
|
|
|
246
264
|
end: number;
|
|
247
265
|
} | null;
|
|
248
266
|
|
|
249
|
-
export { type ChatComposerNode, type ChatComposerSelection, type ChatComposerTextNode, type ChatComposerTokenKind, type ChatComposerTokenNode, type ChatInlineHint, ChatInputBar, type ChatInputBarActionsProps, type ChatInputBarProps, type ChatInputBarToolbarProps, ChatMessageList, type ChatMessageListProps, type ChatMessagePartViewModel, type ChatMessageRole, type ChatMessageTexts, type ChatMessageViewModel, type ChatSelectedItem, type ChatSkillPickerOption, type ChatSkillPickerProps, type ChatSlashItem, type ChatSlashMenuProps, type ChatTexts, type ChatToolPartViewModel, type ChatToolbarAccessory, type ChatToolbarAccessoryIcon, type ChatToolbarIcon, type ChatToolbarSelect, type ChatToolbarSelectOption, copyText, createChatComposerNodesFromText, createChatComposerTextNode, createChatComposerTokenNode, createEmptyChatComposerNodes, extractChatComposerTokenKeys, normalizeChatComposerNodes, removeChatComposerTokenNodes, replaceChatComposerRange, resolveChatComposerSlashTrigger, serializeChatComposerDocument, serializeChatComposerPlainText, useActiveItemScroll, useCopyFeedback, useElementWidth, useStickyBottomScroll };
|
|
267
|
+
export { type ChatComposerNode, type ChatComposerSelection, type ChatComposerTextNode, type ChatComposerTokenKind, type ChatComposerTokenNode, type ChatInlineHint, ChatInputBar, type ChatInputBarActionsProps, type ChatInputBarHandle, type ChatInputBarProps, type ChatInputBarToolbarProps, ChatMessageList, type ChatMessageListProps, type ChatMessagePartViewModel, type ChatMessageRole, type ChatMessageTexts, type ChatMessageViewModel, type ChatSelectedItem, type ChatSkillPickerOption, type ChatSkillPickerProps, type ChatSlashItem, type ChatSlashMenuProps, type ChatTexts, type ChatToolPartViewModel, type ChatToolbarAccessory, type ChatToolbarAccessoryIcon, type ChatToolbarIcon, type ChatToolbarSelect, type ChatToolbarSelectOption, copyText, createChatComposerNodesFromText, createChatComposerTextNode, createChatComposerTokenNode, createEmptyChatComposerNodes, extractChatComposerTokenKeys, normalizeChatComposerNodes, removeChatComposerTokenNodes, replaceChatComposerRange, resolveChatComposerSlashTrigger, serializeChatComposerDocument, serializeChatComposerPlainText, useActiveItemScroll, useCopyFeedback, useElementWidth, useStickyBottomScroll };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/components/chat/ui/chat-input-bar/chat-input-bar.tsx
|
|
2
|
-
import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef4, useState as useState4 } from "react";
|
|
2
|
+
import { forwardRef as forwardRef7, useEffect as useEffect4, useImperativeHandle as useImperativeHandle2, useMemo as useMemo3, useRef as useRef4, useState as useState4 } from "react";
|
|
3
3
|
|
|
4
4
|
// src/components/chat/ui/chat-input-bar/chat-slash-menu.tsx
|
|
5
5
|
import { useMemo, useRef as useRef2 } from "react";
|
|
@@ -1082,22 +1082,14 @@ var ChatComposerController = class {
|
|
|
1082
1082
|
this.selection = { start: nextOffset, end: nextOffset };
|
|
1083
1083
|
return this.getSnapshot();
|
|
1084
1084
|
};
|
|
1085
|
+
this.insertFileToken = (tokenKey, label) => {
|
|
1086
|
+
return this.insertToken("file", tokenKey, label);
|
|
1087
|
+
};
|
|
1085
1088
|
this.insertSkillToken = (tokenKey, label) => {
|
|
1086
1089
|
if (this.getSelectedSkillKeys().includes(tokenKey)) {
|
|
1087
1090
|
return this.getSnapshot();
|
|
1088
1091
|
}
|
|
1089
|
-
|
|
1090
|
-
const documentLength = this.getDocumentLength();
|
|
1091
|
-
const replaceStart = trigger?.start ?? this.selection?.start ?? documentLength;
|
|
1092
|
-
const replaceEnd = trigger?.end ?? this.selection?.end ?? replaceStart;
|
|
1093
|
-
this.nodes = replaceChatComposerRange(
|
|
1094
|
-
this.nodes,
|
|
1095
|
-
replaceStart,
|
|
1096
|
-
replaceEnd,
|
|
1097
|
-
[createChatComposerTokenNode({ tokenKind: "skill", tokenKey, label })]
|
|
1098
|
-
);
|
|
1099
|
-
this.selection = { start: replaceStart + 1, end: replaceStart + 1 };
|
|
1100
|
-
return this.getSnapshot();
|
|
1092
|
+
return this.insertToken("skill", tokenKey, label, this.getSlashTrigger());
|
|
1101
1093
|
};
|
|
1102
1094
|
this.syncSelectedSkills = (nextKeys, options) => {
|
|
1103
1095
|
const selectedSkillKeys = this.getSelectedSkillKeys();
|
|
@@ -1150,6 +1142,19 @@ var ChatComposerController = class {
|
|
|
1150
1142
|
this.getSelectedSkillKeys = () => {
|
|
1151
1143
|
return extractChatComposerTokenKeys(this.nodes, "skill");
|
|
1152
1144
|
};
|
|
1145
|
+
this.insertToken = (tokenKind, tokenKey, label, trigger = null) => {
|
|
1146
|
+
const documentLength = this.getDocumentLength();
|
|
1147
|
+
const replaceStart = trigger?.start ?? this.selection?.start ?? documentLength;
|
|
1148
|
+
const replaceEnd = trigger?.end ?? this.selection?.end ?? replaceStart;
|
|
1149
|
+
this.nodes = replaceChatComposerRange(
|
|
1150
|
+
this.nodes,
|
|
1151
|
+
replaceStart,
|
|
1152
|
+
replaceEnd,
|
|
1153
|
+
[createChatComposerTokenNode({ tokenKind, tokenKey, label })]
|
|
1154
|
+
);
|
|
1155
|
+
this.selection = { start: replaceStart + 1, end: replaceStart + 1 };
|
|
1156
|
+
return this.getSnapshot();
|
|
1157
|
+
};
|
|
1153
1158
|
this.getSlashTrigger = () => {
|
|
1154
1159
|
return resolveChatComposerSlashTrigger(this.nodes, this.selection);
|
|
1155
1160
|
};
|
|
@@ -1246,32 +1251,76 @@ var ChatComposerSurfaceRenderer = class {
|
|
|
1246
1251
|
element.dataset.composerTokenKind = node.tokenKind;
|
|
1247
1252
|
element.dataset.composerTokenKey = node.tokenKey;
|
|
1248
1253
|
element.dataset.composerLabel = node.label;
|
|
1249
|
-
element.
|
|
1254
|
+
element.title = node.label;
|
|
1255
|
+
element.className = this.buildTokenClassName(node.tokenKind, isSelected);
|
|
1256
|
+
element.append(this.createTokenIcon(node.tokenKind));
|
|
1257
|
+
const label = document.createElement("span");
|
|
1258
|
+
label.className = node.tokenKind === "file" ? "min-w-0 flex-1 truncate text-[12px] font-medium text-slate-800" : "truncate";
|
|
1259
|
+
label.textContent = node.label;
|
|
1260
|
+
element.append(label);
|
|
1261
|
+
if (node.tokenKind === "file") {
|
|
1262
|
+
const badge = document.createElement("span");
|
|
1263
|
+
badge.className = [
|
|
1264
|
+
"hidden",
|
|
1265
|
+
"shrink-0",
|
|
1266
|
+
"rounded-md",
|
|
1267
|
+
"border",
|
|
1268
|
+
"border-sky-100",
|
|
1269
|
+
"bg-sky-50",
|
|
1270
|
+
"px-1.5",
|
|
1271
|
+
"py-0.5",
|
|
1272
|
+
"text-[9px]",
|
|
1273
|
+
"font-semibold",
|
|
1274
|
+
"uppercase",
|
|
1275
|
+
"tracking-[0.12em]",
|
|
1276
|
+
"text-sky-700",
|
|
1277
|
+
"sm:inline-flex"
|
|
1278
|
+
].join(" ");
|
|
1279
|
+
badge.textContent = this.resolveFileBadgeLabel(node.label);
|
|
1280
|
+
element.append(badge);
|
|
1281
|
+
}
|
|
1282
|
+
return element;
|
|
1283
|
+
};
|
|
1284
|
+
this.buildTokenClassName = (tokenKind, isSelected) => {
|
|
1285
|
+
if (tokenKind === "file") {
|
|
1286
|
+
return [
|
|
1287
|
+
"mx-[2px]",
|
|
1288
|
+
"inline-flex",
|
|
1289
|
+
"h-8",
|
|
1290
|
+
"max-w-[min(100%,19rem)]",
|
|
1291
|
+
"items-center",
|
|
1292
|
+
"gap-2",
|
|
1293
|
+
"rounded-xl",
|
|
1294
|
+
"border",
|
|
1295
|
+
"px-2",
|
|
1296
|
+
"pr-2.5",
|
|
1297
|
+
"align-baseline",
|
|
1298
|
+
"shadow-[0_1px_2px_rgba(15,23,42,0.06)]",
|
|
1299
|
+
"transition-[border-color,background-color,box-shadow,color]",
|
|
1300
|
+
"duration-150",
|
|
1301
|
+
isSelected ? "border-sky-300 bg-sky-50 text-slate-900 shadow-[0_0_0_3px_rgba(14,165,233,0.14)]" : "border-slate-200 bg-[linear-gradient(180deg,rgba(255,255,255,1),rgba(248,250,252,0.98))] text-slate-700"
|
|
1302
|
+
].join(" ");
|
|
1303
|
+
}
|
|
1304
|
+
return [
|
|
1250
1305
|
"mx-[2px]",
|
|
1251
1306
|
"inline-flex",
|
|
1252
|
-
"h-
|
|
1307
|
+
"h-7",
|
|
1253
1308
|
"max-w-full",
|
|
1254
1309
|
"items-center",
|
|
1255
|
-
"gap-1",
|
|
1256
|
-
"rounded-
|
|
1310
|
+
"gap-1.5",
|
|
1311
|
+
"rounded-lg",
|
|
1257
1312
|
"border",
|
|
1258
|
-
"px-
|
|
1313
|
+
"px-2",
|
|
1259
1314
|
"align-baseline",
|
|
1260
1315
|
"text-[11px]",
|
|
1261
1316
|
"font-medium",
|
|
1262
1317
|
"transition",
|
|
1263
1318
|
isSelected ? "border-primary/30 bg-primary/18 text-primary" : "border-primary/12 bg-primary/8 text-primary"
|
|
1264
1319
|
].join(" ");
|
|
1265
|
-
element.append(this.createTokenIcon(node.tokenKind));
|
|
1266
|
-
const label = document.createElement("span");
|
|
1267
|
-
label.className = "truncate";
|
|
1268
|
-
label.textContent = node.label;
|
|
1269
|
-
element.append(label);
|
|
1270
|
-
return element;
|
|
1271
1320
|
};
|
|
1272
1321
|
this.createTokenIcon = (tokenKind) => {
|
|
1273
1322
|
const wrapper = document.createElement("span");
|
|
1274
|
-
wrapper.className = "inline-flex h-3 w-3 shrink-0 items-center justify-center text-primary/70";
|
|
1323
|
+
wrapper.className = tokenKind === "file" ? "inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-lg bg-sky-100 text-sky-700" : "inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center text-primary/70";
|
|
1275
1324
|
wrapper.append(tokenKind === "file" ? this.createFileIcon() : this.createSkillIcon());
|
|
1276
1325
|
return wrapper;
|
|
1277
1326
|
};
|
|
@@ -1285,12 +1334,15 @@ var ChatComposerSurfaceRenderer = class {
|
|
|
1285
1334
|
};
|
|
1286
1335
|
this.createFileIcon = () => {
|
|
1287
1336
|
return this.createSvgIcon([
|
|
1288
|
-
{ tag: "path", attrs: { d: "
|
|
1289
|
-
{ tag: "path", attrs: { d: "
|
|
1290
|
-
{ tag: "path", attrs: { d: "
|
|
1291
|
-
{ tag: "path", attrs: { d: "M5.75 10.75h4.5" } }
|
|
1337
|
+
{ tag: "path", attrs: { d: "M3.25 4.25A1.5 1.5 0 0 1 4.75 2.75h6.5a1.5 1.5 0 0 1 1.5 1.5v7.5a1.5 1.5 0 0 1-1.5 1.5h-6.5a1.5 1.5 0 0 1-1.5-1.5v-7.5Z" } },
|
|
1338
|
+
{ tag: "path", attrs: { d: "m4.75 10 2.25-2.5 1.75 1.75 1.25-1.25 2 2" } },
|
|
1339
|
+
{ tag: "path", attrs: { d: "M9.75 6.25h.01" } }
|
|
1292
1340
|
]);
|
|
1293
1341
|
};
|
|
1342
|
+
this.resolveFileBadgeLabel = (label) => {
|
|
1343
|
+
const match = /\.([a-z0-9]+)$/i.exec(label.trim());
|
|
1344
|
+
return match?.[1]?.slice(0, 4).toUpperCase() || "IMG";
|
|
1345
|
+
};
|
|
1294
1346
|
this.createSvgIcon = (children) => {
|
|
1295
1347
|
const svg = document.createElementNS(SVG_NAMESPACE, "svg");
|
|
1296
1348
|
svg.setAttribute("viewBox", "0 0 16 16");
|
|
@@ -1443,7 +1495,13 @@ var ChatComposerViewController = class {
|
|
|
1443
1495
|
}
|
|
1444
1496
|
};
|
|
1445
1497
|
this.handlePaste = (params) => {
|
|
1446
|
-
const { event, commitSnapshot } = params;
|
|
1498
|
+
const { event, onFilesAdd, commitSnapshot } = params;
|
|
1499
|
+
const files = Array.from(event.clipboardData.files ?? []);
|
|
1500
|
+
if (files.length > 0 && onFilesAdd) {
|
|
1501
|
+
event.preventDefault();
|
|
1502
|
+
void onFilesAdd(files);
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1447
1505
|
const text = event.clipboardData.getData("text/plain");
|
|
1448
1506
|
if (!text) {
|
|
1449
1507
|
return;
|
|
@@ -1452,8 +1510,8 @@ var ChatComposerViewController = class {
|
|
|
1452
1510
|
commitSnapshot(this.controller.insertText(text));
|
|
1453
1511
|
};
|
|
1454
1512
|
this.handleBlur = (params) => {
|
|
1455
|
-
const {
|
|
1456
|
-
|
|
1513
|
+
const { clearSelectedRange, onSlashQueryChange, onSlashOpenChange } = params;
|
|
1514
|
+
clearSelectedRange();
|
|
1457
1515
|
onSlashQueryChange?.(null);
|
|
1458
1516
|
onSlashOpenChange(false);
|
|
1459
1517
|
};
|
|
@@ -1497,6 +1555,23 @@ var ChatComposerRuntime = class {
|
|
|
1497
1555
|
insertSlashItem: (item) => {
|
|
1498
1556
|
this.viewController.insertSlashItem(item, this.commitSnapshot);
|
|
1499
1557
|
},
|
|
1558
|
+
insertFileToken: (tokenKey, label) => {
|
|
1559
|
+
this.commitSnapshot(this.controller.insertFileToken(tokenKey, label));
|
|
1560
|
+
this.focusComposerSoon();
|
|
1561
|
+
},
|
|
1562
|
+
insertFileTokens: (tokens) => {
|
|
1563
|
+
let nextSnapshot = null;
|
|
1564
|
+
for (const token of tokens) {
|
|
1565
|
+
nextSnapshot = this.controller.insertFileToken(token.tokenKey, token.label);
|
|
1566
|
+
}
|
|
1567
|
+
if (nextSnapshot) {
|
|
1568
|
+
this.commitSnapshot(nextSnapshot);
|
|
1569
|
+
this.focusComposerSoon();
|
|
1570
|
+
}
|
|
1571
|
+
},
|
|
1572
|
+
focusComposer: () => {
|
|
1573
|
+
this.focusComposer();
|
|
1574
|
+
},
|
|
1500
1575
|
syncSelectedSkills: (nextKeys, options) => {
|
|
1501
1576
|
this.viewController.syncSelectedSkills(nextKeys, options, this.commitSnapshot);
|
|
1502
1577
|
}
|
|
@@ -1558,6 +1633,12 @@ var ChatComposerRuntime = class {
|
|
|
1558
1633
|
};
|
|
1559
1634
|
this.handleKeyDown = (event) => {
|
|
1560
1635
|
const config = this.requireConfig();
|
|
1636
|
+
if (this.rootElement && !this.isComposing) {
|
|
1637
|
+
const nextSnapshot = this.viewController.syncSelectionFromRoot(this.rootElement);
|
|
1638
|
+
this.selection = nextSnapshot.selection;
|
|
1639
|
+
this.selectedRange = nextSnapshot.selection;
|
|
1640
|
+
this.snapshot = nextSnapshot;
|
|
1641
|
+
}
|
|
1561
1642
|
const activeSlashItem = config.slashItems[config.activeSlashIndex] ?? null;
|
|
1562
1643
|
this.viewController.handleKeyDown({
|
|
1563
1644
|
event,
|
|
@@ -1575,6 +1656,7 @@ var ChatComposerRuntime = class {
|
|
|
1575
1656
|
this.handlePaste = (event) => {
|
|
1576
1657
|
this.viewController.handlePaste({
|
|
1577
1658
|
event,
|
|
1659
|
+
onFilesAdd: this.requireConfig().onFilesAdd,
|
|
1578
1660
|
commitSnapshot: this.commitSnapshot
|
|
1579
1661
|
});
|
|
1580
1662
|
};
|
|
@@ -1582,7 +1664,7 @@ var ChatComposerRuntime = class {
|
|
|
1582
1664
|
const config = this.requireConfig();
|
|
1583
1665
|
this.isComposing = false;
|
|
1584
1666
|
this.viewController.handleBlur({
|
|
1585
|
-
|
|
1667
|
+
clearSelectedRange: this.clearSelectedRange,
|
|
1586
1668
|
onSlashQueryChange: config.onSlashQueryChange,
|
|
1587
1669
|
onSlashOpenChange: config.onSlashOpenChange
|
|
1588
1670
|
});
|
|
@@ -1592,6 +1674,10 @@ var ChatComposerRuntime = class {
|
|
|
1592
1674
|
this.selection = selection;
|
|
1593
1675
|
this.requestRender();
|
|
1594
1676
|
};
|
|
1677
|
+
this.clearSelectedRange = () => {
|
|
1678
|
+
this.selectedRange = null;
|
|
1679
|
+
this.requestRender();
|
|
1680
|
+
};
|
|
1595
1681
|
this.commitSnapshot = (nextSnapshot) => {
|
|
1596
1682
|
const config = this.requireConfig();
|
|
1597
1683
|
this.selection = nextSnapshot.selection;
|
|
@@ -1605,12 +1691,27 @@ var ChatComposerRuntime = class {
|
|
|
1605
1691
|
};
|
|
1606
1692
|
this.syncSlashState = (nextSnapshot) => {
|
|
1607
1693
|
const config = this.requireConfig();
|
|
1694
|
+
config.onSlashTriggerChange?.(nextSnapshot.slashTrigger);
|
|
1608
1695
|
config.onSlashQueryChange?.(nextSnapshot.slashTrigger?.query ?? null);
|
|
1609
1696
|
config.onSlashOpenChange(nextSnapshot.slashTrigger !== null);
|
|
1610
1697
|
};
|
|
1611
1698
|
this.requestRender = () => {
|
|
1612
1699
|
this.requireConfig().requestRender();
|
|
1613
1700
|
};
|
|
1701
|
+
this.focusComposerSoon = () => {
|
|
1702
|
+
if (typeof requestAnimationFrame === "function") {
|
|
1703
|
+
requestAnimationFrame(() => this.focusComposer());
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
this.focusComposer();
|
|
1707
|
+
};
|
|
1708
|
+
this.focusComposer = () => {
|
|
1709
|
+
if (!this.rootElement) {
|
|
1710
|
+
return;
|
|
1711
|
+
}
|
|
1712
|
+
this.rootElement.focus();
|
|
1713
|
+
this.viewController.restoreSelectionIfFocused(this.rootElement, this.selection);
|
|
1714
|
+
};
|
|
1614
1715
|
this.requireConfig = () => {
|
|
1615
1716
|
if (!this.config) {
|
|
1616
1717
|
throw new Error("ChatComposerRuntime is not configured.");
|
|
@@ -1630,7 +1731,9 @@ var ChatInputBarTokenizedComposer = forwardRef6(function ChatInputBarTokenizedCo
|
|
|
1630
1731
|
slashItems,
|
|
1631
1732
|
actions,
|
|
1632
1733
|
onNodesChange,
|
|
1734
|
+
onFilesAdd,
|
|
1633
1735
|
onSlashQueryChange,
|
|
1736
|
+
onSlashTriggerChange,
|
|
1634
1737
|
onSlashOpenChange,
|
|
1635
1738
|
onSlashActiveIndexChange,
|
|
1636
1739
|
activeSlashIndex
|
|
@@ -1655,7 +1758,9 @@ var ChatInputBarTokenizedComposer = forwardRef6(function ChatInputBarTokenizedCo
|
|
|
1655
1758
|
slashItems,
|
|
1656
1759
|
actions,
|
|
1657
1760
|
onNodesChange,
|
|
1761
|
+
onFilesAdd,
|
|
1658
1762
|
onSlashQueryChange,
|
|
1763
|
+
onSlashTriggerChange,
|
|
1659
1764
|
onSlashOpenChange,
|
|
1660
1765
|
onSlashActiveIndexChange,
|
|
1661
1766
|
activeSlashIndex,
|
|
@@ -1718,11 +1823,13 @@ function InputBarHint({ hint }) {
|
|
|
1718
1823
|
) : null
|
|
1719
1824
|
] }) });
|
|
1720
1825
|
}
|
|
1721
|
-
|
|
1826
|
+
var ChatInputBar = forwardRef7(function ChatInputBar2(props, ref) {
|
|
1722
1827
|
const composerRef = useRef4(null);
|
|
1723
1828
|
const [slashQuery, setSlashQuery] = useState4(null);
|
|
1724
1829
|
const [activeSlashIndex, setActiveSlashIndex] = useState4(0);
|
|
1725
|
-
const
|
|
1830
|
+
const [activeSlashTriggerStart, setActiveSlashTriggerStart] = useState4(null);
|
|
1831
|
+
const [dismissedSlashTriggerStart, setDismissedSlashTriggerStart] = useState4(null);
|
|
1832
|
+
const isSlashPanelOpen = activeSlashTriggerStart !== null && dismissedSlashTriggerStart !== activeSlashTriggerStart;
|
|
1726
1833
|
const activeSlashItem = props.slashMenu.items[activeSlashIndex] ?? null;
|
|
1727
1834
|
useEffect4(() => {
|
|
1728
1835
|
setActiveSlashIndex((current) => {
|
|
@@ -1737,6 +1844,11 @@ function ChatInputBar(props) {
|
|
|
1737
1844
|
setActiveSlashIndex(0);
|
|
1738
1845
|
}
|
|
1739
1846
|
}, [slashQuery]);
|
|
1847
|
+
useEffect4(() => {
|
|
1848
|
+
if (activeSlashTriggerStart === null && dismissedSlashTriggerStart !== null) {
|
|
1849
|
+
setDismissedSlashTriggerStart(null);
|
|
1850
|
+
}
|
|
1851
|
+
}, [activeSlashTriggerStart, dismissedSlashTriggerStart]);
|
|
1740
1852
|
const toolbar = useMemo3(() => {
|
|
1741
1853
|
if (!props.toolbar.skillPicker) {
|
|
1742
1854
|
return props.toolbar;
|
|
@@ -1751,6 +1863,11 @@ function ChatInputBar(props) {
|
|
|
1751
1863
|
}
|
|
1752
1864
|
};
|
|
1753
1865
|
}, [props.toolbar]);
|
|
1866
|
+
useImperativeHandle2(ref, () => ({
|
|
1867
|
+
insertFileToken: (tokenKey, label) => composerRef.current?.insertFileToken(tokenKey, label),
|
|
1868
|
+
insertFileTokens: (tokens) => composerRef.current?.insertFileTokens(tokens),
|
|
1869
|
+
focusComposer: () => composerRef.current?.focusComposer()
|
|
1870
|
+
}), []);
|
|
1754
1871
|
return /* @__PURE__ */ jsx11("div", { className: "border-t border-gray-200/80 bg-white p-4", children: /* @__PURE__ */ jsx11("div", { className: "mx-auto w-full max-w-[min(1120px,100%)]", children: /* @__PURE__ */ jsxs6("div", { className: "overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-card", children: [
|
|
1755
1872
|
/* @__PURE__ */ jsxs6("div", { className: "relative", children: [
|
|
1756
1873
|
/* @__PURE__ */ jsx11(
|
|
@@ -1764,13 +1881,17 @@ function ChatInputBar(props) {
|
|
|
1764
1881
|
actions: props.toolbar.actions,
|
|
1765
1882
|
activeSlashIndex,
|
|
1766
1883
|
onNodesChange: props.composer.onNodesChange,
|
|
1884
|
+
onFilesAdd: props.composer.onFilesAdd,
|
|
1767
1885
|
onSlashQueryChange: (query) => {
|
|
1768
1886
|
setSlashQuery(query);
|
|
1769
1887
|
props.composer.onSlashQueryChange?.(query);
|
|
1770
1888
|
},
|
|
1889
|
+
onSlashTriggerChange: (trigger) => {
|
|
1890
|
+
setActiveSlashTriggerStart(trigger?.start ?? null);
|
|
1891
|
+
},
|
|
1771
1892
|
onSlashOpenChange: (open) => {
|
|
1772
|
-
if (!open) {
|
|
1773
|
-
|
|
1893
|
+
if (!open && activeSlashTriggerStart !== null) {
|
|
1894
|
+
setDismissedSlashTriggerStart(activeSlashTriggerStart);
|
|
1774
1895
|
}
|
|
1775
1896
|
},
|
|
1776
1897
|
onSlashActiveIndexChange: setActiveSlashIndex
|
|
@@ -1786,11 +1907,12 @@ function ChatInputBar(props) {
|
|
|
1786
1907
|
activeItem: activeSlashItem,
|
|
1787
1908
|
texts: props.slashMenu.texts,
|
|
1788
1909
|
onSelectItem: (item) => {
|
|
1910
|
+
setDismissedSlashTriggerStart(null);
|
|
1789
1911
|
composerRef.current?.insertSlashItem(item);
|
|
1790
1912
|
},
|
|
1791
1913
|
onOpenChange: (open) => {
|
|
1792
|
-
if (!open) {
|
|
1793
|
-
|
|
1914
|
+
if (!open && activeSlashTriggerStart !== null) {
|
|
1915
|
+
setDismissedSlashTriggerStart(activeSlashTriggerStart);
|
|
1794
1916
|
}
|
|
1795
1917
|
},
|
|
1796
1918
|
onSetActiveIndex: setActiveSlashIndex
|
|
@@ -1800,7 +1922,8 @@ function ChatInputBar(props) {
|
|
|
1800
1922
|
/* @__PURE__ */ jsx11(InputBarHint, { hint: props.hint }),
|
|
1801
1923
|
/* @__PURE__ */ jsx11(ChatInputBarToolbar, { ...toolbar })
|
|
1802
1924
|
] }) }) });
|
|
1803
|
-
}
|
|
1925
|
+
});
|
|
1926
|
+
ChatInputBar.displayName = "ChatInputBar";
|
|
1804
1927
|
|
|
1805
1928
|
// src/components/chat/ui/chat-message-list/chat-message-avatar.tsx
|
|
1806
1929
|
import { Bot, User, Wrench } from "lucide-react";
|
|
@@ -2077,12 +2200,27 @@ function ChatMessageMarkdown(props) {
|
|
|
2077
2200
|
return /* @__PURE__ */ jsx14("div", { className: cn("chat-markdown", isUser ? "chat-markdown-user" : "chat-markdown-assistant"), children: /* @__PURE__ */ jsx14(ReactMarkdown, { skipHtml: true, remarkPlugins: [remarkGfm], components: markdownComponents, children: trimMarkdown(props.text) }) });
|
|
2078
2201
|
}
|
|
2079
2202
|
|
|
2080
|
-
// src/components/chat/ui/chat-message-list/chat-
|
|
2203
|
+
// src/components/chat/ui/chat-message-list/chat-message-file.tsx
|
|
2081
2204
|
import { jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2205
|
+
function ChatMessageFile({ file }) {
|
|
2206
|
+
if (file.isImage && file.dataUrl) {
|
|
2207
|
+
return /* @__PURE__ */ jsxs8("figure", { className: "overflow-hidden rounded-2xl border border-black/8 bg-black/6", children: [
|
|
2208
|
+
/* @__PURE__ */ jsx15("img", { src: file.dataUrl, alt: file.label, className: "block max-h-80 w-full object-contain" }),
|
|
2209
|
+
/* @__PURE__ */ jsx15("figcaption", { className: "border-t border-black/8 px-3 py-2 text-xs opacity-80", children: file.label })
|
|
2210
|
+
] });
|
|
2211
|
+
}
|
|
2212
|
+
return /* @__PURE__ */ jsxs8("div", { className: "rounded-2xl border border-black/8 bg-black/6 px-3 py-2 text-sm", children: [
|
|
2213
|
+
/* @__PURE__ */ jsx15("div", { className: "font-medium", children: file.label }),
|
|
2214
|
+
/* @__PURE__ */ jsx15("div", { className: "text-xs opacity-75", children: file.mimeType })
|
|
2215
|
+
] });
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
// src/components/chat/ui/chat-message-list/chat-reasoning-block.tsx
|
|
2219
|
+
import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2082
2220
|
function ChatReasoningBlock(props) {
|
|
2083
|
-
return /* @__PURE__ */
|
|
2084
|
-
/* @__PURE__ */
|
|
2085
|
-
/* @__PURE__ */
|
|
2221
|
+
return /* @__PURE__ */ jsxs9("details", { className: "mt-3", open: true, children: [
|
|
2222
|
+
/* @__PURE__ */ jsx16("summary", { className: cn("cursor-pointer text-xs", props.isUser ? "text-primary-100" : "text-gray-500"), children: props.label }),
|
|
2223
|
+
/* @__PURE__ */ jsx16(
|
|
2086
2224
|
"pre",
|
|
2087
2225
|
{
|
|
2088
2226
|
className: cn(
|
|
@@ -2097,87 +2235,90 @@ function ChatReasoningBlock(props) {
|
|
|
2097
2235
|
|
|
2098
2236
|
// src/components/chat/ui/chat-message-list/chat-tool-card.tsx
|
|
2099
2237
|
import { Clock3, FileSearch, Globe, Search as Search2, SendHorizontal, Terminal, Wrench as Wrench2 } from "lucide-react";
|
|
2100
|
-
import { jsx as
|
|
2238
|
+
import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2101
2239
|
var TOOL_OUTPUT_PREVIEW_MAX = 220;
|
|
2102
2240
|
function renderToolIcon(toolName) {
|
|
2103
2241
|
const lowered = toolName.toLowerCase();
|
|
2104
2242
|
if (lowered.includes("exec") || lowered.includes("shell") || lowered.includes("command")) {
|
|
2105
|
-
return /* @__PURE__ */
|
|
2243
|
+
return /* @__PURE__ */ jsx17(Terminal, { className: "h-3.5 w-3.5" });
|
|
2106
2244
|
}
|
|
2107
2245
|
if (lowered.includes("search")) {
|
|
2108
|
-
return /* @__PURE__ */
|
|
2246
|
+
return /* @__PURE__ */ jsx17(Search2, { className: "h-3.5 w-3.5" });
|
|
2109
2247
|
}
|
|
2110
2248
|
if (lowered.includes("fetch") || lowered.includes("http") || lowered.includes("web")) {
|
|
2111
|
-
return /* @__PURE__ */
|
|
2249
|
+
return /* @__PURE__ */ jsx17(Globe, { className: "h-3.5 w-3.5" });
|
|
2112
2250
|
}
|
|
2113
2251
|
if (lowered.includes("read") || lowered.includes("file")) {
|
|
2114
|
-
return /* @__PURE__ */
|
|
2252
|
+
return /* @__PURE__ */ jsx17(FileSearch, { className: "h-3.5 w-3.5" });
|
|
2115
2253
|
}
|
|
2116
2254
|
if (lowered.includes("message") || lowered.includes("send")) {
|
|
2117
|
-
return /* @__PURE__ */
|
|
2255
|
+
return /* @__PURE__ */ jsx17(SendHorizontal, { className: "h-3.5 w-3.5" });
|
|
2118
2256
|
}
|
|
2119
2257
|
if (lowered.includes("cron") || lowered.includes("schedule")) {
|
|
2120
|
-
return /* @__PURE__ */
|
|
2258
|
+
return /* @__PURE__ */ jsx17(Clock3, { className: "h-3.5 w-3.5" });
|
|
2121
2259
|
}
|
|
2122
|
-
return /* @__PURE__ */
|
|
2260
|
+
return /* @__PURE__ */ jsx17(Wrench2, { className: "h-3.5 w-3.5" });
|
|
2123
2261
|
}
|
|
2124
2262
|
function ChatToolCard({ card }) {
|
|
2125
2263
|
const output = card.output?.trim() ?? "";
|
|
2126
2264
|
const showDetails = output.length > TOOL_OUTPUT_PREVIEW_MAX || output.includes("\n");
|
|
2127
2265
|
const preview = showDetails ? `${output.slice(0, TOOL_OUTPUT_PREVIEW_MAX)}...` : output;
|
|
2128
2266
|
const showOutputSection = card.kind === "result" || card.hasResult;
|
|
2129
|
-
return /* @__PURE__ */
|
|
2130
|
-
/* @__PURE__ */
|
|
2267
|
+
return /* @__PURE__ */ jsxs10("div", { className: "rounded-xl border border-amber-200/80 bg-amber-50/60 px-3 py-2.5", children: [
|
|
2268
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap items-center gap-2 text-xs font-semibold text-amber-800", children: [
|
|
2131
2269
|
renderToolIcon(card.toolName),
|
|
2132
|
-
/* @__PURE__ */
|
|
2133
|
-
/* @__PURE__ */
|
|
2270
|
+
/* @__PURE__ */ jsx17("span", { children: card.titleLabel }),
|
|
2271
|
+
/* @__PURE__ */ jsx17("span", { className: "font-mono text-[11px] text-amber-900/80", children: card.toolName })
|
|
2134
2272
|
] }),
|
|
2135
|
-
card.summary ? /* @__PURE__ */
|
|
2136
|
-
showOutputSection ? /* @__PURE__ */
|
|
2137
|
-
/* @__PURE__ */
|
|
2138
|
-
/* @__PURE__ */
|
|
2139
|
-
] }) : /* @__PURE__ */
|
|
2273
|
+
card.summary ? /* @__PURE__ */ jsx17("div", { className: "mt-1 break-words font-mono text-[11px] text-amber-800/90", children: card.summary }) : null,
|
|
2274
|
+
showOutputSection ? /* @__PURE__ */ jsx17("div", { className: "mt-2", children: !output ? /* @__PURE__ */ jsx17("div", { className: "text-[11px] text-amber-700/80", children: card.emptyLabel }) : showDetails ? /* @__PURE__ */ jsxs10("details", { className: "group", children: [
|
|
2275
|
+
/* @__PURE__ */ jsx17("summary", { className: "cursor-pointer text-[11px] text-amber-700", children: card.outputLabel }),
|
|
2276
|
+
/* @__PURE__ */ jsx17("pre", { className: "mt-2 whitespace-pre-wrap break-words rounded-lg border border-amber-200 bg-amber-100/40 p-2 text-[11px] text-amber-900", children: output })
|
|
2277
|
+
] }) : /* @__PURE__ */ jsx17("pre", { className: "rounded-lg border border-amber-200 bg-amber-100/40 p-2 text-[11px] whitespace-pre-wrap break-words text-amber-900", children: preview }) }) : null
|
|
2140
2278
|
] });
|
|
2141
2279
|
}
|
|
2142
2280
|
|
|
2143
2281
|
// src/components/chat/ui/chat-message-list/chat-unknown-part.tsx
|
|
2144
|
-
import { jsx as
|
|
2282
|
+
import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2145
2283
|
function ChatUnknownPart(props) {
|
|
2146
|
-
return /* @__PURE__ */
|
|
2147
|
-
/* @__PURE__ */
|
|
2284
|
+
return /* @__PURE__ */ jsxs11("div", { className: "rounded-lg border border-gray-200 bg-gray-50 px-2.5 py-2 text-xs text-gray-600", children: [
|
|
2285
|
+
/* @__PURE__ */ jsxs11("div", { className: "font-semibold text-gray-700", children: [
|
|
2148
2286
|
props.label,
|
|
2149
2287
|
": ",
|
|
2150
2288
|
props.rawType
|
|
2151
2289
|
] }),
|
|
2152
|
-
props.text ? /* @__PURE__ */
|
|
2290
|
+
props.text ? /* @__PURE__ */ jsx18("pre", { className: "mt-1 whitespace-pre-wrap break-words text-[11px] text-gray-500", children: props.text }) : null
|
|
2153
2291
|
] });
|
|
2154
2292
|
}
|
|
2155
2293
|
|
|
2156
2294
|
// src/components/chat/ui/chat-message-list/chat-message.tsx
|
|
2157
|
-
import { jsx as
|
|
2295
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
2158
2296
|
function ChatMessage(props) {
|
|
2159
2297
|
const { message, texts } = props;
|
|
2160
2298
|
const { role } = message;
|
|
2161
2299
|
const isUser = role === "user";
|
|
2162
|
-
return /* @__PURE__ */
|
|
2300
|
+
return /* @__PURE__ */ jsx19(
|
|
2163
2301
|
"div",
|
|
2164
2302
|
{
|
|
2165
2303
|
className: cn(
|
|
2166
2304
|
"inline-block w-fit max-w-full rounded-2xl border px-4 py-3 shadow-sm",
|
|
2167
2305
|
isUser ? "border-primary bg-primary text-white" : role === "assistant" ? "border-gray-200 bg-white text-gray-900" : "border-orange-200/80 bg-orange-50/70 text-gray-900"
|
|
2168
2306
|
),
|
|
2169
|
-
children: /* @__PURE__ */
|
|
2307
|
+
children: /* @__PURE__ */ jsx19("div", { className: "space-y-2", children: message.parts.map((part, index) => {
|
|
2170
2308
|
if (part.type === "markdown") {
|
|
2171
|
-
return /* @__PURE__ */
|
|
2309
|
+
return /* @__PURE__ */ jsx19(ChatMessageMarkdown, { text: part.text, role, texts }, `markdown-${index}`);
|
|
2172
2310
|
}
|
|
2173
2311
|
if (part.type === "reasoning") {
|
|
2174
|
-
return /* @__PURE__ */
|
|
2312
|
+
return /* @__PURE__ */ jsx19(ChatReasoningBlock, { label: part.label, text: part.text, isUser }, `reasoning-${index}`);
|
|
2175
2313
|
}
|
|
2176
2314
|
if (part.type === "tool-card") {
|
|
2177
|
-
return /* @__PURE__ */
|
|
2315
|
+
return /* @__PURE__ */ jsx19("div", { className: "mt-0.5", children: /* @__PURE__ */ jsx19(ChatToolCard, { card: part.card }) }, `tool-${index}`);
|
|
2316
|
+
}
|
|
2317
|
+
if (part.type === "file") {
|
|
2318
|
+
return /* @__PURE__ */ jsx19(ChatMessageFile, { file: part.file }, `file-${index}`);
|
|
2178
2319
|
}
|
|
2179
2320
|
if (part.type === "unknown") {
|
|
2180
|
-
return /* @__PURE__ */
|
|
2321
|
+
return /* @__PURE__ */ jsx19(ChatUnknownPart, { label: part.label, rawType: part.rawType, text: part.text }, `unknown-${index}`);
|
|
2181
2322
|
}
|
|
2182
2323
|
return null;
|
|
2183
2324
|
}) })
|
|
@@ -2186,9 +2327,9 @@ function ChatMessage(props) {
|
|
|
2186
2327
|
}
|
|
2187
2328
|
|
|
2188
2329
|
// src/components/chat/ui/chat-message-list/chat-message-meta.tsx
|
|
2189
|
-
import { jsxs as
|
|
2330
|
+
import { jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2190
2331
|
function ChatMessageMeta(props) {
|
|
2191
|
-
return /* @__PURE__ */
|
|
2332
|
+
return /* @__PURE__ */ jsxs12(
|
|
2192
2333
|
"div",
|
|
2193
2334
|
{
|
|
2194
2335
|
className: cn(
|
|
@@ -2205,7 +2346,7 @@ function ChatMessageMeta(props) {
|
|
|
2205
2346
|
}
|
|
2206
2347
|
|
|
2207
2348
|
// src/components/chat/ui/chat-message-list/chat-message-list.tsx
|
|
2208
|
-
import { jsx as
|
|
2349
|
+
import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2209
2350
|
var INVISIBLE_ONLY_TEXT_PATTERN = /\u200B|\u200C|\u200D|\u2060|\uFEFF/g;
|
|
2210
2351
|
function hasRenderableText(value) {
|
|
2211
2352
|
const trimmed = value.trim();
|
|
@@ -2227,21 +2368,21 @@ function ChatMessageList(props) {
|
|
|
2227
2368
|
const hasRenderableAssistantDraft = visibleMessages.some(
|
|
2228
2369
|
(message) => message.role === "assistant" && (message.status === "streaming" || message.status === "pending")
|
|
2229
2370
|
);
|
|
2230
|
-
return /* @__PURE__ */
|
|
2371
|
+
return /* @__PURE__ */ jsxs13("div", { className: cn("space-y-5", props.className), children: [
|
|
2231
2372
|
visibleMessages.map((message) => {
|
|
2232
2373
|
const isUser = message.role === "user";
|
|
2233
|
-
return /* @__PURE__ */
|
|
2234
|
-
!isUser ? /* @__PURE__ */
|
|
2235
|
-
/* @__PURE__ */
|
|
2236
|
-
/* @__PURE__ */
|
|
2237
|
-
/* @__PURE__ */
|
|
2374
|
+
return /* @__PURE__ */ jsxs13("div", { className: cn("flex gap-3", isUser ? "justify-end" : "justify-start"), children: [
|
|
2375
|
+
!isUser ? /* @__PURE__ */ jsx20(ChatMessageAvatar, { role: message.role }) : null,
|
|
2376
|
+
/* @__PURE__ */ jsxs13("div", { className: cn("w-fit max-w-[92%] space-y-2", isUser && "flex flex-col items-end"), children: [
|
|
2377
|
+
/* @__PURE__ */ jsx20(ChatMessage, { message, texts: props.texts }),
|
|
2378
|
+
/* @__PURE__ */ jsx20(ChatMessageMeta, { roleLabel: message.roleLabel, timestampLabel: message.timestampLabel, isUser })
|
|
2238
2379
|
] }),
|
|
2239
|
-
isUser ? /* @__PURE__ */
|
|
2380
|
+
isUser ? /* @__PURE__ */ jsx20(ChatMessageAvatar, { role: message.role }) : null
|
|
2240
2381
|
] }, message.id);
|
|
2241
2382
|
}),
|
|
2242
|
-
props.isSending && !hasRenderableAssistantDraft ? /* @__PURE__ */
|
|
2243
|
-
/* @__PURE__ */
|
|
2244
|
-
/* @__PURE__ */
|
|
2383
|
+
props.isSending && !hasRenderableAssistantDraft ? /* @__PURE__ */ jsxs13("div", { className: "flex justify-start gap-3", children: [
|
|
2384
|
+
/* @__PURE__ */ jsx20(ChatMessageAvatar, { role: "assistant" }),
|
|
2385
|
+
/* @__PURE__ */ jsx20("div", { className: "rounded-2xl border border-gray-200 bg-white px-4 py-3 text-sm text-gray-500 shadow-sm", children: props.texts.typingLabel })
|
|
2245
2386
|
] }) : null
|
|
2246
2387
|
] });
|
|
2247
2388
|
}
|