@nextclaw/agent-chat-ui 0.2.4 → 0.2.6
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 -15
- package/dist/index.js +95 -42
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -23,15 +23,15 @@ type ChatSelectedItem = {
|
|
|
23
23
|
key: string;
|
|
24
24
|
label: string;
|
|
25
25
|
};
|
|
26
|
-
type ChatComposerTokenKind =
|
|
26
|
+
type ChatComposerTokenKind = "skill" | "file";
|
|
27
27
|
type ChatComposerTextNode = {
|
|
28
28
|
id: string;
|
|
29
|
-
type:
|
|
29
|
+
type: "text";
|
|
30
30
|
text: string;
|
|
31
31
|
};
|
|
32
32
|
type ChatComposerTokenNode = {
|
|
33
33
|
id: string;
|
|
34
|
-
type:
|
|
34
|
+
type: "token";
|
|
35
35
|
tokenKind: ChatComposerTokenKind;
|
|
36
36
|
tokenKey: string;
|
|
37
37
|
label: string;
|
|
@@ -41,8 +41,8 @@ type ChatComposerSelection = {
|
|
|
41
41
|
start: number;
|
|
42
42
|
end: number;
|
|
43
43
|
};
|
|
44
|
-
type ChatToolbarIcon =
|
|
45
|
-
type ChatToolbarAccessoryIcon = ChatToolbarIcon |
|
|
44
|
+
type ChatToolbarIcon = "sparkles" | "brain";
|
|
45
|
+
type ChatToolbarAccessoryIcon = ChatToolbarIcon | "paperclip";
|
|
46
46
|
type ChatToolbarSelectOption = {
|
|
47
47
|
value: string;
|
|
48
48
|
label: string;
|
|
@@ -106,7 +106,7 @@ type ChatInputBarToolbarProps = {
|
|
|
106
106
|
actions: ChatInputBarActionsProps;
|
|
107
107
|
};
|
|
108
108
|
type ChatInlineHint = {
|
|
109
|
-
tone:
|
|
109
|
+
tone: "neutral" | "warning";
|
|
110
110
|
loading?: boolean;
|
|
111
111
|
text?: string;
|
|
112
112
|
actionLabel?: string;
|
|
@@ -118,7 +118,7 @@ type ChatSlashMenuProps = {
|
|
|
118
118
|
items: ChatSlashItem[];
|
|
119
119
|
activeIndex: number;
|
|
120
120
|
activeItem: ChatSlashItem | null;
|
|
121
|
-
texts: Pick<ChatTexts,
|
|
121
|
+
texts: Pick<ChatTexts, "slashLoadingLabel" | "slashSectionLabel" | "slashEmptyLabel" | "slashHintLabel" | "slashSkillHintLabel">;
|
|
122
122
|
onSelectItem: (item: ChatSlashItem) => void;
|
|
123
123
|
onOpenChange: (open: boolean) => void;
|
|
124
124
|
onSetActiveIndex: (index: number) => void;
|
|
@@ -132,41 +132,47 @@ type ChatInputBarProps = {
|
|
|
132
132
|
onFilesAdd?: (files: File[]) => Promise<void> | void;
|
|
133
133
|
onSlashQueryChange?: (query: string | null) => void;
|
|
134
134
|
};
|
|
135
|
-
slashMenu: Pick<ChatSlashMenuProps,
|
|
135
|
+
slashMenu: Pick<ChatSlashMenuProps, "isLoading" | "items" | "texts">;
|
|
136
136
|
hint?: ChatInlineHint | null;
|
|
137
137
|
toolbar: ChatInputBarToolbarProps;
|
|
138
138
|
};
|
|
139
|
-
type ChatMessageRole =
|
|
139
|
+
type ChatMessageRole = "user" | "assistant" | "tool" | "system" | "message";
|
|
140
140
|
type ChatToolPartViewModel = {
|
|
141
|
-
kind:
|
|
141
|
+
kind: "call" | "result";
|
|
142
142
|
toolName: string;
|
|
143
143
|
summary?: string;
|
|
144
144
|
output?: string;
|
|
145
145
|
hasResult: boolean;
|
|
146
|
+
statusTone: "running" | "success" | "error" | "cancelled";
|
|
147
|
+
statusLabel: string;
|
|
146
148
|
titleLabel: string;
|
|
149
|
+
inputLabel: string;
|
|
147
150
|
outputLabel: string;
|
|
148
151
|
emptyLabel: string;
|
|
152
|
+
callIdLabel: string;
|
|
153
|
+
callId?: string;
|
|
149
154
|
};
|
|
150
155
|
type ChatMessagePartViewModel = {
|
|
151
|
-
type:
|
|
156
|
+
type: "markdown";
|
|
152
157
|
text: string;
|
|
153
158
|
} | {
|
|
154
|
-
type:
|
|
159
|
+
type: "reasoning";
|
|
155
160
|
text: string;
|
|
156
161
|
label: string;
|
|
157
162
|
} | {
|
|
158
|
-
type:
|
|
163
|
+
type: "tool-card";
|
|
159
164
|
card: ChatToolPartViewModel;
|
|
160
165
|
} | {
|
|
161
|
-
type:
|
|
166
|
+
type: "file";
|
|
162
167
|
file: {
|
|
163
168
|
label: string;
|
|
164
169
|
mimeType: string;
|
|
165
170
|
dataUrl?: string;
|
|
171
|
+
sizeBytes?: number;
|
|
166
172
|
isImage: boolean;
|
|
167
173
|
};
|
|
168
174
|
} | {
|
|
169
|
-
type:
|
|
175
|
+
type: "unknown";
|
|
170
176
|
label: string;
|
|
171
177
|
rawType: string;
|
|
172
178
|
text?: string;
|
package/dist/index.js
CHANGED
|
@@ -1255,30 +1255,9 @@ var ChatComposerSurfaceRenderer = class {
|
|
|
1255
1255
|
element.className = this.buildTokenClassName(node.tokenKind, isSelected);
|
|
1256
1256
|
element.append(this.createTokenIcon(node.tokenKind));
|
|
1257
1257
|
const label = document.createElement("span");
|
|
1258
|
-
label.className = node.tokenKind === "file" ? "min-w-0 flex-1 truncate text-[12px] font-medium text-slate-
|
|
1258
|
+
label.className = node.tokenKind === "file" ? "min-w-0 flex-1 truncate text-[12px] font-medium text-slate-700" : "truncate";
|
|
1259
1259
|
label.textContent = node.label;
|
|
1260
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
1261
|
return element;
|
|
1283
1262
|
};
|
|
1284
1263
|
this.buildTokenClassName = (tokenKind, isSelected) => {
|
|
@@ -1286,19 +1265,17 @@ var ChatComposerSurfaceRenderer = class {
|
|
|
1286
1265
|
return [
|
|
1287
1266
|
"mx-[2px]",
|
|
1288
1267
|
"inline-flex",
|
|
1289
|
-
"h-
|
|
1290
|
-
"max-w-[min(100%,
|
|
1268
|
+
"h-7",
|
|
1269
|
+
"max-w-[min(100%,17rem)]",
|
|
1291
1270
|
"items-center",
|
|
1292
|
-
"gap-
|
|
1293
|
-
"rounded-
|
|
1271
|
+
"gap-1.5",
|
|
1272
|
+
"rounded-lg",
|
|
1294
1273
|
"border",
|
|
1295
1274
|
"px-2",
|
|
1296
|
-
"pr-2.5",
|
|
1297
1275
|
"align-baseline",
|
|
1298
|
-
"shadow-[0_1px_2px_rgba(15,23,42,0.06)]",
|
|
1299
1276
|
"transition-[border-color,background-color,box-shadow,color]",
|
|
1300
1277
|
"duration-150",
|
|
1301
|
-
isSelected ? "border-
|
|
1278
|
+
isSelected ? "border-slate-300 bg-slate-100 text-slate-800 shadow-[0_0_0_2px_rgba(148,163,184,0.14)]" : "border-slate-200/80 bg-slate-50 text-slate-700"
|
|
1302
1279
|
].join(" ");
|
|
1303
1280
|
}
|
|
1304
1281
|
return [
|
|
@@ -1320,7 +1297,7 @@ var ChatComposerSurfaceRenderer = class {
|
|
|
1320
1297
|
};
|
|
1321
1298
|
this.createTokenIcon = (tokenKind) => {
|
|
1322
1299
|
const wrapper = document.createElement("span");
|
|
1323
|
-
wrapper.className = tokenKind === "file" ? "inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-
|
|
1300
|
+
wrapper.className = tokenKind === "file" ? "inline-flex h-4.5 w-4.5 shrink-0 items-center justify-center rounded-md bg-white text-slate-500 ring-1 ring-black/5" : "inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center text-primary/70";
|
|
1324
1301
|
wrapper.append(tokenKind === "file" ? this.createFileIcon() : this.createSkillIcon());
|
|
1325
1302
|
return wrapper;
|
|
1326
1303
|
};
|
|
@@ -1339,10 +1316,6 @@ var ChatComposerSurfaceRenderer = class {
|
|
|
1339
1316
|
{ tag: "path", attrs: { d: "M9.75 6.25h.01" } }
|
|
1340
1317
|
]);
|
|
1341
1318
|
};
|
|
1342
|
-
this.resolveFileBadgeLabel = (label) => {
|
|
1343
|
-
const match = /\.([a-z0-9]+)$/i.exec(label.trim());
|
|
1344
|
-
return match?.[1]?.slice(0, 4).toUpperCase() || "IMG";
|
|
1345
|
-
};
|
|
1346
1319
|
this.createSvgIcon = (children) => {
|
|
1347
1320
|
const svg = document.createElementNS(SVG_NAMESPACE, "svg");
|
|
1348
1321
|
svg.setAttribute("viewBox", "0 0 16 16");
|
|
@@ -1709,8 +1682,21 @@ var ChatComposerRuntime = class {
|
|
|
1709
1682
|
if (!this.rootElement) {
|
|
1710
1683
|
return;
|
|
1711
1684
|
}
|
|
1685
|
+
const targetSelection = this.selection;
|
|
1712
1686
|
this.rootElement.focus();
|
|
1713
|
-
|
|
1687
|
+
const restoreSelection2 = () => {
|
|
1688
|
+
if (!this.rootElement) {
|
|
1689
|
+
return;
|
|
1690
|
+
}
|
|
1691
|
+
this.selection = targetSelection;
|
|
1692
|
+
this.selectedRange = targetSelection;
|
|
1693
|
+
this.viewController.restoreSelectionIfFocused(this.rootElement, targetSelection);
|
|
1694
|
+
};
|
|
1695
|
+
if (typeof requestAnimationFrame === "function") {
|
|
1696
|
+
requestAnimationFrame(restoreSelection2);
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
restoreSelection2();
|
|
1714
1700
|
};
|
|
1715
1701
|
this.requireConfig = () => {
|
|
1716
1702
|
if (!this.config) {
|
|
@@ -2204,10 +2190,30 @@ function ChatMessageMarkdown(props) {
|
|
|
2204
2190
|
import { jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2205
2191
|
function ChatMessageFile({ file }) {
|
|
2206
2192
|
if (file.isImage && file.dataUrl) {
|
|
2207
|
-
return /* @__PURE__ */
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2193
|
+
return /* @__PURE__ */ jsx15(
|
|
2194
|
+
"img",
|
|
2195
|
+
{
|
|
2196
|
+
src: file.dataUrl,
|
|
2197
|
+
alt: file.label,
|
|
2198
|
+
className: "block max-h-80 max-w-full rounded-2xl object-contain"
|
|
2199
|
+
}
|
|
2200
|
+
);
|
|
2201
|
+
}
|
|
2202
|
+
if (file.dataUrl) {
|
|
2203
|
+
return /* @__PURE__ */ jsxs8(
|
|
2204
|
+
"a",
|
|
2205
|
+
{
|
|
2206
|
+
href: file.dataUrl,
|
|
2207
|
+
download: file.label,
|
|
2208
|
+
target: "_blank",
|
|
2209
|
+
rel: "noreferrer",
|
|
2210
|
+
className: "block rounded-2xl border border-black/8 bg-black/6 px-3 py-2 text-sm transition hover:bg-black/8",
|
|
2211
|
+
children: [
|
|
2212
|
+
/* @__PURE__ */ jsx15("div", { className: "font-medium", children: file.label }),
|
|
2213
|
+
/* @__PURE__ */ jsx15("div", { className: "text-xs opacity-75", children: file.mimeType })
|
|
2214
|
+
]
|
|
2215
|
+
}
|
|
2216
|
+
);
|
|
2211
2217
|
}
|
|
2212
2218
|
return /* @__PURE__ */ jsxs8("div", { className: "rounded-2xl border border-black/8 bg-black/6 px-3 py-2 text-sm", children: [
|
|
2213
2219
|
/* @__PURE__ */ jsx15("div", { className: "font-medium", children: file.label }),
|
|
@@ -2234,9 +2240,24 @@ function ChatReasoningBlock(props) {
|
|
|
2234
2240
|
}
|
|
2235
2241
|
|
|
2236
2242
|
// src/components/chat/ui/chat-message-list/chat-tool-card.tsx
|
|
2237
|
-
import { Clock3, FileSearch, Globe, Search as Search2, SendHorizontal, Terminal, Wrench as Wrench2 } from "lucide-react";
|
|
2243
|
+
import { AlertCircle, CheckCircle2, CircleSlash, Clock3, FileSearch, Globe, Loader2, Search as Search2, SendHorizontal, Terminal, Wrench as Wrench2 } from "lucide-react";
|
|
2238
2244
|
import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2239
2245
|
var TOOL_OUTPUT_PREVIEW_MAX = 220;
|
|
2246
|
+
var TOOL_CALL_ID_PREVIEW_MAX = 18;
|
|
2247
|
+
var STATUS_STYLES = {
|
|
2248
|
+
running: {
|
|
2249
|
+
text: "text-amber-700/80"
|
|
2250
|
+
},
|
|
2251
|
+
success: {
|
|
2252
|
+
text: "text-amber-700/80"
|
|
2253
|
+
},
|
|
2254
|
+
error: {
|
|
2255
|
+
text: "text-amber-700/80"
|
|
2256
|
+
},
|
|
2257
|
+
cancelled: {
|
|
2258
|
+
text: "text-amber-700/80"
|
|
2259
|
+
}
|
|
2260
|
+
};
|
|
2240
2261
|
function renderToolIcon(toolName) {
|
|
2241
2262
|
const lowered = toolName.toLowerCase();
|
|
2242
2263
|
if (lowered.includes("exec") || lowered.includes("shell") || lowered.includes("command")) {
|
|
@@ -2259,18 +2280,50 @@ function renderToolIcon(toolName) {
|
|
|
2259
2280
|
}
|
|
2260
2281
|
return /* @__PURE__ */ jsx17(Wrench2, { className: "h-3.5 w-3.5" });
|
|
2261
2282
|
}
|
|
2283
|
+
function truncateMiddle(value, maxLength = TOOL_CALL_ID_PREVIEW_MAX) {
|
|
2284
|
+
if (value.length <= maxLength) {
|
|
2285
|
+
return value;
|
|
2286
|
+
}
|
|
2287
|
+
const head = Math.ceil((maxLength - 1) / 2);
|
|
2288
|
+
const tail = Math.floor((maxLength - 1) / 2);
|
|
2289
|
+
return `${value.slice(0, head)}\u2026${value.slice(value.length - tail)}`;
|
|
2290
|
+
}
|
|
2291
|
+
function renderStatusMeta(card) {
|
|
2292
|
+
const style = STATUS_STYLES[card.statusTone];
|
|
2293
|
+
if (card.statusTone === "running") {
|
|
2294
|
+
return /* @__PURE__ */ jsxs10("span", { className: cn("inline-flex items-center gap-1 text-[11px] font-medium leading-none", style.text), children: [
|
|
2295
|
+
/* @__PURE__ */ jsx17(Loader2, { className: "h-3.5 w-3.5 animate-spin" }),
|
|
2296
|
+
card.statusLabel
|
|
2297
|
+
] });
|
|
2298
|
+
}
|
|
2299
|
+
const icon = card.statusTone === "success" ? /* @__PURE__ */ jsx17(CheckCircle2, { className: "h-3.5 w-3.5" }) : card.statusTone === "error" ? /* @__PURE__ */ jsx17(AlertCircle, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx17(CircleSlash, { className: "h-3.5 w-3.5" });
|
|
2300
|
+
return /* @__PURE__ */ jsxs10("span", { className: cn("inline-flex items-center gap-1 text-[11px] font-medium leading-none", style.text), children: [
|
|
2301
|
+
icon,
|
|
2302
|
+
card.statusTone === "success" ? null : card.statusLabel
|
|
2303
|
+
] });
|
|
2304
|
+
}
|
|
2262
2305
|
function ChatToolCard({ card }) {
|
|
2263
2306
|
const output = card.output?.trim() ?? "";
|
|
2264
2307
|
const showDetails = output.length > TOOL_OUTPUT_PREVIEW_MAX || output.includes("\n");
|
|
2265
2308
|
const preview = showDetails ? `${output.slice(0, TOOL_OUTPUT_PREVIEW_MAX)}...` : output;
|
|
2266
2309
|
const showOutputSection = card.kind === "result" || card.hasResult;
|
|
2310
|
+
const statusStyle = STATUS_STYLES[card.statusTone];
|
|
2267
2311
|
return /* @__PURE__ */ jsxs10("div", { className: "rounded-xl border border-amber-200/80 bg-amber-50/60 px-3 py-2.5", children: [
|
|
2268
2312
|
/* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap items-center gap-2 text-xs font-semibold text-amber-800", children: [
|
|
2269
2313
|
renderToolIcon(card.toolName),
|
|
2270
2314
|
/* @__PURE__ */ jsx17("span", { children: card.titleLabel }),
|
|
2271
|
-
/* @__PURE__ */ jsx17("span", { className: "font-mono text-[11px] text-amber-900/80", children: card.toolName })
|
|
2315
|
+
/* @__PURE__ */ jsx17("span", { className: "font-mono text-[11px] text-amber-900/80", children: card.toolName }),
|
|
2316
|
+
renderStatusMeta(card)
|
|
2272
2317
|
] }),
|
|
2273
|
-
card.summary ? /* @__PURE__ */
|
|
2318
|
+
card.summary ? /* @__PURE__ */ jsxs10("div", { className: "mt-1", children: [
|
|
2319
|
+
/* @__PURE__ */ jsx17("div", { className: "text-[10px] text-amber-700/75", children: card.inputLabel }),
|
|
2320
|
+
/* @__PURE__ */ jsx17("div", { className: "break-words font-mono text-[11px] text-amber-800/90", children: card.summary })
|
|
2321
|
+
] }) : null,
|
|
2322
|
+
card.callId ? /* @__PURE__ */ jsxs10("div", { className: cn("mt-1 text-[10px]", statusStyle.text), children: [
|
|
2323
|
+
/* @__PURE__ */ jsx17("span", { children: card.callIdLabel }),
|
|
2324
|
+
/* @__PURE__ */ jsx17("span", { children: ": " }),
|
|
2325
|
+
/* @__PURE__ */ jsx17("span", { className: "font-mono", children: truncateMiddle(card.callId) })
|
|
2326
|
+
] }) : null,
|
|
2274
2327
|
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
2328
|
/* @__PURE__ */ jsx17("summary", { className: "cursor-pointer text-[11px] text-amber-700", children: card.outputLabel }),
|
|
2276
2329
|
/* @__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 })
|