@slipstream-ui/ai 0.3.0 → 0.5.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/index.d.mts +33 -1
- package/dist/index.d.ts +33 -1
- package/dist/index.js +236 -110
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +238 -112
- package/dist/index.mjs.map +1 -1
- package/dist/manifests/attachment.manifest.json +2 -0
- package/dist/manifests/branch.manifest.json +2 -0
- package/dist/manifests/code-block.manifest.json +2 -0
- package/dist/manifests/command-palette.manifest.json +37 -3
- package/dist/manifests/confirmation.manifest.json +2 -0
- package/dist/manifests/inline-citation.manifest.json +2 -0
- package/dist/manifests/message-actions.manifest.json +3 -0
- package/dist/manifests/reasoning.manifest.json +6 -0
- package/dist/manifests/response.manifest.json +2 -0
- package/dist/manifests/tool-call.manifest.json +6 -0
- package/package.json +3 -3
- package/src/components/code-block/CodeBlock.tsx +91 -5
- package/src/components/command-palette/CommandPalette.test.tsx +21 -0
- package/src/components/command-palette/CommandPalette.tsx +71 -6
- package/src/components/confirmation/Confirmation.tsx +1 -1
- package/src/components/conversation/Conversation.tsx +6 -1
- package/src/components/reasoning/Reasoning.tsx +13 -6
- package/src/components/tool-call/ToolCall.tsx +24 -8
- package/src/manifests/attachment.manifest.json +2 -0
- package/src/manifests/branch.manifest.json +2 -0
- package/src/manifests/code-block.manifest.json +2 -0
- package/src/manifests/command-palette.manifest.json +37 -3
- package/src/manifests/confirmation.manifest.json +2 -0
- package/src/manifests/inline-citation.manifest.json +2 -0
- package/src/manifests/message-actions.manifest.json +3 -0
- package/src/manifests/reasoning.manifest.json +6 -0
- package/src/manifests/response.manifest.json +2 -0
- package/src/manifests/tool-call.manifest.json +6 -0
package/dist/index.mjs
CHANGED
|
@@ -292,7 +292,7 @@ import { cn as cn6 } from "@slipstream-ui/core";
|
|
|
292
292
|
|
|
293
293
|
// src/components/code-block/CodeBlock.tsx
|
|
294
294
|
import * as React5 from "react";
|
|
295
|
-
import { Highlight
|
|
295
|
+
import { Highlight } from "prism-react-renderer";
|
|
296
296
|
import { cn as cn5 } from "@slipstream-ui/core";
|
|
297
297
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
298
298
|
function useIsDark() {
|
|
@@ -306,11 +306,80 @@ function useIsDark() {
|
|
|
306
306
|
}, []);
|
|
307
307
|
return dark;
|
|
308
308
|
}
|
|
309
|
+
var LIGHT_CODE_THEME = {
|
|
310
|
+
plain: { color: "#1f2328", backgroundColor: "transparent" },
|
|
311
|
+
styles: [
|
|
312
|
+
{ types: ["comment", "prolog", "doctype", "cdata"], style: { color: "#57606a" } },
|
|
313
|
+
{ types: ["punctuation", "operator"], style: { color: "#24292f" } },
|
|
314
|
+
{ types: ["namespace"], style: { color: "#6f42c1" } },
|
|
315
|
+
{ types: ["tag", "deleted", "keyword", "atrule", "important"], style: { color: "#b31d28" } },
|
|
316
|
+
{ types: ["attr-name", "variable"], style: { color: "#953800" } },
|
|
317
|
+
{ types: ["string", "char", "attr-value", "inserted", "regex"], style: { color: "#0a5c2f" } },
|
|
318
|
+
{
|
|
319
|
+
types: [
|
|
320
|
+
"function",
|
|
321
|
+
"class-name",
|
|
322
|
+
"number",
|
|
323
|
+
"boolean",
|
|
324
|
+
"constant",
|
|
325
|
+
"symbol",
|
|
326
|
+
"property",
|
|
327
|
+
"selector",
|
|
328
|
+
"builtin",
|
|
329
|
+
"entity",
|
|
330
|
+
"url"
|
|
331
|
+
],
|
|
332
|
+
style: { color: "#0550ae" }
|
|
333
|
+
}
|
|
334
|
+
]
|
|
335
|
+
};
|
|
336
|
+
var DARK_CODE_THEME = {
|
|
337
|
+
plain: { color: "#e6edf3", backgroundColor: "transparent" },
|
|
338
|
+
styles: [
|
|
339
|
+
{ types: ["comment", "prolog", "doctype", "cdata"], style: { color: "#8b949e" } },
|
|
340
|
+
{ types: ["punctuation", "operator"], style: { color: "#c9d1d9" } },
|
|
341
|
+
{ types: ["namespace"], style: { color: "#d2a8ff" } },
|
|
342
|
+
{ types: ["tag", "deleted", "keyword", "atrule", "important"], style: { color: "#ff7b72" } },
|
|
343
|
+
{ types: ["attr-name", "variable"], style: { color: "#ffa657" } },
|
|
344
|
+
{ types: ["string", "char", "attr-value", "inserted", "regex"], style: { color: "#7ee787" } },
|
|
345
|
+
{
|
|
346
|
+
types: [
|
|
347
|
+
"function",
|
|
348
|
+
"class-name",
|
|
349
|
+
"number",
|
|
350
|
+
"boolean",
|
|
351
|
+
"constant",
|
|
352
|
+
"symbol",
|
|
353
|
+
"property",
|
|
354
|
+
"selector",
|
|
355
|
+
"builtin",
|
|
356
|
+
"entity",
|
|
357
|
+
"url"
|
|
358
|
+
],
|
|
359
|
+
style: { color: "#79c0ff" }
|
|
360
|
+
}
|
|
361
|
+
]
|
|
362
|
+
};
|
|
309
363
|
var CodeBlock = React5.forwardRef(
|
|
310
364
|
({ code, language = "tsx", showCopyButton = true, className, ...props }, ref) => {
|
|
311
365
|
const [copied, setCopied] = React5.useState(false);
|
|
312
366
|
const isDark = useIsDark();
|
|
313
367
|
const trimmedCode = code.replace(/\n$/, "");
|
|
368
|
+
const preRef = React5.useRef(null);
|
|
369
|
+
const [scrollable, setScrollable] = React5.useState(false);
|
|
370
|
+
React5.useEffect(() => {
|
|
371
|
+
const element = preRef.current;
|
|
372
|
+
if (!element) return;
|
|
373
|
+
const update = () => setScrollable(element.scrollWidth > element.clientWidth + 1);
|
|
374
|
+
update();
|
|
375
|
+
if (typeof ResizeObserver === "undefined") {
|
|
376
|
+
window.addEventListener("resize", update);
|
|
377
|
+
return () => window.removeEventListener("resize", update);
|
|
378
|
+
}
|
|
379
|
+
const observer = new ResizeObserver(update);
|
|
380
|
+
observer.observe(element);
|
|
381
|
+
return () => observer.disconnect();
|
|
382
|
+
}, [trimmedCode, language]);
|
|
314
383
|
const handleCopy = async () => {
|
|
315
384
|
try {
|
|
316
385
|
await navigator.clipboard.writeText(trimmedCode);
|
|
@@ -324,8 +393,8 @@ var CodeBlock = React5.forwardRef(
|
|
|
324
393
|
{
|
|
325
394
|
ref,
|
|
326
395
|
className: cn5(
|
|
327
|
-
// bg-background (not muted):
|
|
328
|
-
// surface
|
|
396
|
+
// bg-background (not muted): the light syntax palette is tuned for
|
|
397
|
+
// a white surface; on gray the token contrast drops below AA.
|
|
329
398
|
"relative group rounded-md border border-border bg-background overflow-hidden",
|
|
330
399
|
className
|
|
331
400
|
),
|
|
@@ -344,10 +413,13 @@ var CodeBlock = React5.forwardRef(
|
|
|
344
413
|
}
|
|
345
414
|
)
|
|
346
415
|
] }),
|
|
347
|
-
/* @__PURE__ */ jsx5(Highlight, { theme: isDark ?
|
|
416
|
+
/* @__PURE__ */ jsx5(Highlight, { theme: isDark ? DARK_CODE_THEME : LIGHT_CODE_THEME, code: trimmedCode, language, children: ({ style, tokens, getLineProps, getTokenProps }) => /* @__PURE__ */ jsx5(
|
|
348
417
|
"pre",
|
|
349
418
|
{
|
|
350
|
-
|
|
419
|
+
ref: preRef,
|
|
420
|
+
tabIndex: scrollable ? 0 : void 0,
|
|
421
|
+
"aria-label": scrollable ? `${language} code, scrollable` : void 0,
|
|
422
|
+
className: "overflow-x-auto p-4 text-xs leading-relaxed m-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
351
423
|
style: { ...style, background: "transparent" },
|
|
352
424
|
children: tokens.map((line, i) => /* @__PURE__ */ jsx5("div", { ...getLineProps({ line }), children: line.map((token, key) => /* @__PURE__ */ jsx5("span", { ...getTokenProps({ token }) }, key)) }, i))
|
|
353
425
|
}
|
|
@@ -444,7 +516,7 @@ import { Button as Button2 } from "@slipstream-ui/core";
|
|
|
444
516
|
import { cn as cn7 } from "@slipstream-ui/core";
|
|
445
517
|
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
446
518
|
var statusChipClasses = {
|
|
447
|
-
pending: "bg-warning/10 text-warning border-warning/20",
|
|
519
|
+
pending: "bg-warning/10 text-warning-ink border-warning/20",
|
|
448
520
|
approved: "bg-success/10 text-success border-success/20",
|
|
449
521
|
rejected: "bg-destructive/10 text-destructive border-destructive/20"
|
|
450
522
|
};
|
|
@@ -744,62 +816,71 @@ var Conversation = React10.forwardRef(
|
|
|
744
816
|
};
|
|
745
817
|
const isEmpty = messages.length === 0 && !isLoading;
|
|
746
818
|
return /* @__PURE__ */ jsxs8("div", { ref, className: cn10("relative h-full w-full", className), ...props, children: [
|
|
747
|
-
/* @__PURE__ */ jsxs8(
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
children:
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
819
|
+
/* @__PURE__ */ jsxs8(
|
|
820
|
+
"div",
|
|
821
|
+
{
|
|
822
|
+
ref: scrollRef,
|
|
823
|
+
tabIndex: 0,
|
|
824
|
+
"aria-label": "Conversation transcript",
|
|
825
|
+
className: "flex flex-col gap-4 p-4 overflow-y-auto h-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
826
|
+
children: [
|
|
827
|
+
(hasMoreHistory || isLoadingMore) && /* @__PURE__ */ jsx10(
|
|
828
|
+
"button",
|
|
829
|
+
{
|
|
830
|
+
type: "button",
|
|
831
|
+
onClick: handleLoadMore,
|
|
832
|
+
disabled: isLoadingMore,
|
|
833
|
+
className: "mx-auto rounded-full border border-border bg-background px-3 py-1 text-xs text-muted-foreground hover:text-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50",
|
|
834
|
+
children: isLoadingMore ? "Loading\u2026" : "Load earlier messages"
|
|
835
|
+
}
|
|
836
|
+
),
|
|
837
|
+
isEmpty ? /* @__PURE__ */ jsxs8("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 text-center py-16", children: [
|
|
838
|
+
/* @__PURE__ */ jsx10("h3", { className: "text-lg font-semibold", children: welcomeTitle }),
|
|
839
|
+
/* @__PURE__ */ jsx10("p", { className: "text-sm text-muted-foreground max-w-sm", children: welcomeDescription }),
|
|
840
|
+
welcomeSuggestions.length > 0 && /* @__PURE__ */ jsx10("div", { className: "mt-2 flex flex-col gap-2 w-full max-w-sm", children: welcomeSuggestions.map((suggestion) => /* @__PURE__ */ jsx10(
|
|
841
|
+
"button",
|
|
842
|
+
{
|
|
843
|
+
type: "button",
|
|
844
|
+
onClick: () => onWelcomeSelect?.(suggestion),
|
|
845
|
+
className: "rounded-md border border-border bg-background px-4 py-2.5 text-sm text-left text-muted-foreground hover:bg-accent hover:text-accent-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
846
|
+
children: suggestion
|
|
847
|
+
},
|
|
848
|
+
suggestion
|
|
849
|
+
)) })
|
|
850
|
+
] }) : messages.map((message) => /* @__PURE__ */ jsx10(
|
|
851
|
+
MemoizedMessage,
|
|
852
|
+
{
|
|
853
|
+
message,
|
|
854
|
+
showAvatar: showAvatars,
|
|
855
|
+
avatarSrc: message.role === "user" ? userAvatarSrc : assistantAvatarSrc,
|
|
856
|
+
avatarFallback: message.role === "user" ? userAvatarFallback : assistantAvatarFallback,
|
|
857
|
+
renderContent,
|
|
858
|
+
isStreaming: message.id === streamingMessageId,
|
|
859
|
+
error: messageErrors?.[message.id],
|
|
860
|
+
onRetry: onRetryMessage && messageErrors?.[message.id] ? () => onRetryMessage(message) : void 0
|
|
861
|
+
},
|
|
862
|
+
message.id
|
|
863
|
+
)),
|
|
864
|
+
isLoading && !isEmpty && /* @__PURE__ */ jsx10(
|
|
865
|
+
MemoizedMessage,
|
|
866
|
+
{
|
|
867
|
+
message: {
|
|
868
|
+
id: "__loading__",
|
|
869
|
+
role: "assistant",
|
|
870
|
+
content: loadingMessage
|
|
871
|
+
},
|
|
872
|
+
showAvatar: showAvatars,
|
|
873
|
+
avatarSrc: assistantAvatarSrc,
|
|
874
|
+
avatarFallback: assistantAvatarFallback,
|
|
875
|
+
className: "bg-muted/50",
|
|
876
|
+
renderContent: (content) => /* @__PURE__ */ jsx10("span", { className: "text-muted-foreground animate-pulse motion-reduce:animate-none", children: content })
|
|
877
|
+
}
|
|
878
|
+
),
|
|
879
|
+
/* @__PURE__ */ jsx10("div", { "aria-live": "polite", className: "sr-only", children: announcement }),
|
|
880
|
+
/* @__PURE__ */ jsx10("div", { ref: messagesEndRef })
|
|
881
|
+
]
|
|
882
|
+
}
|
|
883
|
+
),
|
|
803
884
|
showDownload && messages.length > 0 && /* @__PURE__ */ jsx10(
|
|
804
885
|
"button",
|
|
805
886
|
{
|
|
@@ -1210,17 +1291,17 @@ import * as React12 from "react";
|
|
|
1210
1291
|
import { cn as cn12 } from "@slipstream-ui/core";
|
|
1211
1292
|
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1212
1293
|
var Reasoning = React12.forwardRef(
|
|
1213
|
-
({ steps, className, collapsible = true, defaultExpanded = false, duration, isStreaming = false, ...props }, ref) => {
|
|
1294
|
+
({ steps, className, collapsible = true, defaultExpanded = false, duration, isStreaming = false, labels, ...props }, ref) => {
|
|
1214
1295
|
const [isExpanded, setIsExpanded] = React12.useState(defaultExpanded);
|
|
1215
1296
|
const contentId = React12.useId();
|
|
1216
1297
|
if (steps.length === 0) {
|
|
1217
1298
|
return null;
|
|
1218
1299
|
}
|
|
1219
1300
|
const headerText = [
|
|
1220
|
-
"Reasoning",
|
|
1221
|
-
`(${steps.length} ${steps.length === 1 ? "step" : "steps"})`,
|
|
1301
|
+
labels?.title ?? "Reasoning",
|
|
1302
|
+
labels?.count ? labels.count(steps.length) : `(${steps.length} ${steps.length === 1 ? "step" : "steps"})`,
|
|
1222
1303
|
typeof duration === "number" ? `\xB7 ${duration}s` : duration ? `\xB7 ${duration}` : "",
|
|
1223
|
-
isStreaming ?
|
|
1304
|
+
isStreaming ? `\xB7 ${labels?.thinking ?? "thinking\u2026"}` : ""
|
|
1224
1305
|
].filter(Boolean).join(" ");
|
|
1225
1306
|
return /* @__PURE__ */ jsxs10("div", { ref, className: cn12("border-l-2 border-muted-foreground/30 pl-4", className), ...props, children: [
|
|
1226
1307
|
collapsible ? /* @__PURE__ */ jsxs10(
|
|
@@ -1231,7 +1312,7 @@ var Reasoning = React12.forwardRef(
|
|
|
1231
1312
|
"aria-expanded": isExpanded,
|
|
1232
1313
|
"aria-controls": contentId,
|
|
1233
1314
|
children: [
|
|
1234
|
-
/* @__PURE__ */ jsx12("span", { className: cn12("transition-transform", isExpanded ? "rotate-90" : ""), children: "
|
|
1315
|
+
/* @__PURE__ */ jsx12("span", { className: cn12("transition-transform", isExpanded ? "rotate-90" : ""), children: "?" }),
|
|
1235
1316
|
/* @__PURE__ */ jsx12("span", { children: headerText }),
|
|
1236
1317
|
isStreaming && /* @__PURE__ */ jsx12("span", { className: "flex gap-0.5", "aria-hidden": "true", children: [0, 150, 300].map((delay) => /* @__PURE__ */ jsx12(
|
|
1237
1318
|
"span",
|
|
@@ -1247,10 +1328,7 @@ var Reasoning = React12.forwardRef(
|
|
|
1247
1328
|
(isExpanded || !collapsible) && /* @__PURE__ */ jsx12("div", { id: contentId, className: "mt-3 space-y-3", children: steps.map((step, index) => /* @__PURE__ */ jsxs10("div", { className: "flex gap-3", children: [
|
|
1248
1329
|
/* @__PURE__ */ jsx12("div", { className: "w-3 h-3 rounded-full bg-primary/20 border-2 border-primary flex-shrink-0 mt-1" }),
|
|
1249
1330
|
/* @__PURE__ */ jsxs10("div", { className: "text-sm flex-1", children: [
|
|
1250
|
-
/* @__PURE__ */
|
|
1251
|
-
"Step ",
|
|
1252
|
-
index + 1
|
|
1253
|
-
] }),
|
|
1331
|
+
/* @__PURE__ */ jsx12("div", { className: "font-medium text-foreground mb-1", children: labels?.step?.(index + 1) ?? `Step ${index + 1}` }),
|
|
1254
1332
|
/* @__PURE__ */ jsx12("div", { className: "text-muted-foreground", children: step.content }),
|
|
1255
1333
|
step.timestamp && /* @__PURE__ */ jsx12("div", { className: "text-xs text-muted-foreground mt-1", children: new Date(step.timestamp).toLocaleTimeString() })
|
|
1256
1334
|
] })
|
|
@@ -1265,19 +1343,19 @@ import * as React13 from "react";
|
|
|
1265
1343
|
import { cn as cn13 } from "@slipstream-ui/core";
|
|
1266
1344
|
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1267
1345
|
var statusChipClasses2 = {
|
|
1268
|
-
pending: "bg-warning/10 text-warning border-warning/20",
|
|
1269
|
-
running: "bg-info/10 text-info border-info/20",
|
|
1346
|
+
pending: "bg-warning/10 text-warning-ink border-warning/20",
|
|
1347
|
+
running: "bg-info/10 text-info-ink border-info/20",
|
|
1270
1348
|
completed: "bg-success/10 text-success border-success/20",
|
|
1271
1349
|
failed: "bg-destructive/10 text-destructive border-destructive/20"
|
|
1272
1350
|
};
|
|
1273
|
-
var
|
|
1351
|
+
var defaultStatusLabels = {
|
|
1274
1352
|
pending: "Pending",
|
|
1275
1353
|
running: "Running",
|
|
1276
1354
|
completed: "Completed",
|
|
1277
1355
|
failed: "Failed"
|
|
1278
1356
|
};
|
|
1279
1357
|
var ToolCall = React13.forwardRef(
|
|
1280
|
-
({ toolCall, className, showResult = true, ...props }, ref) => {
|
|
1358
|
+
({ toolCall, className, showResult = true, labels, ...props }, ref) => {
|
|
1281
1359
|
const serializedArguments = React13.useMemo(
|
|
1282
1360
|
() => JSON.stringify(toolCall.arguments, null, 2),
|
|
1283
1361
|
[toolCall.arguments]
|
|
@@ -1286,6 +1364,12 @@ var ToolCall = React13.forwardRef(
|
|
|
1286
1364
|
if (toolCall.result === void 0) return void 0;
|
|
1287
1365
|
return typeof toolCall.result === "string" ? toolCall.result : JSON.stringify(toolCall.result, null, 2);
|
|
1288
1366
|
}, [toolCall.result]);
|
|
1367
|
+
const statusLabels = {
|
|
1368
|
+
pending: labels?.pending ?? defaultStatusLabels.pending,
|
|
1369
|
+
running: labels?.running ?? defaultStatusLabels.running,
|
|
1370
|
+
completed: labels?.completed ?? defaultStatusLabels.completed,
|
|
1371
|
+
failed: labels?.failed ?? defaultStatusLabels.failed
|
|
1372
|
+
};
|
|
1289
1373
|
return /* @__PURE__ */ jsxs11(
|
|
1290
1374
|
"div",
|
|
1291
1375
|
{
|
|
@@ -1314,15 +1398,15 @@ var ToolCall = React13.forwardRef(
|
|
|
1314
1398
|
] }),
|
|
1315
1399
|
/* @__PURE__ */ jsxs11("div", { className: "space-y-2", children: [
|
|
1316
1400
|
/* @__PURE__ */ jsxs11("div", { children: [
|
|
1317
|
-
/* @__PURE__ */ jsx13("div", { className: "text-xs font-medium text-muted-foreground mb-1", children: "Arguments" }),
|
|
1401
|
+
/* @__PURE__ */ jsx13("div", { className: "text-xs font-medium text-muted-foreground mb-1", children: labels?.arguments ?? "Arguments" }),
|
|
1318
1402
|
/* @__PURE__ */ jsx13("pre", { className: "text-xs bg-muted p-2 rounded overflow-x-auto", children: /* @__PURE__ */ jsx13("code", { children: serializedArguments }) })
|
|
1319
1403
|
] }),
|
|
1320
1404
|
showResult && serializedResult !== void 0 && /* @__PURE__ */ jsxs11("div", { children: [
|
|
1321
|
-
/* @__PURE__ */ jsx13("div", { className: "text-xs font-medium text-muted-foreground mb-1", children: "Result" }),
|
|
1405
|
+
/* @__PURE__ */ jsx13("div", { className: "text-xs font-medium text-muted-foreground mb-1", children: labels?.result ?? "Result" }),
|
|
1322
1406
|
/* @__PURE__ */ jsx13("pre", { className: "text-xs bg-muted p-2 rounded overflow-x-auto", children: /* @__PURE__ */ jsx13("code", { children: serializedResult }) })
|
|
1323
1407
|
] }),
|
|
1324
1408
|
toolCall.status === "failed" && toolCall.error && /* @__PURE__ */ jsxs11("div", { role: "alert", children: [
|
|
1325
|
-
/* @__PURE__ */ jsx13("div", { className: "text-xs font-medium text-destructive mb-1", children: "Error" }),
|
|
1409
|
+
/* @__PURE__ */ jsx13("div", { className: "text-xs font-medium text-destructive mb-1", children: labels?.error ?? "Error" }),
|
|
1326
1410
|
/* @__PURE__ */ jsx13("pre", { className: "text-xs bg-destructive/10 text-destructive p-2 rounded overflow-x-auto whitespace-pre-wrap", children: /* @__PURE__ */ jsx13("code", { children: toolCall.error }) })
|
|
1327
1411
|
] })
|
|
1328
1412
|
] })
|
|
@@ -1482,27 +1566,52 @@ StreamingIndicator.displayName = "StreamingIndicator";
|
|
|
1482
1566
|
// src/components/command-palette/CommandPalette.tsx
|
|
1483
1567
|
import * as React19 from "react";
|
|
1484
1568
|
import { cn as cn19 } from "@slipstream-ui/core";
|
|
1485
|
-
import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1569
|
+
import { Fragment as Fragment5, jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1486
1570
|
function CommandPalette({
|
|
1487
1571
|
open,
|
|
1488
1572
|
onOpenChange,
|
|
1489
1573
|
commands,
|
|
1490
1574
|
onSelect,
|
|
1491
|
-
|
|
1575
|
+
loadCommands,
|
|
1576
|
+
debounceMs = 200,
|
|
1577
|
+
renderCommand,
|
|
1578
|
+
loadingText = "Searching\u2026",
|
|
1579
|
+
placeholder = "Type a command."
|
|
1492
1580
|
}) {
|
|
1493
1581
|
const [query, setQuery] = React19.useState("");
|
|
1494
1582
|
const [activeIndex, setActiveIndex] = React19.useState(0);
|
|
1583
|
+
const [asyncCommands, setAsyncCommands] = React19.useState([]);
|
|
1584
|
+
const [loading, setLoading] = React19.useState(false);
|
|
1585
|
+
const requestId = React19.useRef(0);
|
|
1586
|
+
const asyncSignature = loadCommands ? String(loadCommands.length) : "";
|
|
1495
1587
|
const inputRef = React19.useRef(null);
|
|
1496
1588
|
const listboxId = React19.useId();
|
|
1497
1589
|
const optionId = (index) => `${listboxId}-option-${index}`;
|
|
1590
|
+
React19.useEffect(() => {
|
|
1591
|
+
if (!open || !loadCommands) return;
|
|
1592
|
+
const id = ++requestId.current;
|
|
1593
|
+
setLoading(true);
|
|
1594
|
+
const timer = setTimeout(async () => {
|
|
1595
|
+
try {
|
|
1596
|
+
const result = await loadCommands(query);
|
|
1597
|
+
if (requestId.current === id) setAsyncCommands(result);
|
|
1598
|
+
} catch {
|
|
1599
|
+
if (requestId.current === id) setAsyncCommands([]);
|
|
1600
|
+
} finally {
|
|
1601
|
+
if (requestId.current === id) setLoading(false);
|
|
1602
|
+
}
|
|
1603
|
+
}, debounceMs);
|
|
1604
|
+
return () => clearTimeout(timer);
|
|
1605
|
+
}, [open, query, debounceMs, asyncSignature]);
|
|
1498
1606
|
const filtered = React19.useMemo(() => {
|
|
1607
|
+
if (loadCommands) return asyncCommands;
|
|
1499
1608
|
const terms = query.trim().toLowerCase().split(/\s+/).filter(Boolean);
|
|
1500
1609
|
if (terms.length === 0) return commands;
|
|
1501
1610
|
return commands.filter((command) => {
|
|
1502
1611
|
const haystack = `${command.label} ${command.section ?? ""} ${(command.keywords ?? []).join(" ")}`.toLowerCase();
|
|
1503
1612
|
return terms.every((term) => haystack.includes(term));
|
|
1504
1613
|
});
|
|
1505
|
-
}, [commands, query]);
|
|
1614
|
+
}, [commands, query, asyncCommands, loadCommands]);
|
|
1506
1615
|
React19.useEffect(() => {
|
|
1507
1616
|
if (open) {
|
|
1508
1617
|
setQuery("");
|
|
@@ -1565,37 +1674,54 @@ function CommandPalette({
|
|
|
1565
1674
|
className: "w-full border-b border-border bg-transparent px-4 py-3 text-sm outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring placeholder:text-muted-foreground"
|
|
1566
1675
|
}
|
|
1567
1676
|
),
|
|
1568
|
-
/* @__PURE__ */ jsxs16(
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1677
|
+
/* @__PURE__ */ jsxs16(
|
|
1678
|
+
"ul",
|
|
1679
|
+
{
|
|
1680
|
+
id: listboxId,
|
|
1681
|
+
role: "listbox",
|
|
1682
|
+
"aria-label": "Commands",
|
|
1683
|
+
"aria-busy": loading || void 0,
|
|
1684
|
+
className: "max-h-72 overflow-y-auto py-1",
|
|
1685
|
+
children: [
|
|
1686
|
+
loading && filtered.length === 0 && /* @__PURE__ */ jsx19("li", { role: "presentation", className: "px-4 py-3 text-sm text-muted-foreground", children: loadingText }),
|
|
1687
|
+
!loading && filtered.length === 0 && /* @__PURE__ */ jsx19("li", { role: "presentation", className: "px-4 py-3 text-sm text-muted-foreground", children: "No matching commands." }),
|
|
1688
|
+
filtered.map((command, index) => {
|
|
1689
|
+
const showSection = command.section && command.section !== lastSection;
|
|
1690
|
+
lastSection = command.section ?? lastSection;
|
|
1691
|
+
return /* @__PURE__ */ jsxs16(React19.Fragment, { children: [
|
|
1692
|
+
showSection && /* @__PURE__ */ jsx19("li", { role: "presentation", className: "px-4 pb-1 pt-3 text-xs font-medium uppercase text-muted-foreground", children: command.section }),
|
|
1693
|
+
/* @__PURE__ */ jsx19(
|
|
1694
|
+
"li",
|
|
1695
|
+
{
|
|
1696
|
+
id: optionId(index),
|
|
1697
|
+
role: "option",
|
|
1698
|
+
"aria-selected": index === activeIndex,
|
|
1699
|
+
onMouseEnter: () => setActiveIndex(index),
|
|
1700
|
+
onClick: () => {
|
|
1701
|
+
onSelect(command);
|
|
1702
|
+
onOpenChange(false);
|
|
1703
|
+
},
|
|
1704
|
+
className: cn19(
|
|
1705
|
+
"flex cursor-pointer items-center justify-between px-4 py-2 text-sm",
|
|
1706
|
+
index === activeIndex ? "bg-primary/10 text-foreground" : "text-muted-foreground"
|
|
1707
|
+
),
|
|
1708
|
+
children: renderCommand ? renderCommand(command, { active: index === activeIndex }) : /* @__PURE__ */ jsxs16(Fragment5, { children: [
|
|
1709
|
+
/* @__PURE__ */ jsxs16("span", { className: "flex min-w-0 items-center gap-2", children: [
|
|
1710
|
+
command.icon && /* @__PURE__ */ jsx19("span", { className: "shrink-0 [&_svg]:size-4", children: command.icon }),
|
|
1711
|
+
/* @__PURE__ */ jsxs16("span", { className: "min-w-0", children: [
|
|
1712
|
+
/* @__PURE__ */ jsx19("span", { className: "block truncate", children: command.label }),
|
|
1713
|
+
command.description && /* @__PURE__ */ jsx19("span", { className: "block text-xs text-muted-foreground", children: command.description })
|
|
1714
|
+
] })
|
|
1715
|
+
] }),
|
|
1716
|
+
command.shortcut && /* @__PURE__ */ jsx19("span", { className: "font-mono text-xs", children: command.shortcut })
|
|
1717
|
+
] })
|
|
1718
|
+
}
|
|
1719
|
+
)
|
|
1720
|
+
] }, command.id);
|
|
1721
|
+
})
|
|
1722
|
+
]
|
|
1723
|
+
}
|
|
1724
|
+
)
|
|
1599
1725
|
] })
|
|
1600
1726
|
}
|
|
1601
1727
|
);
|