@ash-cloud/ash-ui 0.1.0 → 0.2.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.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
  var react = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
 
6
- // src/components/ToolCallCard.tsx
6
+ // src/components/Conversation.tsx
7
7
 
8
8
  // src/utils.ts
9
9
  function formatToolName(name) {
@@ -362,39 +362,6 @@ function truncate(str, maxLength) {
362
362
  function cn(...classes) {
363
363
  return classes.filter(Boolean).join(" ");
364
364
  }
365
- function groupEntriesForCompactMode(entries, config) {
366
- const result = [];
367
- let currentToolGroup = [];
368
- let toolGroupCounter = 0;
369
- const flushToolGroup = () => {
370
- if (currentToolGroup.length > 0) {
371
- result.push({
372
- type: "tool_group",
373
- entries: [...currentToolGroup],
374
- id: `tool-group-${toolGroupCounter++}`
375
- });
376
- currentToolGroup = [];
377
- }
378
- };
379
- for (const entry of entries) {
380
- if (entry.entryType.type === "tool_call") {
381
- currentToolGroup.push(entry);
382
- if (config.breakEveryNToolCalls && config.breakEveryNToolCalls > 0 && currentToolGroup.length >= config.breakEveryNToolCalls) {
383
- flushToolGroup();
384
- }
385
- } else {
386
- flushToolGroup();
387
- result.push({ type: "single", entry });
388
- }
389
- }
390
- flushToolGroup();
391
- return result;
392
- }
393
- function extractToolCallsFromGroup(entries) {
394
- return entries.filter(
395
- (e) => e.entryType.type === "tool_call"
396
- ).map((e) => e.entryType.toolCall);
397
- }
398
365
  function parseOptionsFromContent(content) {
399
366
  const optionPattern = /(?:\*\*)?Option\s+(\d+)(?:\*\*)?[:\-]\s*([^\n]+)(?:\n((?:(?!\n(?:\*\*)?Option\s+\d).)*?))?/gi;
400
367
  const options = [];
@@ -444,6 +411,376 @@ function parseOptionsFromContent(content) {
444
411
  }
445
412
  return null;
446
413
  }
414
+ var ConversationContext = react.createContext(null);
415
+ function useConversation() {
416
+ const context = react.useContext(ConversationContext);
417
+ if (!context) {
418
+ throw new Error("useConversation must be used within a Conversation");
419
+ }
420
+ return context;
421
+ }
422
+ function Conversation({
423
+ children,
424
+ className,
425
+ autoScroll: initialAutoScroll = true,
426
+ scrollThreshold = 100
427
+ }) {
428
+ const containerRef = react.useRef(null);
429
+ const [isScrolledUp, setIsScrolledUp] = react.useState(false);
430
+ const [autoScroll, setAutoScroll] = react.useState(initialAutoScroll);
431
+ const scrollToBottom = react.useCallback((behavior = "smooth") => {
432
+ if (containerRef.current) {
433
+ containerRef.current.scrollTo({
434
+ top: containerRef.current.scrollHeight,
435
+ behavior
436
+ });
437
+ }
438
+ }, []);
439
+ const handleScroll = react.useCallback(() => {
440
+ if (!containerRef.current) return;
441
+ const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
442
+ const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
443
+ const isAtBottom = distanceFromBottom < scrollThreshold;
444
+ setIsScrolledUp(!isAtBottom);
445
+ if (isAtBottom && !autoScroll) {
446
+ setAutoScroll(true);
447
+ }
448
+ }, [scrollThreshold, autoScroll]);
449
+ react.useEffect(() => {
450
+ if (autoScroll && !isScrolledUp) {
451
+ scrollToBottom("instant");
452
+ }
453
+ });
454
+ const contextValue = {
455
+ containerRef,
456
+ scrollToBottom,
457
+ isScrolledUp,
458
+ autoScroll,
459
+ setAutoScroll
460
+ };
461
+ return /* @__PURE__ */ jsxRuntime.jsx(ConversationContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
462
+ "div",
463
+ {
464
+ ref: containerRef,
465
+ onScroll: handleScroll,
466
+ className: cn(
467
+ "ash-conversation flex flex-col overflow-y-auto",
468
+ "ash-scrollbar",
469
+ className
470
+ ),
471
+ role: "log",
472
+ "aria-live": "polite",
473
+ children
474
+ }
475
+ ) });
476
+ }
477
+ function ConversationContent({
478
+ children,
479
+ className
480
+ }) {
481
+ return /* @__PURE__ */ jsxRuntime.jsx(
482
+ "div",
483
+ {
484
+ className: cn(
485
+ "ash-conversation-content flex flex-col",
486
+ "gap-[var(--ash-message-list-gap,0.25rem)]",
487
+ "p-[var(--ash-spacing-md,0.75rem)]",
488
+ className
489
+ ),
490
+ children
491
+ }
492
+ );
493
+ }
494
+ function ConversationEmptyState({
495
+ children,
496
+ className
497
+ }) {
498
+ return /* @__PURE__ */ jsxRuntime.jsx(
499
+ "div",
500
+ {
501
+ className: cn(
502
+ "ash-conversation-empty flex flex-col items-center justify-center",
503
+ "flex-1 min-h-[200px] text-center",
504
+ "text-[var(--ash-text-muted,rgba(255,255,255,0.5))]",
505
+ className
506
+ ),
507
+ children
508
+ }
509
+ );
510
+ }
511
+ function ConversationScrollButton({
512
+ className,
513
+ label = "Scroll to bottom"
514
+ }) {
515
+ const { isScrolledUp, scrollToBottom } = useConversation();
516
+ if (!isScrolledUp) return null;
517
+ return /* @__PURE__ */ jsxRuntime.jsx(
518
+ "button",
519
+ {
520
+ onClick: () => scrollToBottom(),
521
+ className: cn(
522
+ "ash-conversation-scroll-btn",
523
+ "fixed bottom-20 left-1/2 -translate-x-1/2",
524
+ "px-4 py-2 rounded-full",
525
+ "bg-[var(--ash-surface-elevated,#111)] border border-[var(--ash-border,rgba(255,255,255,0.08))]",
526
+ "text-[var(--ash-text-secondary,rgba(255,255,255,0.7))] text-sm",
527
+ "hover:bg-[var(--ash-surface-card,#0c0c0c)] hover:border-[var(--ash-border-emphasis,rgba(255,255,255,0.15))]",
528
+ "transition-all duration-200",
529
+ "shadow-lg",
530
+ className
531
+ ),
532
+ "aria-label": label,
533
+ children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2", children: [
534
+ /* @__PURE__ */ jsxRuntime.jsx(
535
+ "svg",
536
+ {
537
+ className: "w-4 h-4",
538
+ fill: "none",
539
+ stroke: "currentColor",
540
+ viewBox: "0 0 24 24",
541
+ children: /* @__PURE__ */ jsxRuntime.jsx(
542
+ "path",
543
+ {
544
+ strokeLinecap: "round",
545
+ strokeLinejoin: "round",
546
+ strokeWidth: 2,
547
+ d: "M19 14l-7 7m0 0l-7-7m7 7V3"
548
+ }
549
+ )
550
+ }
551
+ ),
552
+ label
553
+ ] })
554
+ }
555
+ );
556
+ }
557
+ var ReactMarkdown = react.lazy(() => import('react-markdown'));
558
+ function LazyMarkdown({ children, fallback, components, className }) {
559
+ const [mounted, setMounted] = react.useState(false);
560
+ react.useEffect(() => {
561
+ setMounted(true);
562
+ }, []);
563
+ const markdownComponents = react.useMemo(() => {
564
+ if (!components) return void 0;
565
+ return components;
566
+ }, [components]);
567
+ if (!mounted) {
568
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: fallback ?? children });
569
+ }
570
+ return /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: fallback ?? children }), children: /* @__PURE__ */ jsxRuntime.jsx(ReactMarkdown, { components: markdownComponents, children }) });
571
+ }
572
+ var MessageContext = react.createContext(null);
573
+ function useMessage() {
574
+ const context = react.useContext(MessageContext);
575
+ if (!context) {
576
+ throw new Error("useMessage must be used within a Message");
577
+ }
578
+ return context;
579
+ }
580
+ function Message({
581
+ from,
582
+ children,
583
+ className,
584
+ isStreaming,
585
+ toolInvocations,
586
+ id
587
+ }) {
588
+ const contextValue = {
589
+ from,
590
+ isStreaming,
591
+ toolInvocations
592
+ };
593
+ const roleMap = {
594
+ user: "user",
595
+ assistant: "assistant",
596
+ system: "system"
597
+ };
598
+ return /* @__PURE__ */ jsxRuntime.jsx(MessageContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
599
+ "div",
600
+ {
601
+ className: cn(
602
+ "ash-message",
603
+ "flex gap-[var(--ash-message-gap,0.5rem)]",
604
+ from === "user" && "flex-row-reverse",
605
+ className
606
+ ),
607
+ "data-message-role": roleMap[from],
608
+ "data-message-id": id,
609
+ children
610
+ }
611
+ ) });
612
+ }
613
+ function MessageAvatar({ src, fallback, className }) {
614
+ const { from } = useMessage();
615
+ const defaultFallback = from === "user" ? "U" : from === "assistant" ? "A" : "S";
616
+ const displayFallback = fallback || defaultFallback;
617
+ const bgColorMap = {
618
+ user: "var(--ash-avatar-user-bg,rgba(255,255,255,0.1))",
619
+ assistant: "var(--ash-avatar-assistant-bg,#10a37f)",
620
+ system: "var(--ash-avatar-user-bg,rgba(255,255,255,0.1))"
621
+ };
622
+ return /* @__PURE__ */ jsxRuntime.jsx(
623
+ "div",
624
+ {
625
+ className: cn(
626
+ "ash-message-avatar flex-shrink-0",
627
+ "w-[var(--ash-avatar-size,1.25rem)] h-[var(--ash-avatar-size,1.25rem)]",
628
+ "rounded-full flex items-center justify-center",
629
+ "text-[var(--ash-font-size-xs,10px)] font-medium",
630
+ from === "assistant" ? "text-white" : "text-[var(--ash-text-secondary,rgba(255,255,255,0.7))]",
631
+ className
632
+ ),
633
+ style: { backgroundColor: bgColorMap[from] },
634
+ children: src ? /* @__PURE__ */ jsxRuntime.jsx(
635
+ "img",
636
+ {
637
+ src,
638
+ alt: `${from} avatar`,
639
+ className: "w-full h-full rounded-full object-cover"
640
+ }
641
+ ) : displayFallback
642
+ }
643
+ );
644
+ }
645
+ function MessageContent({ children, className }) {
646
+ const { from } = useMessage();
647
+ const roleStyles = {
648
+ user: cn(
649
+ "bg-[var(--ash-user-bg,#5B6EF5)]",
650
+ "text-[var(--ash-user-text,#ffffff)]",
651
+ "border-[var(--ash-user-border,transparent)]"
652
+ ),
653
+ assistant: cn(
654
+ "bg-[var(--ash-assistant-bg,#1a1a1a)]",
655
+ "text-[var(--ash-assistant-text,rgba(255,255,255,0.9))]",
656
+ "border-[var(--ash-assistant-border,rgba(255,255,255,0.08))]"
657
+ ),
658
+ system: cn(
659
+ "bg-[var(--ash-surface-elevated,#111111)]",
660
+ "text-[var(--ash-text-muted,rgba(255,255,255,0.5))]",
661
+ "border-[var(--ash-border,rgba(255,255,255,0.08))]"
662
+ )
663
+ };
664
+ return /* @__PURE__ */ jsxRuntime.jsx(
665
+ "div",
666
+ {
667
+ className: cn(
668
+ "ash-message-content",
669
+ "flex-1 min-w-0",
670
+ "rounded-[var(--ash-radius-lg,0.75rem)]",
671
+ "p-[var(--ash-message-padding,0.5rem_0.75rem)]",
672
+ "border",
673
+ roleStyles[from],
674
+ from === "user" && "rounded-tr-sm",
675
+ from === "assistant" && "rounded-tl-sm",
676
+ className
677
+ ),
678
+ children
679
+ }
680
+ );
681
+ }
682
+ function MessageResponse({
683
+ content,
684
+ isStreaming,
685
+ className,
686
+ components
687
+ }) {
688
+ if (!content) return null;
689
+ return /* @__PURE__ */ jsxRuntime.jsx(
690
+ "div",
691
+ {
692
+ className: cn(
693
+ "ash-message-response",
694
+ "font-[var(--ash-font-size-base,13px)]",
695
+ "leading-relaxed",
696
+ isStreaming && "animate-pulse",
697
+ className
698
+ ),
699
+ children: /* @__PURE__ */ jsxRuntime.jsx(LazyMarkdown, { components, children: content })
700
+ }
701
+ );
702
+ }
703
+ function MessageActions({ children, className }) {
704
+ return /* @__PURE__ */ jsxRuntime.jsx(
705
+ "div",
706
+ {
707
+ className: cn(
708
+ "ash-message-actions",
709
+ "flex items-center gap-1 mt-2",
710
+ "opacity-0 group-hover:opacity-100 transition-opacity",
711
+ className
712
+ ),
713
+ children
714
+ }
715
+ );
716
+ }
717
+ function MessageAction({
718
+ icon,
719
+ label,
720
+ onClick,
721
+ className
722
+ }) {
723
+ return /* @__PURE__ */ jsxRuntime.jsx(
724
+ "button",
725
+ {
726
+ onClick,
727
+ className: cn(
728
+ "ash-message-action",
729
+ "p-1.5 rounded-md",
730
+ "text-[var(--ash-text-muted,rgba(255,255,255,0.5))]",
731
+ "hover:text-[var(--ash-text-secondary,rgba(255,255,255,0.7))]",
732
+ "hover:bg-[var(--ash-surface-elevated,#111111)]",
733
+ "transition-colors",
734
+ className
735
+ ),
736
+ title: label,
737
+ "aria-label": label,
738
+ children: icon || label
739
+ }
740
+ );
741
+ }
742
+ function MessageTimestamp({ timestamp, className }) {
743
+ const date = typeof timestamp === "string" ? new Date(timestamp) : timestamp;
744
+ const formatted = date.toLocaleTimeString(void 0, {
745
+ hour: "numeric",
746
+ minute: "2-digit"
747
+ });
748
+ return /* @__PURE__ */ jsxRuntime.jsx(
749
+ "span",
750
+ {
751
+ className: cn(
752
+ "ash-message-timestamp",
753
+ "text-[var(--ash-font-size-xs,10px)]",
754
+ "text-[var(--ash-text-faint,rgba(255,255,255,0.3))]",
755
+ className
756
+ ),
757
+ children: formatted
758
+ }
759
+ );
760
+ }
761
+ function StatusIndicator({ status, size = "sm", className }) {
762
+ const sizeClasses = {
763
+ sm: "w-2 h-2",
764
+ md: "w-3 h-3",
765
+ lg: "w-4 h-4"
766
+ };
767
+ const statusClasses = {
768
+ pending: "ash-status-pending",
769
+ success: "ash-status-success",
770
+ failed: "ash-status-failed"
771
+ };
772
+ return /* @__PURE__ */ jsxRuntime.jsx(
773
+ "div",
774
+ {
775
+ className: cn(
776
+ "rounded-full flex-shrink-0",
777
+ sizeClasses[size],
778
+ statusClasses[status],
779
+ className
780
+ )
781
+ }
782
+ );
783
+ }
447
784
  function SunIcon({ className }) {
448
785
  return /* @__PURE__ */ jsxRuntime.jsxs("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
449
786
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "5" }),
@@ -694,57 +1031,25 @@ function ErrorIcon({ className }) {
694
1031
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", y1: "9", x2: "15", y2: "15" })
695
1032
  ] });
696
1033
  }
697
- function StatusIndicator({ status, size = "sm", className }) {
698
- const sizeClasses = {
699
- sm: "w-2 h-2",
700
- md: "w-3 h-3",
701
- lg: "w-4 h-4"
702
- };
703
- const statusClasses = {
704
- pending: "ash-status-pending",
705
- success: "ash-status-success",
706
- failed: "ash-status-failed"
707
- };
708
- return /* @__PURE__ */ jsxRuntime.jsx(
709
- "div",
710
- {
711
- className: cn(
712
- "rounded-full flex-shrink-0",
713
- sizeClasses[size],
714
- statusClasses[status],
715
- className
716
- )
717
- }
718
- );
1034
+ function MicrophoneIcon({ className }) {
1035
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1036
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" }),
1037
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
1038
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "19", x2: "12", y2: "23" }),
1039
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "8", y1: "23", x2: "16", y2: "23" })
1040
+ ] });
719
1041
  }
720
- function ActionIcon({ actionType, className = "w-4 h-4" }) {
721
- switch (actionType.action) {
722
- case "command_run":
723
- return /* @__PURE__ */ jsxRuntime.jsx(TerminalIcon, { className });
724
- case "file_read":
725
- return /* @__PURE__ */ jsxRuntime.jsx(FileIcon, { className });
726
- case "file_edit":
727
- return /* @__PURE__ */ jsxRuntime.jsx(EditIcon, { className });
728
- case "file_write":
729
- return /* @__PURE__ */ jsxRuntime.jsx(FilePlusIcon, { className });
730
- case "search":
731
- return /* @__PURE__ */ jsxRuntime.jsx(SearchIcon, { className });
732
- case "glob":
733
- return /* @__PURE__ */ jsxRuntime.jsx(FolderSearchIcon, { className });
734
- case "web_fetch":
735
- return /* @__PURE__ */ jsxRuntime.jsx(GlobeIcon, { className });
736
- case "web_search":
737
- return /* @__PURE__ */ jsxRuntime.jsx(SearchIcon, { className });
738
- case "mcp_tool":
739
- return /* @__PURE__ */ jsxRuntime.jsx(PlugIcon, { className });
740
- case "todo_write":
741
- return /* @__PURE__ */ jsxRuntime.jsx(ListChecksIcon, { className });
742
- case "agent_tool":
743
- return /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className });
744
- case "generic_tool":
745
- default:
746
- return /* @__PURE__ */ jsxRuntime.jsx(ToolIcon, { className });
747
- }
1042
+ function HomeIcon({ className }) {
1043
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1044
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }),
1045
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "9 22 9 12 15 12 15 22" })
1046
+ ] });
1047
+ }
1048
+ function ArrowUpIcon({ className }) {
1049
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1050
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "19", x2: "12", y2: "5" }),
1051
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "5 12 12 5 19 12" })
1052
+ ] });
748
1053
  }
749
1054
  function CodeBlock({
750
1055
  children,
@@ -754,14 +1059,41 @@ function CodeBlock({
754
1059
  className
755
1060
  }) {
756
1061
  const [expanded, setExpanded] = react.useState(false);
1062
+ const [copied, setCopied] = react.useState(false);
757
1063
  const lines = children.split("\n");
758
1064
  const isLong = lines.length > 10 || children.length > 500;
759
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative", className), children: [
1065
+ const handleCopy = react.useCallback(async () => {
1066
+ try {
1067
+ await navigator.clipboard.writeText(children);
1068
+ setCopied(true);
1069
+ setTimeout(() => setCopied(false), 2e3);
1070
+ } catch (err) {
1071
+ console.error("Failed to copy:", err);
1072
+ }
1073
+ }, [children]);
1074
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative rounded-lg overflow-hidden border border-white/10", className), children: [
1075
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-4 py-2 bg-[var(--ash-code-header-bg)] border-b border-white/10", children: [
1076
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-white/60 font-medium", children: language || "code" }),
1077
+ /* @__PURE__ */ jsxRuntime.jsx(
1078
+ "button",
1079
+ {
1080
+ onClick: handleCopy,
1081
+ className: "flex items-center gap-1.5 text-xs text-white/60 hover:text-white transition-colors",
1082
+ children: copied ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1083
+ /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: "w-3.5 h-3.5" }),
1084
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Copied!" })
1085
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1086
+ /* @__PURE__ */ jsxRuntime.jsx(CopyIcon, { className: "w-3.5 h-3.5" }),
1087
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Copy" })
1088
+ ] })
1089
+ }
1090
+ )
1091
+ ] }),
760
1092
  /* @__PURE__ */ jsxRuntime.jsx(
761
1093
  "pre",
762
1094
  {
763
1095
  className: cn(
764
- "ash-tool-code-block text-xs font-mono text-white/90 p-3 rounded-xl overflow-x-auto whitespace-pre-wrap break-words",
1096
+ "text-xs font-mono text-white/90 p-4 bg-black/30 overflow-x-auto whitespace-pre-wrap break-words",
765
1097
  !expanded && isLong && "overflow-y-hidden"
766
1098
  ),
767
1099
  style: !expanded && isLong ? { maxHeight } : void 0,
@@ -794,1519 +1126,1173 @@ function JsonDisplay({ value, maxHeight, className }) {
794
1126
  const formatted = JSON.stringify(value, null, 2);
795
1127
  return /* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { maxHeight, className, children: formatted });
796
1128
  }
797
- function AgentToolCard({
798
- toolCall,
799
- defaultExpanded = false,
1129
+ var ToolContext = react.createContext(null);
1130
+ function useTool() {
1131
+ const context = react.useContext(ToolContext);
1132
+ if (!context) {
1133
+ throw new Error("useTool must be used within a Tool");
1134
+ }
1135
+ return context;
1136
+ }
1137
+ function Tool({
1138
+ toolInvocation,
1139
+ children,
800
1140
  className,
801
- depth = 0
1141
+ defaultExpanded = false
802
1142
  }) {
803
- const [expanded, setExpanded] = react.useState(defaultExpanded);
804
- const [elapsedTime, setElapsedTime] = react.useState("");
805
- const intervalRef = react.useRef(null);
806
- const { actionType, status, summary, nestedToolCalls, nestedToolCallCount, startedAt } = toolCall;
807
- const agentData = react.useMemo(() => {
808
- if (actionType.action !== "agent_tool") {
809
- return null;
1143
+ const [isExpanded, setIsExpanded] = react.useState(defaultExpanded);
1144
+ const toggleExpanded = () => setIsExpanded((prev) => !prev);
1145
+ const contextValue = {
1146
+ toolInvocation,
1147
+ isExpanded,
1148
+ toggleExpanded
1149
+ };
1150
+ const status = toolInvocation.state === "result" ? "success" : "pending";
1151
+ const isErrorResult = Boolean(
1152
+ toolInvocation.result && typeof toolInvocation.result === "object" && "error" in toolInvocation.result
1153
+ );
1154
+ return /* @__PURE__ */ jsxRuntime.jsx(ToolContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
1155
+ "div",
1156
+ {
1157
+ className: cn(
1158
+ "ash-tool",
1159
+ "rounded-[var(--ash-radius-md,0.5rem)]",
1160
+ "border border-[var(--ash-border,rgba(255,255,255,0.08))]",
1161
+ "bg-[var(--ash-tool-bg,var(--ash-surface-dark,#0a0a0a))]",
1162
+ "overflow-hidden",
1163
+ status === "pending" && "ash-tool-status-pending",
1164
+ isErrorResult && "border-red-500/30",
1165
+ className
1166
+ ),
1167
+ "data-tool-name": toolInvocation.toolName,
1168
+ "data-tool-state": toolInvocation.state,
1169
+ children: children || /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1170
+ /* @__PURE__ */ jsxRuntime.jsx(ToolHeader, {}),
1171
+ isExpanded && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1172
+ /* @__PURE__ */ jsxRuntime.jsx(ToolInput, {}),
1173
+ /* @__PURE__ */ jsxRuntime.jsx(ToolOutput, {})
1174
+ ] })
1175
+ ] })
810
1176
  }
811
- const agentAction = actionType;
812
- return {
813
- agentType: agentAction.agentType,
814
- description: agentAction.description,
815
- prompt: agentAction.prompt
816
- };
817
- }, [actionType]);
818
- const toolCount = nestedToolCallCount ?? nestedToolCalls?.length ?? 0;
819
- const isRunning = status === "pending";
820
- react.useEffect(() => {
821
- if (isRunning && startedAt) {
822
- setElapsedTime(formatElapsedTime(startedAt));
823
- intervalRef.current = setInterval(() => {
824
- setElapsedTime(formatElapsedTime(startedAt));
825
- }, 1e3);
826
- return () => {
827
- if (intervalRef.current) {
828
- clearInterval(intervalRef.current);
829
- intervalRef.current = null;
830
- }
831
- };
832
- } else if (!isRunning) {
833
- if (intervalRef.current) {
834
- clearInterval(intervalRef.current);
835
- intervalRef.current = null;
836
- }
837
- setElapsedTime("");
1177
+ ) });
1178
+ }
1179
+ function ToolHeader({
1180
+ className,
1181
+ icon,
1182
+ showToggle = true
1183
+ }) {
1184
+ const { toolInvocation, isExpanded, toggleExpanded } = useTool();
1185
+ const status = toolInvocation.state === "result" ? "success" : "pending";
1186
+ const formattedName = formatToolName(toolInvocation.toolName);
1187
+ const toolIcon = icon;
1188
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1189
+ "button",
1190
+ {
1191
+ onClick: showToggle ? toggleExpanded : void 0,
1192
+ className: cn(
1193
+ "ash-tool-header",
1194
+ "w-full flex items-center gap-2",
1195
+ "px-3 py-2",
1196
+ "text-left",
1197
+ showToggle && "cursor-pointer hover:bg-white/[0.03] transition-colors",
1198
+ className
1199
+ ),
1200
+ children: [
1201
+ /* @__PURE__ */ jsxRuntime.jsx(StatusIndicator, { status, size: "sm" }),
1202
+ toolIcon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[var(--ash-text-muted,rgba(255,255,255,0.5))]", children: toolIcon }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[var(--ash-text-muted,rgba(255,255,255,0.5))]", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [
1203
+ /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" }),
1204
+ /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 12a3 3 0 11-6 0 3 3 0 016 0z" })
1205
+ ] }) }),
1206
+ /* @__PURE__ */ jsxRuntime.jsx(
1207
+ "span",
1208
+ {
1209
+ className: cn(
1210
+ "flex-1 font-medium",
1211
+ "text-[var(--ash-font-size-sm,12px)]",
1212
+ "text-[var(--ash-text-primary,rgba(255,255,255,0.9))]"
1213
+ ),
1214
+ children: formattedName
1215
+ }
1216
+ ),
1217
+ /* @__PURE__ */ jsxRuntime.jsx(
1218
+ "span",
1219
+ {
1220
+ className: cn(
1221
+ "text-[var(--ash-font-size-xs,10px)]",
1222
+ "text-[var(--ash-text-muted,rgba(255,255,255,0.5))]",
1223
+ "truncate max-w-[200px]"
1224
+ ),
1225
+ children: Object.keys(toolInvocation.args).length > 0 && `(${Object.keys(toolInvocation.args).join(", ")})`
1226
+ }
1227
+ ),
1228
+ showToggle && /* @__PURE__ */ jsxRuntime.jsx(
1229
+ "svg",
1230
+ {
1231
+ className: cn(
1232
+ "w-4 h-4 text-[var(--ash-text-muted,rgba(255,255,255,0.5))]",
1233
+ "transition-transform",
1234
+ isExpanded && "rotate-180"
1235
+ ),
1236
+ fill: "none",
1237
+ stroke: "currentColor",
1238
+ viewBox: "0 0 24 24",
1239
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1240
+ "path",
1241
+ {
1242
+ strokeLinecap: "round",
1243
+ strokeLinejoin: "round",
1244
+ strokeWidth: 2,
1245
+ d: "M19 9l-7 7-7-7"
1246
+ }
1247
+ )
1248
+ }
1249
+ )
1250
+ ]
838
1251
  }
839
- return void 0;
840
- }, [isRunning, startedAt]);
841
- if (!agentData) {
1252
+ );
1253
+ }
1254
+ function ToolInput({ className, maxHeight = 200 }) {
1255
+ const { toolInvocation } = useTool();
1256
+ if (!toolInvocation.args || Object.keys(toolInvocation.args).length === 0) {
842
1257
  return null;
843
1258
  }
844
- const { agentType, description, prompt } = agentData;
845
- const indentClass = depth > 0 ? "ml-4" : "";
846
1259
  return /* @__PURE__ */ jsxRuntime.jsxs(
847
1260
  "div",
848
1261
  {
849
1262
  className: cn(
850
- "rounded-xl border bg-[var(--ash-surface-dark,#0a0a0a)] overflow-hidden",
851
- isRunning ? "border-yellow-500/30" : status === "failed" ? "border-red-500/30" : "border-white/10",
852
- indentClass,
1263
+ "ash-tool-input",
1264
+ "border-t border-[var(--ash-border-subtle,rgba(255,255,255,0.04))]",
853
1265
  className
854
1266
  ),
855
1267
  children: [
856
- /* @__PURE__ */ jsxRuntime.jsxs(
857
- "button",
1268
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ash-tool-section-header", children: "Input" }),
1269
+ /* @__PURE__ */ jsxRuntime.jsx(
1270
+ "div",
858
1271
  {
859
- onClick: () => setExpanded(!expanded),
860
- className: "w-full px-4 py-3 flex items-center gap-3 hover:bg-white/5 cursor-pointer transition-colors",
861
- children: [
862
- /* @__PURE__ */ jsxRuntime.jsx(
863
- ChevronRightIcon,
864
- {
865
- className: cn(
866
- "w-4 h-4 text-white/40 transition-transform duration-200 shrink-0",
867
- expanded && "rotate-90"
868
- )
869
- }
870
- ),
871
- /* @__PURE__ */ jsxRuntime.jsx(
872
- "div",
873
- {
874
- className: cn(
875
- "w-6 h-6 rounded-lg flex items-center justify-center shrink-0",
876
- isRunning ? "bg-yellow-500/20" : status === "failed" ? "bg-red-500/20" : "bg-[var(--ash-accent)]/20"
877
- ),
878
- children: isRunning ? /* @__PURE__ */ jsxRuntime.jsx(
879
- SpinnerIcon,
880
- {
881
- className: "w-3.5 h-3.5 text-yellow-400 animate-spin"
882
- }
883
- ) : /* @__PURE__ */ jsxRuntime.jsx(
884
- BotIcon,
885
- {
886
- className: cn(
887
- "w-3.5 h-3.5",
888
- status === "failed" ? "text-red-400" : "text-[var(--ash-accent)]"
889
- )
890
- }
891
- )
892
- }
893
- ),
894
- /* @__PURE__ */ jsxRuntime.jsx(
895
- "span",
896
- {
897
- className: cn(
898
- "px-2 py-0.5 rounded text-xs font-medium shrink-0",
899
- isRunning ? "bg-yellow-500/20 text-yellow-400" : status === "failed" ? "bg-red-500/20 text-red-400" : "bg-white/10 text-white/70"
900
- ),
901
- children: agentType
902
- }
903
- ),
904
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-white/80 truncate flex-1 text-left", children: description || summary }),
905
- toolCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-white/50 shrink-0", children: [
906
- toolCount,
907
- " tool call",
908
- toolCount !== 1 ? "s" : ""
909
- ] }),
910
- isRunning && elapsedTime && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 text-xs text-white/40 shrink-0", children: [
911
- /* @__PURE__ */ jsxRuntime.jsx(ClockIcon, { className: "w-3 h-3" }),
912
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: elapsedTime })
913
- ] }),
914
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-white/30 shrink-0", children: "..." })
915
- ]
1272
+ className: "p-3 overflow-auto",
1273
+ style: { maxHeight },
1274
+ children: /* @__PURE__ */ jsxRuntime.jsx(JsonDisplay, { value: toolInvocation.args })
916
1275
  }
917
- ),
918
- expanded && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-white/5 bg-black/20", children: [
919
- prompt && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 border-b border-white/5", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-white/70 whitespace-pre-wrap", children: prompt.length > 500 ? prompt.substring(0, 500) + "..." : prompt }) }),
920
- nestedToolCalls && nestedToolCalls.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 space-y-2", children: nestedToolCalls.map((nestedCall) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: nestedCall.actionType.action === "agent_tool" ? /* @__PURE__ */ jsxRuntime.jsx(
921
- AgentToolCard,
922
- {
923
- toolCall: nestedCall,
924
- defaultExpanded: false,
925
- depth: depth + 1
926
- }
927
- ) : /* @__PURE__ */ jsxRuntime.jsx(
928
- ToolCallCard,
929
- {
930
- toolCall: nestedCall,
931
- defaultExpanded: false
932
- }
933
- ) }, nestedCall.id)) }),
934
- (!nestedToolCalls || nestedToolCalls.length === 0) && isRunning && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-6 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm text-white/40", children: [
935
- /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, { className: "w-4 h-4 animate-spin" }),
936
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Agent is working..." })
937
- ] }) }),
938
- (!nestedToolCalls || nestedToolCalls.length === 0) && !isRunning && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-4 text-sm text-white/40 text-center", children: "No tool calls recorded" })
939
- ] })
1276
+ )
940
1277
  ]
941
1278
  }
942
1279
  );
943
1280
  }
944
- function SectionHeader({ children }) {
945
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ash-tool-section-header", children });
946
- }
947
- function SectionContent({ children }) {
948
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2", children });
949
- }
950
- function CommandRunDetails({ action }) {
951
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
952
- action.command && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
953
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "COMMAND" }),
954
- /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { children: action.command }) })
955
- ] }),
956
- action.result?.output && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
957
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "OUTPUT" }),
958
- /* @__PURE__ */ jsxRuntime.jsxs(SectionContent, { children: [
959
- /* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { maxHeight: 300, children: action.result.output }),
960
- action.result.exitCode !== void 0 && action.result.exitCode !== 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 text-xs text-red-400", children: [
961
- "Exit code: ",
962
- action.result.exitCode
963
- ] })
964
- ] })
965
- ] })
966
- ] });
967
- }
968
- function FileReadDetails({ action }) {
969
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
970
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "PATH" }),
971
- /* @__PURE__ */ jsxRuntime.jsxs(SectionContent, { children: [
972
- /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-xs font-mono bg-white/10 text-white/90 px-1 py-0.5 rounded", children: action.path }),
973
- (action.offset !== void 0 || action.limit !== void 0) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 text-xs text-white/50", children: [
974
- action.offset !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
975
- "Offset: ",
976
- action.offset
977
- ] }),
978
- action.offset !== void 0 && action.limit !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { children: " \xB7 " }),
979
- action.limit !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
980
- "Limit: ",
981
- action.limit
982
- ] })
983
- ] })
984
- ] })
985
- ] });
986
- }
987
- function FileEditDetails({ action }) {
988
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
989
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "PATH" }),
990
- /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-xs font-mono bg-white/10 text-white/90 px-1 py-0.5 rounded", children: action.path }) }),
991
- action.oldString && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
992
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "OLD" }),
993
- /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { children: action.oldString }) })
994
- ] }),
995
- action.newString && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
996
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "NEW" }),
997
- /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { children: action.newString }) })
998
- ] })
999
- ] });
1000
- }
1001
- function FileWriteDetails({ action }) {
1002
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1003
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "PATH" }),
1004
- /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-xs font-mono bg-white/10 text-white/90 px-1 py-0.5 rounded", children: action.path }) }),
1005
- action.content && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1006
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "CONTENT" }),
1007
- /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { maxHeight: 300, children: action.content }) })
1008
- ] })
1009
- ] });
1010
- }
1011
- function SearchDetails({ action }) {
1012
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1013
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "PATTERN" }),
1014
- /* @__PURE__ */ jsxRuntime.jsxs(SectionContent, { children: [
1015
- /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-xs font-mono bg-white/10 text-white/90 px-1 py-0.5 rounded", children: action.pattern }),
1016
- (action.path || action.glob || action.type) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 text-xs text-white/50", children: [
1017
- action.path && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1018
- "Path: ",
1019
- action.path
1020
- ] }),
1021
- action.glob && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1022
- "Glob: ",
1023
- action.glob
1024
- ] }),
1025
- action.type && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1026
- "Type: ",
1027
- action.type
1028
- ] })
1029
- ] })
1030
- ] })
1031
- ] });
1032
- }
1033
- function GlobDetails({ action }) {
1034
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1035
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "PATTERN" }),
1036
- /* @__PURE__ */ jsxRuntime.jsxs(SectionContent, { children: [
1037
- /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-xs font-mono bg-white/10 text-white/90 px-1 py-0.5 rounded", children: action.pattern }),
1038
- action.path && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 text-xs text-white/50", children: [
1039
- "Path: ",
1040
- action.path
1041
- ] })
1042
- ] })
1043
- ] });
1044
- }
1045
- function WebFetchDetails({ action }) {
1046
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1047
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "URL" }),
1048
- /* @__PURE__ */ jsxRuntime.jsxs(SectionContent, { children: [
1049
- /* @__PURE__ */ jsxRuntime.jsx(
1050
- "a",
1051
- {
1052
- href: action.url,
1053
- target: "_blank",
1054
- rel: "noopener noreferrer",
1055
- className: "text-xs text-blue-400 hover:text-blue-300 underline break-all",
1056
- children: action.url
1057
- }
1058
- ),
1059
- action.prompt && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 text-xs text-white/50", children: action.prompt })
1060
- ] })
1061
- ] });
1062
- }
1063
- function WebSearchDetails({ action }) {
1064
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1065
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "QUERY" }),
1066
- /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-white/90", children: action.query }) })
1067
- ] });
1068
- }
1069
- function McpToolDetails({ action }) {
1070
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1071
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "TOOL" }),
1072
- /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsxs("code", { className: "text-xs font-mono bg-white/10 text-white/90 px-1 py-0.5 rounded", children: [
1073
- action.serverName,
1074
- ":",
1075
- action.toolName
1076
- ] }) }),
1077
- action.arguments && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1078
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "ARGS" }),
1079
- /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsx(JsonDisplay, { value: action.arguments }) })
1080
- ] }),
1081
- action.result && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1082
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "RESULT" }),
1083
- /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: action.result.type === "markdown" ? /* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { children: String(action.result.value) }) : /* @__PURE__ */ jsxRuntime.jsx(JsonDisplay, { value: action.result.value }) })
1084
- ] })
1085
- ] });
1086
- }
1087
- function GenericToolDetails({ action }) {
1088
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1089
- action.arguments && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1090
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "ARGS" }),
1091
- /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsx(JsonDisplay, { value: action.arguments }) })
1092
- ] }),
1093
- action.result && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1094
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "RESULT" }),
1095
- /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: action.result.type === "markdown" ? /* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { children: String(action.result.value) }) : /* @__PURE__ */ jsxRuntime.jsx(JsonDisplay, { value: action.result.value }) })
1096
- ] })
1097
- ] });
1098
- }
1099
- function TodoWriteDetails({ action }) {
1100
- const { todos, stats } = action;
1101
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1102
- stats && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1103
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "PROGRESS" }),
1104
- /* @__PURE__ */ jsxRuntime.jsxs(SectionContent, { children: [
1105
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
1106
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-2 bg-white/10 rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
1107
- "div",
1108
- {
1109
- className: "h-full bg-emerald-400 rounded-full transition-all duration-500 ease-out",
1110
- style: { width: `${stats.total > 0 ? stats.completed / stats.total * 100 : 0}%` }
1111
- }
1112
- ) }),
1113
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-white/60 tabular-nums", children: [
1114
- stats.completed,
1115
- "/",
1116
- stats.total
1117
- ] })
1118
- ] }),
1119
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 mt-2 text-xs", children: [
1120
- stats.inProgress > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-yellow-400", children: [
1121
- stats.inProgress,
1122
- " in progress"
1123
- ] }),
1124
- stats.pending > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-white/40", children: [
1125
- stats.pending,
1126
- " pending"
1127
- ] })
1128
- ] })
1129
- ] })
1130
- ] }),
1131
- /* @__PURE__ */ jsxRuntime.jsx(SectionHeader, { children: "TASKS" }),
1132
- /* @__PURE__ */ jsxRuntime.jsx(SectionContent, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5", children: todos.map((todo, index) => {
1133
- const displayText = todo.status === "in_progress" ? todo.activeForm : todo.content;
1134
- return /* @__PURE__ */ jsxRuntime.jsxs(
1135
- "div",
1136
- {
1137
- className: cn(
1138
- "flex items-start gap-2 py-1 transition-all duration-200",
1139
- todo.status === "completed" && "opacity-50",
1140
- todo.status === "in_progress" && "bg-yellow-500/10 -mx-2 px-2 rounded"
1141
- ),
1142
- children: [
1143
- todo.status === "completed" ? /* @__PURE__ */ jsxRuntime.jsx(CheckCircleIcon, { className: "w-4 h-4 text-emerald-400 shrink-0 mt-0.5" }) : todo.status === "in_progress" ? /* @__PURE__ */ jsxRuntime.jsx(LoaderIcon, { className: "w-4 h-4 text-yellow-400 animate-spin shrink-0 mt-0.5" }) : /* @__PURE__ */ jsxRuntime.jsx(CircleIcon, { className: "w-4 h-4 text-white/30 shrink-0 mt-0.5" }),
1144
- /* @__PURE__ */ jsxRuntime.jsxs(
1145
- "span",
1146
- {
1147
- className: cn(
1148
- "text-sm leading-relaxed",
1149
- todo.status === "completed" ? "text-white/50 line-through" : "text-white/80"
1150
- ),
1151
- children: [
1152
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-white/40 mr-1.5", children: [
1153
- index + 1,
1154
- "."
1155
- ] }),
1156
- displayText
1157
- ]
1158
- }
1159
- )
1160
- ]
1161
- },
1162
- `${todo.content}-${index}`
1163
- );
1164
- }) }) })
1165
- ] });
1166
- }
1167
- function ToolDetails({ actionType }) {
1168
- switch (actionType.action) {
1169
- case "command_run":
1170
- return /* @__PURE__ */ jsxRuntime.jsx(CommandRunDetails, { action: actionType });
1171
- case "file_read":
1172
- return /* @__PURE__ */ jsxRuntime.jsx(FileReadDetails, { action: actionType });
1173
- case "file_edit":
1174
- return /* @__PURE__ */ jsxRuntime.jsx(FileEditDetails, { action: actionType });
1175
- case "file_write":
1176
- return /* @__PURE__ */ jsxRuntime.jsx(FileWriteDetails, { action: actionType });
1177
- case "search":
1178
- return /* @__PURE__ */ jsxRuntime.jsx(SearchDetails, { action: actionType });
1179
- case "glob":
1180
- return /* @__PURE__ */ jsxRuntime.jsx(GlobDetails, { action: actionType });
1181
- case "web_fetch":
1182
- return /* @__PURE__ */ jsxRuntime.jsx(WebFetchDetails, { action: actionType });
1183
- case "web_search":
1184
- return /* @__PURE__ */ jsxRuntime.jsx(WebSearchDetails, { action: actionType });
1185
- case "mcp_tool":
1186
- return /* @__PURE__ */ jsxRuntime.jsx(McpToolDetails, { action: actionType });
1187
- case "generic_tool":
1188
- return /* @__PURE__ */ jsxRuntime.jsx(GenericToolDetails, { action: actionType });
1189
- case "todo_write":
1190
- return /* @__PURE__ */ jsxRuntime.jsx(TodoWriteDetails, { action: actionType });
1191
- default:
1192
- return null;
1193
- }
1194
- }
1195
- function hasDetails(actionType) {
1196
- switch (actionType.action) {
1197
- case "command_run":
1198
- return Boolean(actionType.command || actionType.result?.output);
1199
- case "file_read":
1200
- return true;
1201
- case "file_edit":
1202
- return Boolean(actionType.oldString || actionType.newString);
1203
- case "file_write":
1204
- return Boolean(actionType.content);
1205
- case "search":
1206
- return true;
1207
- case "glob":
1208
- return true;
1209
- case "web_fetch":
1210
- return true;
1211
- case "web_search":
1212
- return true;
1213
- case "mcp_tool":
1214
- return Boolean(actionType.arguments || actionType.result);
1215
- case "generic_tool":
1216
- return Boolean(actionType.arguments || actionType.result);
1217
- case "todo_write":
1218
- return actionType.todos.length > 0;
1219
- case "agent_tool":
1220
- return true;
1221
- // Always expandable (handled by AgentToolCard)
1222
- default:
1223
- return false;
1224
- }
1225
- }
1226
- function ToolCallCard({ toolCall, defaultExpanded = false, className }) {
1227
- const [expanded, setExpanded] = react.useState(defaultExpanded);
1228
- const { actionType, status, summary } = toolCall;
1229
- if (actionType.action === "agent_tool") {
1230
- return /* @__PURE__ */ jsxRuntime.jsx(
1231
- AgentToolCard,
1232
- {
1233
- toolCall,
1234
- defaultExpanded,
1235
- className
1236
- }
1237
- );
1281
+ function ToolOutput({ className, maxHeight = 300 }) {
1282
+ const { toolInvocation } = useTool();
1283
+ if (toolInvocation.state !== "result" || !toolInvocation.result) {
1284
+ return null;
1238
1285
  }
1239
- const canExpand = hasDetails(actionType);
1240
- const statusClasses = {
1241
- pending: "border-yellow-500/30 ash-tool-status-pending",
1242
- success: "border-white/10",
1243
- failed: "border-red-500/30"
1244
- };
1286
+ const result = toolInvocation.result;
1287
+ const isString = typeof result === "string";
1288
+ const isError = typeof result === "object" && result !== null && "error" in result;
1245
1289
  return /* @__PURE__ */ jsxRuntime.jsxs(
1246
1290
  "div",
1247
1291
  {
1248
1292
  className: cn(
1249
- "rounded-xl border bg-[var(--ash-surface-dark,#0a0a0a)] overflow-hidden",
1250
- statusClasses[status],
1293
+ "ash-tool-output",
1294
+ "border-t border-[var(--ash-border-subtle,rgba(255,255,255,0.04))]",
1295
+ isError && "bg-red-500/5",
1251
1296
  className
1252
1297
  ),
1253
1298
  children: [
1254
- /* @__PURE__ */ jsxRuntime.jsxs(
1255
- "button",
1299
+ /* @__PURE__ */ jsxRuntime.jsx(
1300
+ "div",
1256
1301
  {
1257
- onClick: () => canExpand && setExpanded(!expanded),
1258
1302
  className: cn(
1259
- "w-full px-4 py-3 flex items-center justify-between transition-colors",
1260
- canExpand ? "hover:bg-white/5 cursor-pointer" : "cursor-default"
1303
+ "ash-tool-section-header",
1304
+ isError && "text-red-400"
1261
1305
  ),
1262
- disabled: !canExpand,
1263
- children: [
1264
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0 flex-1", children: [
1265
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(
1266
- "w-6 h-6 rounded-lg flex items-center justify-center shrink-0",
1267
- status === "pending" ? "bg-yellow-500/20" : status === "failed" ? "bg-red-500/20" : "bg-[var(--ash-accent)]/20"
1268
- ), children: /* @__PURE__ */ jsxRuntime.jsx(
1269
- ActionIcon,
1270
- {
1271
- actionType,
1272
- className: cn(
1273
- "w-3.5 h-3.5",
1274
- status === "pending" ? "text-yellow-400" : status === "failed" ? "text-red-400" : "text-[var(--ash-accent)]"
1275
- )
1276
- }
1277
- ) }),
1278
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-white shrink-0", children: getActionLabel(actionType) }),
1279
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-sm truncate text-white/60 min-w-0", children: summary })
1280
- ] }),
1281
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
1282
- /* @__PURE__ */ jsxRuntime.jsx(StatusIndicator, { status, size: "sm" }),
1283
- canExpand && /* @__PURE__ */ jsxRuntime.jsx(
1284
- ChevronDownIcon,
1285
- {
1286
- className: cn(
1287
- "w-4 h-4 text-white/40 transition-transform duration-200",
1288
- expanded && "rotate-180"
1289
- )
1290
- }
1291
- )
1292
- ] })
1293
- ]
1306
+ children: isError ? "Error" : "Output"
1294
1307
  }
1295
1308
  ),
1296
- expanded && canExpand && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-white/5 max-h-[400px] overflow-y-auto ash-scrollbar bg-black/20", children: [
1297
- /* @__PURE__ */ jsxRuntime.jsx(ToolDetails, { actionType }),
1298
- status === "success" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 border-t border-white/5 bg-[var(--ash-accent)]/5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1299
- /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: "w-4 h-4 text-[var(--ash-accent)]" }),
1300
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-[var(--ash-accent)] font-medium", children: "Completed successfully" })
1301
- ] }) })
1302
- ] })
1309
+ /* @__PURE__ */ jsxRuntime.jsx(
1310
+ "div",
1311
+ {
1312
+ className: "p-3 overflow-auto",
1313
+ style: { maxHeight },
1314
+ children: isString ? /* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { maxHeight: maxHeight - 24, children: result }) : /* @__PURE__ */ jsxRuntime.jsx(JsonDisplay, { value: result })
1315
+ }
1316
+ )
1303
1317
  ]
1304
1318
  }
1305
1319
  );
1306
1320
  }
1307
- var ReactMarkdown = react.lazy(() => import('react-markdown'));
1308
- function LazyMarkdown({ children, fallback, className }) {
1309
- const [mounted, setMounted] = react.useState(false);
1310
- react.useEffect(() => {
1311
- setMounted(true);
1312
- }, []);
1313
- if (!mounted) {
1314
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: fallback ?? children });
1315
- }
1316
- return /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx("span", { className, children: fallback ?? children }), children: /* @__PURE__ */ jsxRuntime.jsx(ReactMarkdown, { children }) });
1317
- }
1318
- function DefaultMentionBadge({ segment }) {
1319
- const bgColor = segment.color ? `${segment.color}20` : "rgba(34, 197, 94, 0.2)";
1320
- const textColor = segment.color || "#22c55e";
1321
- const borderColor = segment.color ? `${segment.color}40` : "rgba(34, 197, 94, 0.4)";
1322
- return /* @__PURE__ */ jsxRuntime.jsxs(
1323
- "span",
1321
+ function ToolList({
1322
+ toolInvocations,
1323
+ className,
1324
+ defaultExpanded = false
1325
+ }) {
1326
+ if (!toolInvocations || toolInvocations.length === 0) {
1327
+ return null;
1328
+ }
1329
+ return /* @__PURE__ */ jsxRuntime.jsx(
1330
+ "div",
1324
1331
  {
1325
- className: "inline-flex items-center px-2 py-0.5 rounded-md text-xs font-medium mx-0.5",
1326
- style: {
1327
- backgroundColor: bgColor,
1328
- color: textColor,
1329
- border: `1px solid ${borderColor}`
1330
- },
1331
- title: segment.id ? `ID: ${segment.id}` : void 0,
1332
- children: [
1333
- "@",
1334
- segment.name
1335
- ]
1332
+ className: cn(
1333
+ "ash-tool-list",
1334
+ "flex flex-col gap-2",
1335
+ className
1336
+ ),
1337
+ children: toolInvocations.map((inv) => /* @__PURE__ */ jsxRuntime.jsx(
1338
+ Tool,
1339
+ {
1340
+ toolInvocation: inv,
1341
+ defaultExpanded
1342
+ },
1343
+ inv.toolCallId
1344
+ ))
1336
1345
  }
1337
1346
  );
1338
1347
  }
1339
- function RichContentRenderer({
1340
- content,
1341
- renderers,
1342
- className
1348
+ var ReasoningContext = react.createContext(null);
1349
+ function useReasoning() {
1350
+ const context = react.useContext(ReasoningContext);
1351
+ if (!context) {
1352
+ throw new Error("useReasoning must be used within a Reasoning");
1353
+ }
1354
+ return context;
1355
+ }
1356
+ function Reasoning({
1357
+ children,
1358
+ isStreaming = false,
1359
+ className,
1360
+ autoExpand = true,
1361
+ autoCollapse = true,
1362
+ initialDuration = 0
1343
1363
  }) {
1344
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("rich-content", className), children: content.map((segment, index) => {
1345
- const key = `segment-${index}`;
1346
- switch (segment.type) {
1347
- case "text":
1348
- return /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx(LazyMarkdown, { children: segment.content }) }, key);
1349
- case "mention":
1350
- if (renderers?.renderMention) {
1351
- return /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: renderers.renderMention({ segment }) }, key);
1352
- }
1353
- return /* @__PURE__ */ jsxRuntime.jsx(DefaultMentionBadge, { segment }, key);
1354
- case "component":
1355
- if (renderers?.renderComponent) {
1356
- return /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: renderers.renderComponent({ segment }) }, key);
1364
+ const [isOpen, setIsOpen] = react.useState(false);
1365
+ const [duration, setDuration] = react.useState(initialDuration);
1366
+ const startTimeRef = react.useRef(null);
1367
+ const intervalRef = react.useRef(null);
1368
+ react.useEffect(() => {
1369
+ if (isStreaming && autoExpand) {
1370
+ setIsOpen(true);
1371
+ startTimeRef.current = Date.now();
1372
+ intervalRef.current = setInterval(() => {
1373
+ if (startTimeRef.current) {
1374
+ setDuration(Math.floor((Date.now() - startTimeRef.current) / 1e3));
1357
1375
  }
1358
- return /* @__PURE__ */ jsxRuntime.jsxs("code", { className: "text-xs text-orange-400", children: [
1359
- "[component: ",
1360
- segment.componentType,
1361
- "]"
1362
- ] }, key);
1363
- default:
1364
- return null;
1376
+ }, 1e3);
1365
1377
  }
1366
- }) });
1378
+ return () => {
1379
+ if (intervalRef.current) {
1380
+ clearInterval(intervalRef.current);
1381
+ intervalRef.current = null;
1382
+ }
1383
+ };
1384
+ }, [isStreaming, autoExpand]);
1385
+ react.useEffect(() => {
1386
+ if (!isStreaming && autoCollapse && startTimeRef.current) {
1387
+ const timeout = setTimeout(() => {
1388
+ setIsOpen(false);
1389
+ }, 500);
1390
+ startTimeRef.current = null;
1391
+ if (intervalRef.current) {
1392
+ clearInterval(intervalRef.current);
1393
+ intervalRef.current = null;
1394
+ }
1395
+ return () => clearTimeout(timeout);
1396
+ }
1397
+ return void 0;
1398
+ }, [isStreaming, autoCollapse]);
1399
+ const contextValue = {
1400
+ isOpen,
1401
+ setIsOpen,
1402
+ isStreaming,
1403
+ duration
1404
+ };
1405
+ return /* @__PURE__ */ jsxRuntime.jsx(ReasoningContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
1406
+ "div",
1407
+ {
1408
+ className: cn(
1409
+ "ash-reasoning",
1410
+ "rounded-[var(--ash-radius-md,0.5rem)]",
1411
+ "border",
1412
+ isStreaming ? "border-[var(--ash-thinking-border,rgba(147,51,234,0.2))] bg-[var(--ash-thinking-bg,rgba(147,51,234,0.06))]" : "border-[var(--ash-border,rgba(255,255,255,0.08))] bg-[var(--ash-surface-dark,#0a0a0a)]",
1413
+ "overflow-hidden",
1414
+ className
1415
+ ),
1416
+ "data-reasoning-streaming": isStreaming,
1417
+ "data-reasoning-open": isOpen,
1418
+ children
1419
+ }
1420
+ ) });
1367
1421
  }
1368
- function OptionCards({ options, onSelect, className }) {
1369
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("grid gap-2 mt-3", className), style: {
1370
- gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))"
1371
- }, children: options.map((option) => /* @__PURE__ */ jsxRuntime.jsxs(
1422
+ function ReasoningTrigger({
1423
+ className,
1424
+ label = "Thinking",
1425
+ showDuration = true
1426
+ }) {
1427
+ const { isOpen, setIsOpen, isStreaming, duration } = useReasoning();
1428
+ const formatDuration = (seconds) => {
1429
+ if (seconds < 60) return `${seconds}s`;
1430
+ const minutes = Math.floor(seconds / 60);
1431
+ const remainingSeconds = seconds % 60;
1432
+ return `${minutes}m ${remainingSeconds}s`;
1433
+ };
1434
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1372
1435
  "button",
1373
1436
  {
1374
- onClick: () => onSelect(option),
1437
+ onClick: () => setIsOpen(!isOpen),
1375
1438
  className: cn(
1376
- "flex items-start gap-3 p-3 rounded-xl text-left",
1377
- "bg-white/5 border border-white/10",
1378
- "hover:bg-[var(--ash-accent)]/10 hover:border-[var(--ash-accent)]/30",
1379
- "focus:outline-none focus:ring-2 focus:ring-[var(--ash-accent)]/50",
1380
- "transition-all duration-200 cursor-pointer",
1381
- "group"
1439
+ "ash-reasoning-trigger",
1440
+ "w-full flex items-center gap-2",
1441
+ "px-3 py-2",
1442
+ "text-left",
1443
+ "cursor-pointer hover:bg-white/[0.03] transition-colors",
1444
+ className
1382
1445
  ),
1383
1446
  children: [
1384
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(
1385
- "flex-shrink-0 w-6 h-6 rounded-lg",
1386
- "bg-[var(--ash-accent)]/20 text-[var(--ash-accent)]",
1387
- "flex items-center justify-center",
1388
- "text-xs font-semibold",
1389
- "group-hover:bg-[var(--ash-accent)]/30",
1390
- "transition-colors duration-200"
1391
- ), children: option.id }),
1392
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
1393
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-white/90 group-hover:text-white transition-colors", children: option.label }),
1394
- option.description && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-white/50 mt-0.5 line-clamp-2 group-hover:text-white/60 transition-colors", children: option.description })
1395
- ] }),
1396
1447
  /* @__PURE__ */ jsxRuntime.jsx(
1397
- "svg",
1448
+ "span",
1398
1449
  {
1399
1450
  className: cn(
1400
- "w-4 h-4 text-white/30 flex-shrink-0 mt-0.5",
1401
- "group-hover:text-[var(--ash-accent)] group-hover:translate-x-0.5",
1402
- "transition-all duration-200"
1451
+ "w-4 h-4 flex items-center justify-center",
1452
+ isStreaming && "animate-pulse"
1403
1453
  ),
1404
- fill: "none",
1405
- viewBox: "0 0 24 24",
1406
- stroke: "currentColor",
1407
- strokeWidth: 2,
1408
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" })
1454
+ children: isStreaming ? /* @__PURE__ */ jsxRuntime.jsxs(
1455
+ "svg",
1456
+ {
1457
+ className: "w-4 h-4 text-purple-400 animate-spin",
1458
+ fill: "none",
1459
+ viewBox: "0 0 24 24",
1460
+ children: [
1461
+ /* @__PURE__ */ jsxRuntime.jsx(
1462
+ "circle",
1463
+ {
1464
+ className: "opacity-25",
1465
+ cx: "12",
1466
+ cy: "12",
1467
+ r: "10",
1468
+ stroke: "currentColor",
1469
+ strokeWidth: "4"
1470
+ }
1471
+ ),
1472
+ /* @__PURE__ */ jsxRuntime.jsx(
1473
+ "path",
1474
+ {
1475
+ className: "opacity-75",
1476
+ fill: "currentColor",
1477
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
1478
+ }
1479
+ )
1480
+ ]
1481
+ }
1482
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1483
+ "svg",
1484
+ {
1485
+ className: "w-4 h-4 text-purple-400",
1486
+ fill: "none",
1487
+ stroke: "currentColor",
1488
+ viewBox: "0 0 24 24",
1489
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1490
+ "path",
1491
+ {
1492
+ strokeLinecap: "round",
1493
+ strokeLinejoin: "round",
1494
+ strokeWidth: 2,
1495
+ d: "M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"
1496
+ }
1497
+ )
1498
+ }
1499
+ )
1409
1500
  }
1410
- )
1411
- ]
1412
- },
1413
- option.id
1414
- )) });
1415
- }
1416
- function parseFilesFromContent(content) {
1417
- const fileMarker = "[Uploaded files available at /uploads/]";
1418
- const markerIndex = content.indexOf(fileMarker);
1419
- if (markerIndex === -1) {
1420
- return { text: content, files: [] };
1421
- }
1422
- const text = content.substring(0, markerIndex).trim();
1423
- const fileSection = content.substring(markerIndex + fileMarker.length).trim();
1424
- const files = fileSection.split("\n").filter((line) => line.startsWith("- ")).map((line) => line.substring(2).trim());
1425
- return { text, files };
1426
- }
1427
- function UserMessage({ entry, className }) {
1428
- const { text, files } = parseFilesFromContent(entry.content);
1429
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex gap-3 justify-end ash-animate-fade-in", className), children: [
1430
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-[85%]", children: [
1431
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-2xl p-4 bg-[var(--ash-accent)] text-[var(--ash-accent-foreground)]", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm leading-relaxed whitespace-pre-wrap", children: text || "(files attached)" }) }),
1432
- files.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 pt-2 border-t border-[var(--ash-accent-foreground)]/20", children: [
1433
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-[var(--ash-accent-foreground)]/60 mb-1 flex items-center gap-1", children: [
1434
- /* @__PURE__ */ jsxRuntime.jsx(PaperclipIcon, { className: "w-3 h-3" }),
1435
- "Attached Files"
1436
- ] }),
1437
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1", children: files.map((file, index) => /* @__PURE__ */ jsxRuntime.jsx(
1501
+ ),
1502
+ /* @__PURE__ */ jsxRuntime.jsxs(
1438
1503
  "span",
1439
1504
  {
1440
- className: "inline-flex items-center px-2 py-0.5 rounded-lg bg-[var(--ash-accent-foreground)]/10 text-xs",
1441
- title: file,
1442
- children: file.split(" (")[0]
1443
- },
1444
- index
1445
- )) })
1446
- ] }),
1447
- entry.timestamp && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-white/40 mt-2", children: formatTimestamp(entry.timestamp) })
1448
- ] }),
1449
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-full bg-white/10 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(UserIcon, { className: "w-4 h-4 text-white/50" }) })
1450
- ] });
1451
- }
1452
- function AssistantMessage({
1453
- entry,
1454
- onOptionSelect,
1455
- richContentRenderers,
1456
- className
1457
- }) {
1458
- const parsedOptions = !entry.richContent ? parseOptionsFromContent(entry.content) : null;
1459
- const handleOptionSelect = (option) => {
1460
- if (onOptionSelect) {
1461
- onOptionSelect(`Option ${option.id}: ${option.label}`);
1462
- }
1463
- };
1464
- const renderContent = (textContent) => {
1465
- if (entry.richContent && entry.richContent.length > 0) {
1466
- return /* @__PURE__ */ jsxRuntime.jsx(
1467
- RichContentRenderer,
1468
- {
1469
- content: entry.richContent,
1470
- renderers: richContentRenderers
1471
- }
1472
- );
1473
- }
1474
- return /* @__PURE__ */ jsxRuntime.jsx(LazyMarkdown, { children: textContent || entry.content });
1475
- };
1476
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex gap-3 ash-animate-fade-in", className), children: [
1477
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-full bg-[var(--ash-accent)]/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className: "w-4 h-4 text-[var(--ash-accent)]" }) }),
1478
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 max-w-[85%]", children: [
1479
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ash-card-glass rounded-2xl p-4", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ash-message-content prose prose-sm prose-invert max-w-none text-sm leading-relaxed", children: parsedOptions ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1480
- parsedOptions.preamble && renderContent(parsedOptions.preamble),
1505
+ className: cn(
1506
+ "flex-1 font-medium",
1507
+ "text-[var(--ash-font-size-sm,12px)]",
1508
+ isStreaming ? "text-[var(--ash-thinking-text,rgb(196,181,253))]" : "text-[var(--ash-text-secondary,rgba(255,255,255,0.7))]"
1509
+ ),
1510
+ children: [
1511
+ label,
1512
+ isStreaming && "..."
1513
+ ]
1514
+ }
1515
+ ),
1516
+ showDuration && duration > 0 && /* @__PURE__ */ jsxRuntime.jsx(
1517
+ "span",
1518
+ {
1519
+ className: cn(
1520
+ "text-[var(--ash-font-size-xs,10px)]",
1521
+ "text-[var(--ash-text-muted,rgba(255,255,255,0.5))]"
1522
+ ),
1523
+ children: formatDuration(duration)
1524
+ }
1525
+ ),
1481
1526
  /* @__PURE__ */ jsxRuntime.jsx(
1482
- OptionCards,
1527
+ "svg",
1483
1528
  {
1484
- options: parsedOptions.options,
1485
- onSelect: handleOptionSelect
1529
+ className: cn(
1530
+ "w-4 h-4 text-[var(--ash-text-muted,rgba(255,255,255,0.5))]",
1531
+ "transition-transform",
1532
+ isOpen && "rotate-180"
1533
+ ),
1534
+ fill: "none",
1535
+ stroke: "currentColor",
1536
+ viewBox: "0 0 24 24",
1537
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1538
+ "path",
1539
+ {
1540
+ strokeLinecap: "round",
1541
+ strokeLinejoin: "round",
1542
+ strokeWidth: 2,
1543
+ d: "M19 9l-7 7-7-7"
1544
+ }
1545
+ )
1486
1546
  }
1487
1547
  )
1488
- ] }) : renderContent() }) }),
1489
- entry.timestamp && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-white/40 mt-2", children: formatTimestamp(entry.timestamp) })
1490
- ] })
1491
- ] });
1492
- }
1493
- function ThinkingMessage({ entry, className }) {
1494
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex gap-3 ash-animate-fade-in", className), children: [
1495
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-full bg-purple-500/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(SparklesIcon, { className: "w-4 h-4 text-purple-400" }) }),
1496
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 max-w-[85%]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl p-4 bg-purple-500/10 border border-purple-500/30", children: [
1497
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-purple-400 mb-2 font-medium", children: "Thinking" }),
1498
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-purple-300 italic opacity-80 whitespace-pre-wrap leading-relaxed", children: entry.content })
1499
- ] }) })
1500
- ] });
1501
- }
1502
- function ToolCallMessage({ entry, defaultExpanded = false, className }) {
1503
- if (entry.entryType.type !== "tool_call") return null;
1504
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex gap-3 ash-animate-fade-in", className), children: [
1505
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-full bg-[var(--ash-accent)]/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className: "w-4 h-4 text-[var(--ash-accent)]" }) }),
1506
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(ToolCallCard, { toolCall: entry.entryType.toolCall, defaultExpanded }) })
1507
- ] });
1508
- }
1509
- function ErrorMessage({ entry, className }) {
1510
- if (entry.entryType.type !== "error") return null;
1511
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex gap-3 ash-animate-fade-in", className), children: [
1512
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-full bg-red-500/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(AlertTriangleIcon, { className: "w-4 h-4 text-red-400" }) }),
1513
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 max-w-[85%]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-2xl p-4 bg-red-500/10 border border-red-500/30", children: [
1514
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-red-400 mb-2 font-medium", children: "Error" }),
1515
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-red-300", children: entry.entryType.message }),
1516
- entry.entryType.code && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-red-400/70 mt-2 font-mono", children: [
1517
- "Code: ",
1518
- entry.entryType.code
1519
- ] })
1520
- ] }) })
1521
- ] });
1548
+ ]
1549
+ }
1550
+ );
1522
1551
  }
1523
- function MessageEntry({
1524
- entry,
1525
- onOptionSelect,
1526
- defaultExpanded,
1527
- richContentRenderers,
1528
- className
1552
+ function ReasoningContent({
1553
+ children,
1554
+ className,
1555
+ maxHeight = 300
1529
1556
  }) {
1530
- switch (entry.entryType.type) {
1531
- case "user_message":
1532
- return /* @__PURE__ */ jsxRuntime.jsx(UserMessage, { entry, className });
1533
- case "assistant_message":
1534
- return /* @__PURE__ */ jsxRuntime.jsx(
1535
- AssistantMessage,
1557
+ const { isOpen, isStreaming } = useReasoning();
1558
+ if (!isOpen) return null;
1559
+ return /* @__PURE__ */ jsxRuntime.jsx(
1560
+ "div",
1561
+ {
1562
+ className: cn(
1563
+ "ash-reasoning-content",
1564
+ "border-t border-[var(--ash-border-subtle,rgba(255,255,255,0.04))]",
1565
+ "ash-accordion-content",
1566
+ className
1567
+ ),
1568
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1569
+ "div",
1536
1570
  {
1537
- entry,
1538
- onOptionSelect,
1539
- richContentRenderers,
1540
- className
1571
+ className: cn(
1572
+ "p-3 overflow-auto",
1573
+ "text-[var(--ash-font-size-sm,12px)]",
1574
+ "text-[var(--ash-text-secondary,rgba(255,255,255,0.7))]",
1575
+ "whitespace-pre-wrap font-mono",
1576
+ isStreaming && "animate-pulse"
1577
+ ),
1578
+ style: { maxHeight },
1579
+ children
1541
1580
  }
1542
- );
1543
- case "thinking":
1544
- return /* @__PURE__ */ jsxRuntime.jsx(ThinkingMessage, { entry, className });
1545
- case "tool_call":
1546
- return /* @__PURE__ */ jsxRuntime.jsx(ToolCallMessage, { entry, defaultExpanded, className });
1547
- case "error":
1548
- return /* @__PURE__ */ jsxRuntime.jsx(ErrorMessage, { entry, className });
1549
- default:
1550
- return null;
1551
- }
1552
- }
1553
- function LoadingIndicator({ variant = "dots", size = "md", className }) {
1554
- if (variant === "dots") {
1555
- const dotSizes = {
1556
- sm: "w-1 h-1",
1557
- md: "w-1.5 h-1.5",
1558
- lg: "w-2 h-2"
1559
- };
1560
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex items-center gap-1", className), children: [
1561
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("rounded-full bg-[var(--ash-accent)] animate-pulse", dotSizes[size]) }),
1562
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("rounded-full bg-[var(--ash-accent)] animate-pulse", dotSizes[size]), style: { animationDelay: "150ms" } }),
1563
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("rounded-full bg-[var(--ash-accent)] animate-pulse", dotSizes[size]), style: { animationDelay: "300ms" } })
1564
- ] });
1565
- }
1566
- if (variant === "pulse") {
1567
- const dotSizes = {
1568
- sm: "w-1.5 h-1.5",
1569
- md: "w-2 h-2",
1570
- lg: "w-3 h-3"
1571
- };
1572
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex items-center gap-1", className), children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("bg-[var(--ash-accent)] rounded-full animate-pulse", dotSizes[size]) }) });
1573
- }
1574
- if (variant === "cursor") {
1575
- const cursorSizes = {
1576
- sm: "w-1 h-3",
1577
- md: "w-1.5 h-4",
1578
- lg: "w-2 h-5"
1579
- };
1580
- return /* @__PURE__ */ jsxRuntime.jsx(
1581
- "span",
1582
- {
1583
- className: cn(
1584
- "inline-block bg-[var(--ash-accent)]/50 ash-tool-status-pending",
1585
- cursorSizes[size],
1586
- className
1587
- )
1588
- }
1589
- );
1590
- }
1591
- const spinnerSizes = {
1592
- sm: "w-4 h-4",
1593
- md: "w-6 h-6",
1594
- lg: "w-8 h-8"
1595
- };
1596
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("animate-spin text-[var(--ash-accent)]", spinnerSizes[size], className), children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1597
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10", className: "opacity-25" }),
1598
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2a10 10 0 0 1 10 10", strokeLinecap: "round" })
1599
- ] }) });
1581
+ )
1582
+ }
1583
+ );
1600
1584
  }
1601
- function StreamingText({
1602
- content,
1603
- isStreaming = false,
1604
- renderMarkdown = true,
1585
+ function ThinkingIndicator({
1586
+ isActive = true,
1587
+ label = "Thinking",
1605
1588
  className
1606
1589
  }) {
1607
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("relative", className), children: renderMarkdown ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ash-message-content prose prose-sm prose-invert max-w-none text-sm leading-relaxed", children: [
1608
- /* @__PURE__ */ jsxRuntime.jsx(LazyMarkdown, { children: content }),
1609
- isStreaming && /* @__PURE__ */ jsxRuntime.jsx(LoadingIndicator, { variant: "cursor", size: "sm", className: "inline-block ml-0.5" })
1610
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "whitespace-pre-wrap text-sm leading-relaxed", children: [
1611
- content,
1612
- isStreaming && /* @__PURE__ */ jsxRuntime.jsx(LoadingIndicator, { variant: "cursor", size: "sm", className: "inline-block ml-0.5" })
1613
- ] }) });
1614
- }
1615
- function getFilePath(actionType) {
1616
- switch (actionType.action) {
1617
- case "file_read":
1618
- case "file_edit":
1619
- case "file_write":
1620
- return actionType.path;
1621
- default:
1622
- return null;
1623
- }
1624
- }
1625
- function getFileName(path) {
1626
- const parts = path.split("/");
1627
- return parts[parts.length - 1] || path;
1628
- }
1629
- function getFileExtension(path) {
1630
- const fileName = getFileName(path);
1631
- const dotIndex = fileName.lastIndexOf(".");
1632
- if (dotIndex === -1) return null;
1633
- return fileName.slice(dotIndex + 1).toLowerCase();
1634
- }
1635
- function getDiffStats(actionType) {
1636
- switch (actionType.action) {
1637
- case "file_edit": {
1638
- const edit = actionType;
1639
- if (edit.linesAdded !== void 0 || edit.linesRemoved !== void 0) {
1640
- return { added: edit.linesAdded, removed: edit.linesRemoved };
1641
- }
1642
- return null;
1643
- }
1644
- case "file_read": {
1645
- const read = actionType;
1646
- if (read.linesRead !== void 0) {
1647
- return { read: read.linesRead };
1648
- }
1649
- return null;
1650
- }
1651
- case "file_write": {
1652
- const write = actionType;
1653
- if (write.linesWritten !== void 0) {
1654
- return { written: write.linesWritten };
1655
- }
1656
- return null;
1657
- }
1658
- default:
1659
- return null;
1660
- }
1661
- }
1662
- function getFileTypeColor(ext) {
1663
- switch (ext) {
1664
- case "ts":
1665
- case "tsx":
1666
- return "text-blue-400";
1667
- case "js":
1668
- case "jsx":
1669
- return "text-yellow-400";
1670
- case "md":
1671
- return "text-white/60";
1672
- case "json":
1673
- return "text-orange-400";
1674
- case "sh":
1675
- return "text-green-400";
1676
- case "css":
1677
- case "scss":
1678
- return "text-pink-400";
1679
- case "py":
1680
- return "text-blue-300";
1681
- default:
1682
- return "text-white/70";
1683
- }
1684
- }
1685
- function CompactToolRow({ toolCall, showFullPath = false, className }) {
1686
- const { actionType, status, summary } = toolCall;
1687
- const label = getActionLabel(actionType);
1688
- const filePath = getFilePath(actionType);
1689
- const diffStats = getDiffStats(actionType);
1690
- const displayPath = filePath ? showFullPath ? filePath : getFileName(filePath) : null;
1691
- const ext = filePath ? getFileExtension(filePath) : null;
1692
- const fileColor = getFileTypeColor(ext);
1693
- const showSummary = !filePath && summary;
1590
+ if (!isActive) return null;
1694
1591
  return /* @__PURE__ */ jsxRuntime.jsxs(
1695
1592
  "div",
1696
1593
  {
1697
1594
  className: cn(
1698
- "flex items-center gap-2 py-1.5 text-sm min-w-0",
1595
+ "ash-thinking-indicator",
1596
+ "flex items-center gap-2",
1597
+ "px-3 py-2",
1598
+ "text-[var(--ash-font-size-sm,12px)]",
1599
+ "text-[var(--ash-thinking-text,rgb(196,181,253))]",
1699
1600
  className
1700
1601
  ),
1701
1602
  children: [
1702
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 shrink-0", children: [
1703
- /* @__PURE__ */ jsxRuntime.jsx(
1704
- ActionIcon,
1705
- {
1706
- actionType,
1707
- className: cn(
1708
- "w-3.5 h-3.5",
1709
- status === "pending" ? "text-yellow-400" : status === "failed" ? "text-red-400" : "text-white/50"
1710
- )
1711
- }
1712
- ),
1713
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(
1714
- "font-medium",
1715
- status === "pending" ? "text-white/90" : status === "failed" ? "text-red-400" : "text-white/60"
1716
- ), children: label })
1603
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex gap-1", children: [
1604
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-1.5 h-1.5 bg-current rounded-full animate-bounce", style: { animationDelay: "0ms" } }),
1605
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-1.5 h-1.5 bg-current rounded-full animate-bounce", style: { animationDelay: "150ms" } }),
1606
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-1.5 h-1.5 bg-current rounded-full animate-bounce", style: { animationDelay: "300ms" } })
1717
1607
  ] }),
1718
- displayPath && /* @__PURE__ */ jsxRuntime.jsx("code", { className: cn(
1719
- "px-1.5 py-0.5 rounded bg-white/5 font-mono text-xs truncate max-w-[200px]",
1720
- fileColor
1721
- ), children: displayPath }),
1722
- diffStats && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1 text-xs shrink-0 font-mono", children: [
1723
- diffStats.added !== void 0 && diffStats.added > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-emerald-400", children: [
1724
- "+",
1725
- diffStats.added
1726
- ] }),
1727
- diffStats.removed !== void 0 && diffStats.removed > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-red-400", children: [
1728
- "-",
1729
- diffStats.removed
1730
- ] }),
1731
- diffStats.read !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-white/40", children: [
1732
- diffStats.read,
1733
- " lines"
1734
- ] }),
1735
- diffStats.written !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-emerald-400", children: [
1736
- "+",
1737
- diffStats.written
1738
- ] })
1739
- ] }),
1740
- showSummary && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-white/40 truncate min-w-0 text-xs", children: summary })
1608
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: label })
1741
1609
  ]
1742
1610
  }
1743
1611
  );
1744
1612
  }
1745
- function getFileExtension2(path) {
1746
- const fileName = path.split("/").pop() || path;
1747
- const dotIndex = fileName.lastIndexOf(".");
1748
- if (dotIndex === -1) return null;
1749
- return fileName.slice(dotIndex + 1).toLowerCase();
1750
- }
1751
- function getFileIcon(ext) {
1752
- switch (ext) {
1753
- case "ts":
1754
- case "tsx":
1755
- return "TS";
1756
- case "js":
1757
- case "jsx":
1758
- return "JS";
1759
- case "md":
1760
- return "MD";
1761
- case "json":
1762
- return "{}";
1763
- case "sh":
1764
- return "$";
1765
- case "css":
1766
- case "scss":
1767
- return "#";
1768
- case "py":
1769
- return "PY";
1770
- default:
1771
- return "";
1772
- }
1773
- }
1774
- function getFileBgColor(ext) {
1775
- switch (ext) {
1776
- case "ts":
1777
- case "tsx":
1778
- return "bg-blue-500/20";
1779
- case "js":
1780
- case "jsx":
1781
- return "bg-yellow-500/20";
1782
- case "md":
1783
- return "bg-white/10";
1784
- case "json":
1785
- return "bg-orange-500/20";
1786
- case "sh":
1787
- return "bg-green-500/20";
1788
- case "css":
1789
- case "scss":
1790
- return "bg-pink-500/20";
1791
- case "py":
1792
- return "bg-blue-400/20";
1793
- default:
1794
- return "bg-white/10";
1613
+ var TaskContext = react.createContext(null);
1614
+ function useTask() {
1615
+ const context = react.useContext(TaskContext);
1616
+ if (!context) {
1617
+ throw new Error("useTask must be used within a Task");
1795
1618
  }
1619
+ return context;
1796
1620
  }
1797
- function FileBadge({
1798
- path,
1799
- linesAdded,
1800
- linesRemoved,
1801
- showOnlyFilename = true,
1621
+ function Task({
1622
+ children,
1623
+ defaultOpen = false,
1802
1624
  className
1803
1625
  }) {
1804
- const fileName = showOnlyFilename ? path.split("/").pop() || path : path;
1805
- const ext = getFileExtension2(path);
1806
- const icon = getFileIcon(ext);
1807
- const bgColor = getFileBgColor(ext);
1808
- const hasDiff = linesAdded !== void 0 && linesAdded > 0 || linesRemoved !== void 0 && linesRemoved > 0;
1626
+ const [isOpen, setIsOpen] = react.useState(defaultOpen);
1627
+ const contextValue = {
1628
+ isOpen,
1629
+ setIsOpen
1630
+ };
1631
+ return /* @__PURE__ */ jsxRuntime.jsx(TaskContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
1632
+ "div",
1633
+ {
1634
+ className: cn(
1635
+ "ash-task",
1636
+ "rounded-[var(--ash-radius-md,0.5rem)]",
1637
+ "border border-[var(--ash-border,rgba(255,255,255,0.08))]",
1638
+ "bg-[var(--ash-surface-dark,#0a0a0a)]",
1639
+ "overflow-hidden",
1640
+ className
1641
+ ),
1642
+ children
1643
+ }
1644
+ ) });
1645
+ }
1646
+ function TaskTrigger({ children, className }) {
1647
+ const { isOpen, setIsOpen } = useTask();
1809
1648
  return /* @__PURE__ */ jsxRuntime.jsxs(
1810
- "span",
1649
+ "button",
1811
1650
  {
1651
+ onClick: () => setIsOpen(!isOpen),
1812
1652
  className: cn(
1813
- "inline-flex items-center gap-1.5 px-2 py-0.5 rounded-md text-xs font-mono",
1814
- bgColor,
1653
+ "ash-task-trigger",
1654
+ "w-full flex items-center gap-2",
1655
+ "px-3 py-2",
1656
+ "text-left",
1657
+ "cursor-pointer hover:bg-white/[0.03] transition-colors",
1815
1658
  className
1816
1659
  ),
1817
1660
  children: [
1818
- icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] opacity-60 font-semibold", children: icon }),
1819
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-white/80 truncate max-w-[120px]", children: fileName }),
1820
- hasDiff && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-0.5", children: [
1821
- linesAdded !== void 0 && linesAdded > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-emerald-400", children: [
1822
- "+",
1823
- linesAdded
1824
- ] }),
1825
- linesRemoved !== void 0 && linesRemoved > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-red-400", children: [
1826
- "-",
1827
- linesRemoved
1828
- ] })
1829
- ] })
1661
+ /* @__PURE__ */ jsxRuntime.jsx(
1662
+ "svg",
1663
+ {
1664
+ className: "w-4 h-4 text-[var(--ash-text-muted,rgba(255,255,255,0.5))]",
1665
+ fill: "none",
1666
+ stroke: "currentColor",
1667
+ viewBox: "0 0 24 24",
1668
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1669
+ "path",
1670
+ {
1671
+ strokeLinecap: "round",
1672
+ strokeLinejoin: "round",
1673
+ strokeWidth: 2,
1674
+ d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"
1675
+ }
1676
+ )
1677
+ }
1678
+ ),
1679
+ /* @__PURE__ */ jsxRuntime.jsx(
1680
+ "span",
1681
+ {
1682
+ className: cn(
1683
+ "flex-1 font-medium",
1684
+ "text-[var(--ash-font-size-sm,12px)]",
1685
+ "text-[var(--ash-text-primary,rgba(255,255,255,0.9))]"
1686
+ ),
1687
+ children
1688
+ }
1689
+ ),
1690
+ /* @__PURE__ */ jsxRuntime.jsx(
1691
+ "svg",
1692
+ {
1693
+ className: cn(
1694
+ "w-4 h-4 text-[var(--ash-text-muted,rgba(255,255,255,0.5))]",
1695
+ "transition-transform",
1696
+ isOpen && "rotate-180"
1697
+ ),
1698
+ fill: "none",
1699
+ stroke: "currentColor",
1700
+ viewBox: "0 0 24 24",
1701
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1702
+ "path",
1703
+ {
1704
+ strokeLinecap: "round",
1705
+ strokeLinejoin: "round",
1706
+ strokeWidth: 2,
1707
+ d: "M19 9l-7 7-7-7"
1708
+ }
1709
+ )
1710
+ }
1711
+ )
1830
1712
  ]
1831
1713
  }
1832
1714
  );
1833
1715
  }
1834
- function extractFileChanges(toolCalls) {
1835
- const fileMap = /* @__PURE__ */ new Map();
1836
- for (const tc of toolCalls) {
1837
- const { actionType } = tc;
1838
- if (actionType.action === "file_edit") {
1839
- const edit = actionType;
1840
- const existing = fileMap.get(edit.path);
1841
- if (existing) {
1842
- existing.linesAdded = (existing.linesAdded || 0) + (edit.linesAdded || 0);
1843
- existing.linesRemoved = (existing.linesRemoved || 0) + (edit.linesRemoved || 0);
1844
- } else {
1845
- fileMap.set(edit.path, {
1846
- path: edit.path,
1847
- linesAdded: edit.linesAdded,
1848
- linesRemoved: edit.linesRemoved
1849
- });
1850
- }
1851
- } else if (actionType.action === "file_write") {
1852
- const write = actionType;
1853
- if (!fileMap.has(write.path)) {
1854
- fileMap.set(write.path, {
1855
- path: write.path,
1856
- linesAdded: write.linesWritten
1857
- });
1858
- }
1716
+ function TaskContent({ children, className }) {
1717
+ const { isOpen } = useTask();
1718
+ if (!isOpen) return null;
1719
+ return /* @__PURE__ */ jsxRuntime.jsx(
1720
+ "div",
1721
+ {
1722
+ className: cn(
1723
+ "ash-task-content",
1724
+ "border-t border-[var(--ash-border-subtle,rgba(255,255,255,0.04))]",
1725
+ "ash-accordion-content",
1726
+ className
1727
+ ),
1728
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 space-y-1", children })
1859
1729
  }
1860
- }
1861
- return Array.from(fileMap.values());
1862
- }
1863
- function countActionTypes(toolCalls) {
1864
- const counts = {};
1865
- for (const tc of toolCalls) {
1866
- const action = tc.actionType.action;
1867
- counts[action] = (counts[action] || 0) + 1;
1868
- }
1869
- return counts;
1870
- }
1871
- function getActionIconComponent(action) {
1872
- switch (action) {
1873
- case "file_read":
1874
- return FileIcon;
1875
- case "file_edit":
1876
- case "file_write":
1877
- return EditIcon;
1878
- case "command_run":
1879
- return TerminalIcon;
1880
- case "search":
1881
- case "glob":
1882
- return SearchIcon;
1883
- default:
1884
- return null;
1885
- }
1730
+ );
1886
1731
  }
1887
- function ToolExecutionGroup({
1888
- toolCalls,
1889
- defaultExpanded = false,
1732
+ function TaskItem({
1733
+ children,
1734
+ status,
1735
+ activeForm,
1890
1736
  className
1891
1737
  }) {
1892
- const [expanded, setExpanded] = react.useState(defaultExpanded);
1893
- const [expandedCardId, setExpandedCardId] = react.useState(null);
1894
- const fileChanges = react.useMemo(() => extractFileChanges(toolCalls), [toolCalls]);
1895
- const actionCounts = react.useMemo(() => countActionTypes(toolCalls), [toolCalls]);
1896
- const displayActions = react.useMemo(() => {
1897
- return Object.entries(actionCounts).sort((a, b) => b[1] - a[1]).slice(0, 3).map(([action]) => action);
1898
- }, [actionCounts]);
1899
- const totalCount = toolCalls.length;
1900
- if (toolCalls.length === 0) {
1901
- return null;
1902
- }
1903
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("ash-animate-fade-in", className), children: [
1904
- /* @__PURE__ */ jsxRuntime.jsxs(
1905
- "button",
1738
+ const statusIcons = {
1739
+ pending: /* @__PURE__ */ jsxRuntime.jsx(
1740
+ "svg",
1906
1741
  {
1907
- onClick: () => setExpanded(!expanded),
1908
- className: "w-full flex items-center gap-2 py-1 text-left group",
1742
+ className: "w-4 h-4 text-[var(--ash-text-muted,rgba(255,255,255,0.5))]",
1743
+ fill: "none",
1744
+ stroke: "currentColor",
1745
+ viewBox: "0 0 24 24",
1746
+ children: /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10", strokeWidth: 2 })
1747
+ }
1748
+ ),
1749
+ in_progress: /* @__PURE__ */ jsxRuntime.jsxs(
1750
+ "svg",
1751
+ {
1752
+ className: "w-4 h-4 text-yellow-400 animate-spin",
1753
+ fill: "none",
1754
+ viewBox: "0 0 24 24",
1909
1755
  children: [
1910
1756
  /* @__PURE__ */ jsxRuntime.jsx(
1911
- ChevronRightIcon,
1757
+ "circle",
1912
1758
  {
1913
- className: cn(
1914
- "w-3.5 h-3.5 text-white/40 transition-transform duration-200 shrink-0",
1915
- expanded && "rotate-90"
1916
- )
1759
+ className: "opacity-25",
1760
+ cx: "12",
1761
+ cy: "12",
1762
+ r: "10",
1763
+ stroke: "currentColor",
1764
+ strokeWidth: "4"
1917
1765
  }
1918
1766
  ),
1919
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-white/60", children: [
1920
- totalCount,
1921
- " tool call",
1922
- totalCount !== 1 ? "s" : ""
1923
- ] }),
1924
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: displayActions.map((action) => {
1925
- const IconComponent = getActionIconComponent(action);
1926
- if (!IconComponent) return null;
1927
- return /* @__PURE__ */ jsxRuntime.jsx(
1928
- IconComponent,
1929
- {
1930
- className: "w-3.5 h-3.5 text-white/30"
1931
- },
1932
- action
1933
- );
1934
- }) }),
1935
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1" }),
1936
- !expanded && fileChanges.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 flex-wrap justify-end", children: [
1937
- fileChanges.slice(0, 4).map((fc) => /* @__PURE__ */ jsxRuntime.jsx(
1938
- FileBadge,
1939
- {
1940
- path: fc.path,
1941
- linesAdded: fc.linesAdded,
1942
- linesRemoved: fc.linesRemoved
1943
- },
1944
- fc.path
1945
- )),
1946
- fileChanges.length > 4 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-white/40", children: [
1947
- "+",
1948
- fileChanges.length - 4,
1949
- " more"
1950
- ] })
1951
- ] })
1767
+ /* @__PURE__ */ jsxRuntime.jsx(
1768
+ "path",
1769
+ {
1770
+ className: "opacity-75",
1771
+ fill: "currentColor",
1772
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
1773
+ }
1774
+ )
1952
1775
  ]
1953
1776
  }
1954
1777
  ),
1955
- expanded && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pl-5 border-l border-white/10 ml-1.5 mt-1 space-y-0.5", children: toolCalls.map((toolCall) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: expandedCardId === toolCall.id ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "py-1", children: [
1956
- /* @__PURE__ */ jsxRuntime.jsx(
1957
- ToolCallCard,
1958
- {
1959
- toolCall,
1960
- defaultExpanded: true
1961
- }
1962
- ),
1963
- /* @__PURE__ */ jsxRuntime.jsx(
1964
- "button",
1965
- {
1966
- onClick: () => setExpandedCardId(null),
1967
- className: "text-xs text-white/40 hover:text-white/60 mt-1 pl-1",
1968
- children: "Collapse"
1969
- }
1970
- )
1971
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(
1972
- "button",
1778
+ completed: /* @__PURE__ */ jsxRuntime.jsx(
1779
+ "svg",
1973
1780
  {
1974
- onClick: () => setExpandedCardId(toolCall.id),
1975
- className: "w-full text-left hover:bg-white/5 rounded px-1 -mx-1 transition-colors",
1976
- children: /* @__PURE__ */ jsxRuntime.jsx(CompactToolRow, { toolCall })
1977
- }
1978
- ) }, toolCall.id)) })
1979
- ] });
1980
- }
1981
- function extractFileChanges2(toolCalls) {
1982
- const fileMap = /* @__PURE__ */ new Map();
1983
- for (const tc of toolCalls) {
1984
- const { actionType } = tc;
1985
- if (actionType.action === "file_edit") {
1986
- const edit = actionType;
1987
- const existing = fileMap.get(edit.path);
1988
- if (existing) {
1989
- existing.linesAdded = (existing.linesAdded || 0) + (edit.linesAdded || 0);
1990
- existing.linesRemoved = (existing.linesRemoved || 0) + (edit.linesRemoved || 0);
1991
- } else {
1992
- fileMap.set(edit.path, {
1993
- path: edit.path,
1994
- linesAdded: edit.linesAdded,
1995
- linesRemoved: edit.linesRemoved
1996
- });
1997
- }
1998
- } else if (actionType.action === "file_write") {
1999
- if (!fileMap.has(actionType.path)) {
2000
- fileMap.set(actionType.path, {
2001
- path: actionType.path,
2002
- linesAdded: actionType.linesWritten
2003
- });
1781
+ className: "w-4 h-4 text-[var(--ash-accent,#ccff00)]",
1782
+ fill: "none",
1783
+ stroke: "currentColor",
1784
+ viewBox: "0 0 24 24",
1785
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1786
+ "path",
1787
+ {
1788
+ strokeLinecap: "round",
1789
+ strokeLinejoin: "round",
1790
+ strokeWidth: 2,
1791
+ d: "M5 13l4 4L19 7"
1792
+ }
1793
+ )
2004
1794
  }
1795
+ )
1796
+ };
1797
+ const statusColors = {
1798
+ pending: "text-[var(--ash-text-muted,rgba(255,255,255,0.5))]",
1799
+ in_progress: "text-[var(--ash-text-primary,rgba(255,255,255,0.9))]",
1800
+ completed: "text-[var(--ash-text-secondary,rgba(255,255,255,0.7))] line-through"
1801
+ };
1802
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1803
+ "div",
1804
+ {
1805
+ className: cn(
1806
+ "ash-task-item",
1807
+ "flex items-center gap-2",
1808
+ "px-2 py-1.5 rounded-md",
1809
+ status === "in_progress" && "bg-yellow-400/10",
1810
+ className
1811
+ ),
1812
+ "data-task-status": status,
1813
+ children: [
1814
+ statusIcons[status],
1815
+ /* @__PURE__ */ jsxRuntime.jsx(
1816
+ "span",
1817
+ {
1818
+ className: cn(
1819
+ "flex-1",
1820
+ "text-[var(--ash-font-size-sm,12px)]",
1821
+ statusColors[status]
1822
+ ),
1823
+ children: status === "in_progress" && activeForm ? activeForm : children
1824
+ }
1825
+ )
1826
+ ]
2005
1827
  }
2006
- }
2007
- return Array.from(fileMap.values());
2008
- }
2009
- function countActionTypes2(toolCalls) {
2010
- const counts = {};
2011
- for (const tc of toolCalls) {
2012
- const action = tc.actionType.action;
2013
- counts[action] = (counts[action] || 0) + 1;
2014
- }
2015
- return counts;
2016
- }
2017
- function getActionIconComponent2(action) {
2018
- switch (action) {
2019
- case "file_read":
2020
- return FileIcon;
2021
- case "file_edit":
2022
- case "file_write":
2023
- return EditIcon;
2024
- case "command_run":
2025
- return TerminalIcon;
2026
- case "search":
2027
- case "glob":
2028
- return SearchIcon;
2029
- default:
2030
- return null;
2031
- }
1828
+ );
2032
1829
  }
2033
- function StepAccordion({
2034
- toolCalls,
2035
- defaultExpanded = false,
2036
- isExpanded: controlledExpanded,
2037
- onToggle,
2038
- className
1830
+ function TaskList({
1831
+ items,
1832
+ defaultOpen = true,
1833
+ className,
1834
+ title
2039
1835
  }) {
2040
- const [internalExpanded, setInternalExpanded] = react.useState(defaultExpanded);
2041
- const isExpanded = controlledExpanded !== void 0 ? controlledExpanded : internalExpanded;
2042
- const handleToggle = react.useCallback(() => {
2043
- if (onToggle) {
2044
- onToggle();
2045
- } else {
2046
- setInternalExpanded((prev) => !prev);
2047
- }
2048
- }, [onToggle]);
2049
- const fileChanges = react.useMemo(() => extractFileChanges2(toolCalls), [toolCalls]);
2050
- const actionCounts = react.useMemo(() => countActionTypes2(toolCalls), [toolCalls]);
2051
- const displayActions = react.useMemo(() => {
2052
- return Object.entries(actionCounts).sort((a, b) => b[1] - a[1]).slice(0, 3).map(([action]) => action);
2053
- }, [actionCounts]);
2054
- if (toolCalls.length === 0) {
2055
- return null;
2056
- }
2057
- const totalCount = toolCalls.length;
2058
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("ash-animate-fade-in", className), children: [
2059
- /* @__PURE__ */ jsxRuntime.jsxs(
2060
- "button",
1836
+ const completed = items.filter((t) => t.status === "completed").length;
1837
+ const total = items.length;
1838
+ const displayTitle = title || `Tasks (${completed}/${total})`;
1839
+ return /* @__PURE__ */ jsxRuntime.jsxs(Task, { defaultOpen, className, children: [
1840
+ /* @__PURE__ */ jsxRuntime.jsx(TaskTrigger, { children: displayTitle }),
1841
+ /* @__PURE__ */ jsxRuntime.jsx(TaskContent, { children: items.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
1842
+ TaskItem,
2061
1843
  {
2062
- type: "button",
2063
- onClick: handleToggle,
2064
- className: "w-full flex items-center gap-2 py-1 text-left group",
2065
- children: [
2066
- /* @__PURE__ */ jsxRuntime.jsx(
2067
- ChevronRightIcon,
2068
- {
2069
- className: cn(
2070
- "w-3.5 h-3.5 text-white/40 transition-transform duration-200 shrink-0",
2071
- isExpanded && "rotate-90"
2072
- )
2073
- }
2074
- ),
2075
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-white/60", children: [
2076
- totalCount,
2077
- " tool call",
2078
- totalCount !== 1 ? "s" : ""
2079
- ] }),
2080
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: displayActions.map((action) => {
2081
- const IconComponent = getActionIconComponent2(action);
2082
- if (!IconComponent) return null;
2083
- return /* @__PURE__ */ jsxRuntime.jsx(
2084
- IconComponent,
2085
- {
2086
- className: "w-3.5 h-3.5 text-white/30"
2087
- },
2088
- action
2089
- );
2090
- }) }),
2091
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1" }),
2092
- !isExpanded && fileChanges.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 flex-wrap justify-end", children: [
2093
- fileChanges.slice(0, 4).map((fc) => /* @__PURE__ */ jsxRuntime.jsx(
2094
- FileBadge,
2095
- {
2096
- path: fc.path,
2097
- linesAdded: fc.linesAdded,
2098
- linesRemoved: fc.linesRemoved
2099
- },
2100
- fc.path
2101
- )),
2102
- fileChanges.length > 4 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-white/40", children: [
2103
- "+",
2104
- fileChanges.length - 4,
2105
- " more"
2106
- ] })
2107
- ] })
2108
- ]
2109
- }
2110
- ),
2111
- isExpanded && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pl-5 border-l border-white/10 ml-1.5 mt-1 space-y-0.5", children: toolCalls.map((toolCall) => /* @__PURE__ */ jsxRuntime.jsx(CompactToolRow, { toolCall }, toolCall.id)) })
1844
+ status: item.status,
1845
+ activeForm: item.activeForm,
1846
+ children: item.content
1847
+ },
1848
+ index
1849
+ )) })
2112
1850
  ] });
2113
1851
  }
2114
-
2115
- // src/types.ts
2116
- function isCommandRunAction(action) {
2117
- return action.action === "command_run";
2118
- }
2119
- function isFileReadAction(action) {
2120
- return action.action === "file_read";
2121
- }
2122
- function isFileEditAction(action) {
2123
- return action.action === "file_edit";
2124
- }
2125
- function isFileWriteAction(action) {
2126
- return action.action === "file_write";
2127
- }
2128
- function isSearchAction(action) {
2129
- return action.action === "search";
2130
- }
2131
- function isGlobAction(action) {
2132
- return action.action === "glob";
2133
- }
2134
- function isWebFetchAction(action) {
2135
- return action.action === "web_fetch";
2136
- }
2137
- function isWebSearchAction(action) {
2138
- return action.action === "web_search";
1852
+ var AttachmentContext = react.createContext(null);
1853
+ function useAttachment() {
1854
+ const context = react.useContext(AttachmentContext);
1855
+ if (!context) {
1856
+ throw new Error("useAttachment must be used within an Attachment");
1857
+ }
1858
+ return context;
2139
1859
  }
2140
- function isMcpToolAction(action) {
2141
- return action.action === "mcp_tool";
1860
+ function Attachments({
1861
+ children,
1862
+ layout = "inline",
1863
+ className
1864
+ }) {
1865
+ const layoutStyles = {
1866
+ grid: "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-2",
1867
+ inline: "flex flex-wrap gap-2",
1868
+ list: "flex flex-col gap-1"
1869
+ };
1870
+ return /* @__PURE__ */ jsxRuntime.jsx(
1871
+ "div",
1872
+ {
1873
+ className: cn(
1874
+ "ash-attachments",
1875
+ layoutStyles[layout],
1876
+ className
1877
+ ),
1878
+ "data-layout": layout,
1879
+ children
1880
+ }
1881
+ );
2142
1882
  }
2143
- function isGenericToolAction(action) {
2144
- return action.action === "generic_tool";
1883
+ function Attachment({
1884
+ file,
1885
+ children,
1886
+ removable = false,
1887
+ onRemove,
1888
+ className
1889
+ }) {
1890
+ const contextValue = {
1891
+ file,
1892
+ removable,
1893
+ onRemove
1894
+ };
1895
+ return /* @__PURE__ */ jsxRuntime.jsx(AttachmentContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
1896
+ "div",
1897
+ {
1898
+ className: cn(
1899
+ "ash-attachment relative group",
1900
+ "rounded-[var(--ash-radius-sm,0.375rem)]",
1901
+ "border border-[var(--ash-border,rgba(255,255,255,0.08))]",
1902
+ "bg-[var(--ash-surface-elevated,#111111)]",
1903
+ "overflow-hidden",
1904
+ "hover:border-[var(--ash-border-emphasis,rgba(255,255,255,0.15))]",
1905
+ "transition-colors",
1906
+ className
1907
+ ),
1908
+ "data-file-type": file.type,
1909
+ children: children || /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1910
+ /* @__PURE__ */ jsxRuntime.jsx(AttachmentPreview, {}),
1911
+ /* @__PURE__ */ jsxRuntime.jsx(AttachmentInfo, {}),
1912
+ removable && /* @__PURE__ */ jsxRuntime.jsx(AttachmentRemove, {})
1913
+ ] })
1914
+ }
1915
+ ) });
2145
1916
  }
2146
- function isTodoWriteAction(action) {
2147
- return action.action === "todo_write";
1917
+ function AttachmentPreview({
1918
+ className,
1919
+ height = 80
1920
+ }) {
1921
+ const { file } = useAttachment();
1922
+ const isImage = file.type.startsWith("image/");
1923
+ const url = "url" in file ? file.url : "base64" in file ? `data:${file.type};base64,${file.base64}` : void 0;
1924
+ if (isImage && url) {
1925
+ return /* @__PURE__ */ jsxRuntime.jsx(
1926
+ "div",
1927
+ {
1928
+ className: cn(
1929
+ "ash-attachment-preview",
1930
+ "w-full bg-black/20 overflow-hidden",
1931
+ className
1932
+ ),
1933
+ style: { height },
1934
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1935
+ "img",
1936
+ {
1937
+ src: url,
1938
+ alt: file.name,
1939
+ className: "w-full h-full object-cover"
1940
+ }
1941
+ )
1942
+ }
1943
+ );
1944
+ }
1945
+ const iconForType = () => {
1946
+ if (file.type.includes("pdf")) {
1947
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { className: "w-8 h-8", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [
1948
+ /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" }),
1949
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: "7", y: "16", fontSize: "6", fill: "currentColor", stroke: "none", fontWeight: "bold", children: "PDF" })
1950
+ ] });
1951
+ }
1952
+ if (file.type.includes("text") || file.type.includes("json")) {
1953
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-8 h-8", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" }) });
1954
+ }
1955
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-8 h-8", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" }) });
1956
+ };
1957
+ return /* @__PURE__ */ jsxRuntime.jsx(
1958
+ "div",
1959
+ {
1960
+ className: cn(
1961
+ "ash-attachment-preview",
1962
+ "w-full flex items-center justify-center",
1963
+ "bg-[var(--ash-surface-dark,#0a0a0a)]",
1964
+ "text-[var(--ash-text-muted,rgba(255,255,255,0.5))]",
1965
+ className
1966
+ ),
1967
+ style: { height },
1968
+ children: iconForType()
1969
+ }
1970
+ );
2148
1971
  }
2149
- function isAgentToolAction(action) {
2150
- return action.action === "agent_tool";
1972
+ function AttachmentInfo({
1973
+ className,
1974
+ showSize = true
1975
+ }) {
1976
+ const { file } = useAttachment();
1977
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1978
+ "div",
1979
+ {
1980
+ className: cn(
1981
+ "ash-attachment-info",
1982
+ "px-2 py-1.5",
1983
+ "truncate",
1984
+ className
1985
+ ),
1986
+ children: [
1987
+ /* @__PURE__ */ jsxRuntime.jsx(
1988
+ "div",
1989
+ {
1990
+ className: cn(
1991
+ "text-[var(--ash-font-size-xs,10px)]",
1992
+ "text-[var(--ash-text-primary,rgba(255,255,255,0.9))]",
1993
+ "truncate font-medium"
1994
+ ),
1995
+ title: file.name,
1996
+ children: file.name
1997
+ }
1998
+ ),
1999
+ showSize && /* @__PURE__ */ jsxRuntime.jsx(
2000
+ "div",
2001
+ {
2002
+ className: cn(
2003
+ "text-[var(--ash-font-size-xs,10px)]",
2004
+ "text-[var(--ash-text-muted,rgba(255,255,255,0.5))]"
2005
+ ),
2006
+ children: formatFileSize(file.size)
2007
+ }
2008
+ )
2009
+ ]
2010
+ }
2011
+ );
2151
2012
  }
2152
- function isToolCallEntry(entry) {
2153
- return entry.type === "tool_call";
2013
+ function AttachmentRemove({ className }) {
2014
+ const { onRemove, removable } = useAttachment();
2015
+ if (!removable || !onRemove) return null;
2016
+ return /* @__PURE__ */ jsxRuntime.jsx(
2017
+ "button",
2018
+ {
2019
+ onClick: (e) => {
2020
+ e.stopPropagation();
2021
+ onRemove();
2022
+ },
2023
+ className: cn(
2024
+ "ash-attachment-remove",
2025
+ "absolute top-1 right-1",
2026
+ "w-5 h-5 rounded-full",
2027
+ "flex items-center justify-center",
2028
+ "bg-black/60 text-white/80",
2029
+ "hover:bg-black/80 hover:text-white",
2030
+ "opacity-0 group-hover:opacity-100",
2031
+ "transition-opacity",
2032
+ className
2033
+ ),
2034
+ "aria-label": "Remove attachment",
2035
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
2036
+ }
2037
+ );
2154
2038
  }
2155
- function isErrorEntry(entry) {
2156
- return entry.type === "error";
2039
+ function FileBadgeCompact({
2040
+ file,
2041
+ removable = false,
2042
+ onRemove,
2043
+ className
2044
+ }) {
2045
+ const isImage = file.type.startsWith("image/");
2046
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2047
+ "div",
2048
+ {
2049
+ className: cn(
2050
+ "ash-file-badge group inline-flex items-center gap-1.5",
2051
+ "px-2 py-1 rounded-md",
2052
+ "bg-[var(--ash-surface-elevated,#111111)]",
2053
+ "border border-[var(--ash-border,rgba(255,255,255,0.08))]",
2054
+ "text-[var(--ash-font-size-xs,10px)]",
2055
+ className
2056
+ ),
2057
+ children: [
2058
+ isImage ? /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-3 h-3 text-[var(--ash-text-muted)]", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" }) }) : /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-3 h-3 text-[var(--ash-text-muted)]", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" }) }),
2059
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[var(--ash-text-primary)] truncate max-w-[100px]", children: file.name }),
2060
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[var(--ash-text-muted)]", children: formatFileSize(file.size) }),
2061
+ removable && onRemove && /* @__PURE__ */ jsxRuntime.jsx(
2062
+ "button",
2063
+ {
2064
+ onClick: (e) => {
2065
+ e.stopPropagation();
2066
+ onRemove();
2067
+ },
2068
+ className: cn(
2069
+ "ml-0.5 text-[var(--ash-text-muted)]",
2070
+ "hover:text-red-400 transition-colors",
2071
+ "opacity-0 group-hover:opacity-100"
2072
+ ),
2073
+ "aria-label": "Remove",
2074
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
2075
+ }
2076
+ )
2077
+ ]
2078
+ }
2079
+ );
2157
2080
  }
2158
- function isWidgetEntry(entry) {
2159
- return entry.type === "widget";
2081
+ function Shimmer({
2082
+ children,
2083
+ className,
2084
+ isActive = true
2085
+ }) {
2086
+ if (!isActive) return null;
2087
+ return /* @__PURE__ */ jsxRuntime.jsx(
2088
+ "div",
2089
+ {
2090
+ className: cn(
2091
+ "ash-shimmer",
2092
+ "flex flex-col gap-2",
2093
+ className
2094
+ ),
2095
+ role: "status",
2096
+ "aria-label": "Loading",
2097
+ children: children || /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2098
+ /* @__PURE__ */ jsxRuntime.jsx(ShimmerLine, { width: "100%" }),
2099
+ /* @__PURE__ */ jsxRuntime.jsx(ShimmerLine, { width: "80%" }),
2100
+ /* @__PURE__ */ jsxRuntime.jsx(ShimmerLine, { width: "60%" })
2101
+ ] })
2102
+ }
2103
+ );
2160
2104
  }
2161
- var DEFAULT_DISPLAY_CONFIG = {
2162
- mode: "inline",
2163
- breakEveryNToolCalls: 0,
2164
- defaultExpanded: false,
2165
- animationDuration: 300
2166
- };
2167
- var DisplayModeContext = react.createContext(null);
2168
- function DisplayModeProvider({ children, initialConfig }) {
2169
- const [config, setConfigState] = react.useState({
2170
- ...DEFAULT_DISPLAY_CONFIG,
2171
- ...initialConfig
2172
- });
2173
- const setMode = react.useCallback((mode) => {
2174
- setConfigState((prev) => ({ ...prev, mode }));
2175
- }, []);
2176
- const setConfig = react.useCallback((updates) => {
2177
- setConfigState((prev) => ({ ...prev, ...updates }));
2178
- }, []);
2179
- const toggleMode = react.useCallback(() => {
2180
- setConfigState((prev) => ({
2181
- ...prev,
2182
- mode: prev.mode === "inline" ? "compact" : "inline"
2183
- }));
2184
- }, []);
2185
- const value = react.useMemo(
2186
- () => ({ config, setMode, setConfig, toggleMode }),
2187
- [config, setMode, setConfig, toggleMode]
2105
+ function ShimmerLine({
2106
+ width = "100%",
2107
+ height = 16,
2108
+ className
2109
+ }) {
2110
+ return /* @__PURE__ */ jsxRuntime.jsx(
2111
+ "div",
2112
+ {
2113
+ className: cn(
2114
+ "ash-shimmer-line",
2115
+ "rounded-md",
2116
+ "bg-gradient-to-r from-white/5 via-white/10 to-white/5",
2117
+ "bg-[length:200%_100%]",
2118
+ "animate-[shimmer_1.5s_ease-in-out_infinite]",
2119
+ className
2120
+ ),
2121
+ style: {
2122
+ width: typeof width === "number" ? `${width}px` : width,
2123
+ height
2124
+ }
2125
+ }
2188
2126
  );
2189
- return /* @__PURE__ */ jsxRuntime.jsx(DisplayModeContext.Provider, { value, children });
2190
2127
  }
2191
- function useDisplayMode() {
2192
- const context = react.useContext(DisplayModeContext);
2193
- if (!context) {
2194
- return {
2195
- config: DEFAULT_DISPLAY_CONFIG,
2196
- setMode: () => {
2197
- },
2198
- setConfig: () => {
2199
- },
2200
- toggleMode: () => {
2128
+ function ShimmerBlock({
2129
+ width = "100%",
2130
+ height = 100,
2131
+ rounded = "md",
2132
+ className
2133
+ }) {
2134
+ const roundedStyles = {
2135
+ none: "rounded-none",
2136
+ sm: "rounded-sm",
2137
+ md: "rounded-md",
2138
+ lg: "rounded-lg",
2139
+ full: "rounded-full"
2140
+ };
2141
+ return /* @__PURE__ */ jsxRuntime.jsx(
2142
+ "div",
2143
+ {
2144
+ className: cn(
2145
+ "ash-shimmer-block",
2146
+ roundedStyles[rounded],
2147
+ "bg-gradient-to-r from-white/5 via-white/10 to-white/5",
2148
+ "bg-[length:200%_100%]",
2149
+ "animate-[shimmer_1.5s_ease-in-out_infinite]",
2150
+ className
2151
+ ),
2152
+ style: {
2153
+ width: typeof width === "number" ? `${width}px` : width,
2154
+ height
2201
2155
  }
2202
- };
2203
- }
2204
- return context;
2156
+ }
2157
+ );
2205
2158
  }
2206
- function useDisplayConfig() {
2207
- const { config } = useDisplayMode();
2208
- return config;
2209
- }
2210
- function MessageList({
2211
- entries,
2212
- loading,
2213
- streamingContent,
2214
- displayConfig: displayConfigProp,
2215
- onOptionSelect,
2216
- renderWidget,
2217
- onWidgetAction,
2218
- autoScroll = true,
2219
- richContentRenderers,
2159
+ function ShimmerText({
2160
+ chars = 20,
2220
2161
  className
2221
2162
  }) {
2222
- const contextConfig = useDisplayConfig();
2223
- const config = displayConfigProp || contextConfig;
2224
- const containerRef = react.useRef(null);
2225
- const messagesEndRef = react.useRef(null);
2226
- react.useEffect(() => {
2227
- if (autoScroll && messagesEndRef.current && containerRef.current) {
2228
- messagesEndRef.current.scrollIntoView({ behavior: "smooth", block: "end" });
2229
- }
2230
- }, [entries, streamingContent, loading, autoScroll]);
2231
- const createWidgetActionHandler = react.useCallback(
2232
- (entryId, widgetType) => {
2233
- if (!onWidgetAction) return void 0;
2234
- return (action) => {
2235
- onWidgetAction({
2236
- ...action,
2237
- entryId,
2238
- widgetType
2239
- });
2240
- };
2241
- },
2242
- [onWidgetAction]
2163
+ return /* @__PURE__ */ jsxRuntime.jsx(
2164
+ "span",
2165
+ {
2166
+ className: cn(
2167
+ "ash-shimmer-text inline-block",
2168
+ "rounded",
2169
+ "bg-gradient-to-r from-white/5 via-white/10 to-white/5",
2170
+ "bg-[length:200%_100%]",
2171
+ "animate-[shimmer_1.5s_ease-in-out_infinite]",
2172
+ className
2173
+ ),
2174
+ style: {
2175
+ width: `${chars}ch`,
2176
+ height: "1em",
2177
+ verticalAlign: "middle"
2178
+ }
2179
+ }
2243
2180
  );
2244
- const groupedEntries = react.useMemo(() => {
2245
- if (config.mode === "inline") {
2246
- return entries.map((entry) => ({
2247
- type: "single",
2248
- entry,
2249
- id: entry.id
2250
- }));
2181
+ }
2182
+ function LoadingDots({
2183
+ size = "md",
2184
+ className
2185
+ }) {
2186
+ const sizeStyles = {
2187
+ sm: "w-1 h-1",
2188
+ md: "w-1.5 h-1.5",
2189
+ lg: "w-2 h-2"
2190
+ };
2191
+ const gapStyles = {
2192
+ sm: "gap-0.5",
2193
+ md: "gap-1",
2194
+ lg: "gap-1.5"
2195
+ };
2196
+ return /* @__PURE__ */ jsxRuntime.jsx(
2197
+ "span",
2198
+ {
2199
+ className: cn(
2200
+ "ash-loading-dots inline-flex items-center",
2201
+ gapStyles[size],
2202
+ className
2203
+ ),
2204
+ role: "status",
2205
+ "aria-label": "Loading",
2206
+ children: [0, 1, 2].map((i) => /* @__PURE__ */ jsxRuntime.jsx(
2207
+ "span",
2208
+ {
2209
+ className: cn(
2210
+ sizeStyles[size],
2211
+ "rounded-full",
2212
+ "bg-current",
2213
+ "animate-bounce"
2214
+ ),
2215
+ style: { animationDelay: `${i * 150}ms` }
2216
+ },
2217
+ i
2218
+ ))
2251
2219
  }
2252
- return groupEntriesForCompactMode(entries, config);
2253
- }, [entries, config]);
2254
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, className: cn("flex-1 overflow-y-auto p-4 space-y-4 ash-scrollbar", className), children: [
2255
- groupedEntries.map((groupedEntry) => {
2256
- if (groupedEntry.type === "single") {
2257
- const entry = groupedEntry.entry;
2258
- if (entry.entryType.type === "widget" && renderWidget) {
2259
- const widgetEntry = entry.entryType;
2260
- const widgetContent = renderWidget({
2261
- entry,
2262
- widgetType: widgetEntry.widgetType,
2263
- widgetData: widgetEntry.widgetData,
2264
- onAction: createWidgetActionHandler(entry.id, widgetEntry.widgetType)
2265
- });
2266
- if (widgetContent !== null) {
2267
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ash-animate-fade-in", children: widgetContent }, entry.id);
2268
- }
2269
- }
2270
- return /* @__PURE__ */ jsxRuntime.jsx(
2271
- MessageEntry,
2220
+ );
2221
+ }
2222
+ function LoadingSpinner({
2223
+ size = "md",
2224
+ className
2225
+ }) {
2226
+ const sizeStyles = {
2227
+ sm: "w-4 h-4",
2228
+ md: "w-6 h-6",
2229
+ lg: "w-8 h-8"
2230
+ };
2231
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2232
+ "svg",
2233
+ {
2234
+ className: cn(
2235
+ "ash-loading-spinner animate-spin",
2236
+ sizeStyles[size],
2237
+ className
2238
+ ),
2239
+ fill: "none",
2240
+ viewBox: "0 0 24 24",
2241
+ role: "status",
2242
+ "aria-label": "Loading",
2243
+ children: [
2244
+ /* @__PURE__ */ jsxRuntime.jsx(
2245
+ "circle",
2272
2246
  {
2273
- entry,
2274
- onOptionSelect,
2275
- defaultExpanded: config.defaultExpanded,
2276
- richContentRenderers
2277
- },
2278
- entry.id
2279
- );
2280
- }
2281
- const toolCalls = extractToolCallsFromGroup(groupedEntry.entries);
2282
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 ash-animate-fade-in", children: [
2283
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-full bg-[var(--ash-accent)]/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className: "w-4 h-4 text-[var(--ash-accent)]" }) }),
2284
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: config.mode === "accordion" ? /* @__PURE__ */ jsxRuntime.jsx(
2285
- StepAccordion,
2247
+ className: "opacity-25",
2248
+ cx: "12",
2249
+ cy: "12",
2250
+ r: "10",
2251
+ stroke: "currentColor",
2252
+ strokeWidth: "4"
2253
+ }
2254
+ ),
2255
+ /* @__PURE__ */ jsxRuntime.jsx(
2256
+ "path",
2286
2257
  {
2287
- toolCalls,
2288
- defaultExpanded: config.defaultExpanded
2258
+ className: "opacity-75",
2259
+ fill: "currentColor",
2260
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
2289
2261
  }
2290
- ) : /* @__PURE__ */ jsxRuntime.jsx(
2291
- ToolExecutionGroup,
2262
+ )
2263
+ ]
2264
+ }
2265
+ );
2266
+ }
2267
+ function MessageShimmer({
2268
+ role = "assistant",
2269
+ className
2270
+ }) {
2271
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2272
+ "div",
2273
+ {
2274
+ className: cn(
2275
+ "ash-message-shimmer flex gap-2",
2276
+ role === "user" && "flex-row-reverse",
2277
+ className
2278
+ ),
2279
+ children: [
2280
+ /* @__PURE__ */ jsxRuntime.jsx(
2281
+ ShimmerBlock,
2292
2282
  {
2293
- toolCalls,
2294
- defaultExpanded: config.defaultExpanded,
2295
- animationDuration: config.animationDuration
2283
+ width: 20,
2284
+ height: 20,
2285
+ rounded: "full"
2296
2286
  }
2297
- ) })
2298
- ] }, groupedEntry.id);
2299
- }),
2300
- streamingContent && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 ash-animate-fade-in", children: [
2301
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-full bg-[var(--ash-accent)]/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className: "w-4 h-4 text-[var(--ash-accent)]" }) }),
2302
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 max-w-[85%]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl p-3 bg-white/5 text-white/80", children: /* @__PURE__ */ jsxRuntime.jsx(StreamingText, { content: streamingContent, isStreaming: true }) }) })
2303
- ] }),
2304
- loading && !streamingContent && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 ash-animate-fade-in", children: [
2305
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-full bg-[var(--ash-accent)]/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className: "w-4 h-4 text-[var(--ash-accent)]" }) }),
2306
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl p-3 bg-white/5", children: /* @__PURE__ */ jsxRuntime.jsx(LoadingIndicator, { variant: "dots" }) })
2307
- ] }),
2308
- /* @__PURE__ */ jsxRuntime.jsx("div", { ref: messagesEndRef })
2309
- ] });
2287
+ ),
2288
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-2 max-w-[80%]", children: [
2289
+ /* @__PURE__ */ jsxRuntime.jsx(ShimmerLine, { width: "100%", height: 14 }),
2290
+ /* @__PURE__ */ jsxRuntime.jsx(ShimmerLine, { width: "85%", height: 14 }),
2291
+ /* @__PURE__ */ jsxRuntime.jsx(ShimmerLine, { width: "70%", height: 14 })
2292
+ ] })
2293
+ ]
2294
+ }
2295
+ );
2310
2296
  }
2311
2297
  function getLevelIcon(level) {
2312
2298
  switch (level) {
@@ -2452,208 +2438,6 @@ function LogsPanel({
2452
2438
  }
2453
2439
  );
2454
2440
  }
2455
- function CompactToolStatusLine({
2456
- toolCall,
2457
- previousToolCall,
2458
- animationDuration = 300,
2459
- className
2460
- }) {
2461
- const [isAnimating, setIsAnimating] = react.useState(false);
2462
- const [displayedToolCall, setDisplayedToolCall] = react.useState(toolCall);
2463
- const [exitingToolCall, setExitingToolCall] = react.useState(null);
2464
- const prevToolCallRef = react.useRef(null);
2465
- react.useEffect(() => {
2466
- if (toolCall.id !== prevToolCallRef.current) {
2467
- if (prevToolCallRef.current !== null && previousToolCall) {
2468
- setExitingToolCall(previousToolCall);
2469
- setIsAnimating(true);
2470
- const timer = setTimeout(() => {
2471
- setDisplayedToolCall(toolCall);
2472
- setExitingToolCall(null);
2473
- setIsAnimating(false);
2474
- }, animationDuration);
2475
- prevToolCallRef.current = toolCall.id;
2476
- return () => clearTimeout(timer);
2477
- } else {
2478
- setDisplayedToolCall(toolCall);
2479
- prevToolCallRef.current = toolCall.id;
2480
- }
2481
- } else {
2482
- setDisplayedToolCall(toolCall);
2483
- }
2484
- return void 0;
2485
- }, [toolCall, previousToolCall, animationDuration]);
2486
- const statusClasses = {
2487
- pending: "border-yellow-500/30",
2488
- success: "border-[var(--ash-accent)]/30",
2489
- failed: "border-red-500/30"
2490
- };
2491
- const renderToolCallContent = (tc, isExiting) => /* @__PURE__ */ jsxRuntime.jsxs(
2492
- "div",
2493
- {
2494
- className: cn(
2495
- "flex items-center gap-3 px-4 py-2.5",
2496
- isExiting ? "ash-status-line-exit" : isAnimating ? "ash-status-line-enter" : ""
2497
- ),
2498
- style: {
2499
- animationDuration: `${animationDuration}ms`
2500
- },
2501
- children: [
2502
- /* @__PURE__ */ jsxRuntime.jsx(
2503
- "div",
2504
- {
2505
- className: cn(
2506
- "w-6 h-6 rounded-lg flex items-center justify-center shrink-0",
2507
- tc.status === "pending" ? "bg-yellow-500/20" : tc.status === "failed" ? "bg-red-500/20" : "bg-[var(--ash-accent)]/20"
2508
- ),
2509
- children: /* @__PURE__ */ jsxRuntime.jsx(
2510
- ActionIcon,
2511
- {
2512
- actionType: tc.actionType,
2513
- className: cn(
2514
- "w-3.5 h-3.5",
2515
- tc.status === "pending" ? "text-yellow-400" : tc.status === "failed" ? "text-red-400" : "text-[var(--ash-accent)]"
2516
- )
2517
- }
2518
- )
2519
- }
2520
- ),
2521
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-white shrink-0", children: getActionLabel(tc.actionType) }),
2522
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-sm truncate text-white/60 flex-1 min-w-0", children: tc.summary }),
2523
- /* @__PURE__ */ jsxRuntime.jsx(StatusIndicator, { status: tc.status, size: "sm" })
2524
- ]
2525
- }
2526
- );
2527
- return /* @__PURE__ */ jsxRuntime.jsxs(
2528
- "div",
2529
- {
2530
- className: cn(
2531
- "relative rounded-xl border bg-[var(--ash-surface-dark,#0a0a0a)] overflow-hidden",
2532
- statusClasses[displayedToolCall.status],
2533
- displayedToolCall.status === "pending" && "ash-tool-status-pending",
2534
- className
2535
- ),
2536
- children: [
2537
- exitingToolCall && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0", children: renderToolCallContent(exitingToolCall, true) }),
2538
- renderToolCallContent(displayedToolCall, false)
2539
- ]
2540
- }
2541
- );
2542
- }
2543
- function TodoStatusIcon({ status, className = "w-4 h-4" }) {
2544
- switch (status) {
2545
- case "completed":
2546
- return /* @__PURE__ */ jsxRuntime.jsx(CheckCircleIcon, { className: cn(className, "text-emerald-400") });
2547
- case "in_progress":
2548
- return /* @__PURE__ */ jsxRuntime.jsx(LoaderIcon, { className: cn(className, "text-yellow-400 animate-spin") });
2549
- case "pending":
2550
- default:
2551
- return /* @__PURE__ */ jsxRuntime.jsx(CircleIcon, { className: cn(className, "text-white/30") });
2552
- }
2553
- }
2554
- function TodoListItem({ todo, index, compact = false }) {
2555
- const displayText = todo.status === "in_progress" ? todo.activeForm : todo.content;
2556
- return /* @__PURE__ */ jsxRuntime.jsxs(
2557
- "div",
2558
- {
2559
- className: cn(
2560
- "flex items-start gap-2 transition-all duration-200",
2561
- compact ? "py-1" : "py-1.5",
2562
- todo.status === "completed" && "opacity-60",
2563
- todo.status === "in_progress" && "bg-yellow-500/5 -mx-2 px-2 rounded-md"
2564
- ),
2565
- children: [
2566
- /* @__PURE__ */ jsxRuntime.jsx(TodoStatusIcon, { status: todo.status, className: compact ? "w-3.5 h-3.5 mt-0.5" : "w-4 h-4 mt-0.5" }),
2567
- /* @__PURE__ */ jsxRuntime.jsxs(
2568
- "span",
2569
- {
2570
- className: cn(
2571
- "flex-1 text-white/80",
2572
- compact ? "text-xs" : "text-sm",
2573
- todo.status === "completed" && "line-through text-white/50"
2574
- ),
2575
- children: [
2576
- !compact && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-white/40 mr-1.5", children: [
2577
- index + 1,
2578
- "."
2579
- ] }),
2580
- displayText
2581
- ]
2582
- }
2583
- )
2584
- ]
2585
- }
2586
- );
2587
- }
2588
- function TodoProgress({ completed, total, className }) {
2589
- const percentage = total > 0 ? Math.round(completed / total * 100) : 0;
2590
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex items-center gap-2", className), children: [
2591
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-1.5 bg-white/10 rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
2592
- "div",
2593
- {
2594
- className: "h-full bg-emerald-400 rounded-full transition-all duration-500 ease-out",
2595
- style: { width: `${percentage}%` }
2596
- }
2597
- ) }),
2598
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-white/50 tabular-nums min-w-[3rem] text-right", children: [
2599
- completed,
2600
- "/",
2601
- total
2602
- ] })
2603
- ] });
2604
- }
2605
- function TodoPanel({
2606
- todos,
2607
- title,
2608
- compact = false,
2609
- showProgress = true,
2610
- className
2611
- }) {
2612
- const stats = react.useMemo(() => {
2613
- return {
2614
- total: todos.length,
2615
- completed: todos.filter((t) => t.status === "completed").length,
2616
- inProgress: todos.filter((t) => t.status === "in_progress").length,
2617
- pending: todos.filter((t) => t.status === "pending").length
2618
- };
2619
- }, [todos]);
2620
- if (todos.length === 0) {
2621
- return null;
2622
- }
2623
- return /* @__PURE__ */ jsxRuntime.jsxs(
2624
- "div",
2625
- {
2626
- className: cn(
2627
- "rounded-lg border border-white/10 bg-white/5 overflow-hidden",
2628
- className
2629
- ),
2630
- children: [
2631
- (title || showProgress) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("border-b border-white/5", compact ? "px-2 py-1.5" : "px-3 py-2"), children: [
2632
- title && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("font-medium text-white/90 mb-1.5", compact ? "text-xs" : "text-sm"), children: title }),
2633
- showProgress && /* @__PURE__ */ jsxRuntime.jsx(TodoProgress, { completed: stats.completed, total: stats.total })
2634
- ] }),
2635
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(compact ? "px-2 py-1.5" : "px-3 py-2"), children: todos.map((todo, index) => /* @__PURE__ */ jsxRuntime.jsx(
2636
- TodoListItem,
2637
- {
2638
- todo,
2639
- index,
2640
- compact
2641
- },
2642
- `${todo.content}-${index}`
2643
- )) }),
2644
- !compact && stats.inProgress > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 border-t border-white/5 bg-yellow-500/5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs text-yellow-400", children: [
2645
- /* @__PURE__ */ jsxRuntime.jsx(LoaderIcon, { className: "w-3 h-3 animate-spin" }),
2646
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2647
- stats.inProgress,
2648
- " task",
2649
- stats.inProgress !== 1 ? "s" : "",
2650
- " in progress"
2651
- ] })
2652
- ] }) })
2653
- ]
2654
- }
2655
- );
2656
- }
2657
2441
  function EnvVarsPanel({
2658
2442
  envVars,
2659
2443
  onChange,
@@ -2785,236 +2569,90 @@ function EnvVarsPanel({
2785
2569
  onClick: handleAddEnvVar,
2786
2570
  disabled: !newEnvKey.trim(),
2787
2571
  className: "ash-env-vars-add-button",
2788
- children: "Add"
2789
- }
2790
- )
2791
- ] }),
2792
- helperText && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ash-env-vars-helper", children: helperText })
2793
- ] })
2794
- ] });
2795
- }
2796
- function DisplayModeToggle({
2797
- className,
2798
- showLabel = true,
2799
- labels = { inline: "Inline", compact: "Compact", accordion: "Steps" },
2800
- modes = ["inline", "compact", "accordion"]
2801
- }) {
2802
- const { config, setMode } = useDisplayMode();
2803
- const currentMode = config.mode;
2804
- const currentIndex = modes.indexOf(currentMode);
2805
- const effectiveIndex = currentIndex === -1 ? 0 : currentIndex;
2806
- const nextIndex = (effectiveIndex + 1) % modes.length;
2807
- const nextMode = modes[nextIndex];
2808
- const handleClick = () => {
2809
- setMode(nextMode);
2810
- };
2811
- const getIcon = (mode) => {
2812
- switch (mode) {
2813
- case "inline":
2814
- return /* @__PURE__ */ jsxRuntime.jsx(
2815
- "svg",
2816
- {
2817
- className: "ash-display-mode-icon",
2818
- viewBox: "0 0 24 24",
2819
- fill: "none",
2820
- stroke: "currentColor",
2821
- strokeWidth: "1.5",
2822
- children: /* @__PURE__ */ jsxRuntime.jsx(
2823
- "path",
2824
- {
2825
- strokeLinecap: "round",
2826
- strokeLinejoin: "round",
2827
- d: "M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
2828
- }
2829
- )
2572
+ children: "Add"
2830
2573
  }
2831
- );
2832
- case "compact":
2833
- return /* @__PURE__ */ jsxRuntime.jsx(
2574
+ )
2575
+ ] }),
2576
+ helperText && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ash-env-vars-helper", children: helperText })
2577
+ ] })
2578
+ ] });
2579
+ }
2580
+ function OptionCards({ options, onSelect, className }) {
2581
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("grid gap-2 mt-3", className), style: {
2582
+ gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))"
2583
+ }, children: options.map((option) => /* @__PURE__ */ jsxRuntime.jsxs(
2584
+ "button",
2585
+ {
2586
+ onClick: () => onSelect(option),
2587
+ className: cn(
2588
+ "flex items-start gap-3 p-3 rounded-xl text-left",
2589
+ "bg-white/5 border border-white/10",
2590
+ "hover:bg-[var(--ash-accent)]/10 hover:border-[var(--ash-accent)]/30",
2591
+ "focus:outline-none focus:ring-2 focus:ring-[var(--ash-accent)]/50",
2592
+ "transition-all duration-200 cursor-pointer",
2593
+ "group"
2594
+ ),
2595
+ children: [
2596
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(
2597
+ "flex-shrink-0 w-6 h-6 rounded-lg",
2598
+ "bg-[var(--ash-accent)]/20 text-[var(--ash-accent)]",
2599
+ "flex items-center justify-center",
2600
+ "text-xs font-semibold",
2601
+ "group-hover:bg-[var(--ash-accent)]/30",
2602
+ "transition-colors duration-200"
2603
+ ), children: option.id }),
2604
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
2605
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-white/90 group-hover:text-white transition-colors", children: option.label }),
2606
+ option.description && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-white/50 mt-0.5 line-clamp-2 group-hover:text-white/60 transition-colors", children: option.description })
2607
+ ] }),
2608
+ /* @__PURE__ */ jsxRuntime.jsx(
2834
2609
  "svg",
2835
2610
  {
2836
- className: "ash-display-mode-icon",
2837
- viewBox: "0 0 24 24",
2611
+ className: cn(
2612
+ "w-4 h-4 text-white/30 flex-shrink-0 mt-0.5",
2613
+ "group-hover:text-[var(--ash-accent)] group-hover:translate-x-0.5",
2614
+ "transition-all duration-200"
2615
+ ),
2838
2616
  fill: "none",
2839
- stroke: "currentColor",
2840
- strokeWidth: "1.5",
2841
- children: /* @__PURE__ */ jsxRuntime.jsx(
2842
- "path",
2843
- {
2844
- strokeLinecap: "round",
2845
- strokeLinejoin: "round",
2846
- d: "M3.75 6.75h16.5M3.75 12h16.5M12 17.25h8.25"
2847
- }
2848
- )
2849
- }
2850
- );
2851
- case "accordion":
2852
- return /* @__PURE__ */ jsxRuntime.jsx(
2853
- "svg",
2854
- {
2855
- className: "ash-display-mode-icon",
2856
2617
  viewBox: "0 0 24 24",
2857
- fill: "none",
2858
2618
  stroke: "currentColor",
2859
- strokeWidth: "1.5",
2860
- children: /* @__PURE__ */ jsxRuntime.jsx(
2861
- "path",
2862
- {
2863
- strokeLinecap: "round",
2864
- strokeLinejoin: "round",
2865
- d: "M8.25 6.75h12M8.25 12h12M8.25 17.25h12M3.75 6.75h.007v.008H3.75V6.75zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zM3.75 12h.007v.008H3.75V12zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm-.375 5.25h.007v.008H3.75v-.008zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z"
2866
- }
2867
- )
2619
+ strokeWidth: 2,
2620
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" })
2868
2621
  }
2869
- );
2870
- }
2871
- };
2872
- const getLabel = (mode) => {
2873
- return labels[mode] || mode.charAt(0).toUpperCase() + mode.slice(1);
2874
- };
2875
- return /* @__PURE__ */ jsxRuntime.jsxs(
2876
- "button",
2877
- {
2878
- type: "button",
2879
- onClick: handleClick,
2880
- className: cn("ash-display-mode-toggle", className),
2881
- title: `Switch to ${nextMode} mode`,
2882
- children: [
2883
- getIcon(currentMode),
2884
- showLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ash-display-mode-label", children: getLabel(currentMode) })
2885
- ]
2886
- }
2887
- );
2888
- }
2889
- var DEFAULT_WORDS = [
2890
- "Thinking",
2891
- "Reasoning",
2892
- "Pondering",
2893
- "Analyzing",
2894
- "Considering",
2895
- "Processing",
2896
- "Evaluating"
2897
- ];
2898
- function TypewriterText({
2899
- words = DEFAULT_WORDS,
2900
- typeSpeed = 50,
2901
- pauseBeforeErase = 800,
2902
- eraseSpeed = 30,
2903
- pauseBeforeType = 200,
2904
- showCursor = true,
2905
- className,
2906
- cursorClassName
2907
- }) {
2908
- const [displayText, setDisplayText] = react.useState("");
2909
- const [wordIndex, setWordIndex] = react.useState(0);
2910
- const [isTyping, setIsTyping] = react.useState(true);
2911
- const timeoutRef = react.useRef(null);
2912
- react.useEffect(() => {
2913
- const currentWord = words[wordIndex] ?? "";
2914
- if (isTyping) {
2915
- if (displayText.length < currentWord.length) {
2916
- timeoutRef.current = setTimeout(() => {
2917
- setDisplayText(currentWord.slice(0, displayText.length + 1));
2918
- }, typeSpeed);
2919
- } else {
2920
- timeoutRef.current = setTimeout(() => {
2921
- setIsTyping(false);
2922
- }, pauseBeforeErase);
2923
- }
2924
- } else {
2925
- if (displayText.length > 0) {
2926
- timeoutRef.current = setTimeout(() => {
2927
- setDisplayText(displayText.slice(0, -1));
2928
- }, eraseSpeed);
2929
- } else {
2930
- timeoutRef.current = setTimeout(() => {
2931
- setWordIndex((prev) => (prev + 1) % words.length);
2932
- setIsTyping(true);
2933
- }, pauseBeforeType);
2934
- }
2935
- }
2936
- return () => {
2937
- if (timeoutRef.current) {
2938
- clearTimeout(timeoutRef.current);
2939
- }
2940
- };
2941
- }, [displayText, wordIndex, isTyping, words, typeSpeed, pauseBeforeErase, eraseSpeed, pauseBeforeType]);
2942
- return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("inline-flex items-center", className), children: [
2943
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: displayText }),
2944
- showCursor && /* @__PURE__ */ jsxRuntime.jsx(
2945
- "span",
2946
- {
2947
- className: cn(
2948
- "w-0.5 h-4 bg-current ml-0.5 animate-pulse",
2949
- cursorClassName
2950
2622
  )
2951
- }
2952
- )
2953
- ] });
2954
- }
2955
- var ThemeContext = react.createContext(void 0);
2956
- var DEFAULT_STORAGE_KEY = "ash-ui-theme";
2957
- function ThemeProvider({
2958
- children,
2959
- defaultTheme,
2960
- storageKey = DEFAULT_STORAGE_KEY,
2961
- listenToSystemChanges = true
2962
- }) {
2963
- const [theme, setThemeState] = react.useState(() => {
2964
- if (typeof window !== "undefined") {
2965
- const stored = localStorage.getItem(storageKey);
2966
- if (stored === "dark" || stored === "light") {
2967
- return stored;
2968
- }
2969
- if (!defaultTheme && window.matchMedia("(prefers-color-scheme: dark)").matches) {
2970
- return "dark";
2971
- }
2972
- }
2973
- return defaultTheme ?? "light";
2974
- });
2975
- const [mounted, setMounted] = react.useState(false);
2976
- react.useEffect(() => {
2977
- setMounted(true);
2978
- }, []);
2979
- react.useEffect(() => {
2980
- if (!mounted) return;
2981
- const root = document.documentElement;
2982
- if (theme === "dark") {
2983
- root.classList.add("dark");
2984
- } else {
2985
- root.classList.remove("dark");
2986
- }
2987
- localStorage.setItem(storageKey, theme);
2988
- }, [theme, mounted, storageKey]);
2989
- react.useEffect(() => {
2990
- if (!listenToSystemChanges || typeof window === "undefined") return;
2991
- const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
2992
- const handleChange = (e) => {
2993
- const stored = localStorage.getItem(storageKey);
2994
- if (!stored) {
2995
- setThemeState(e.matches ? "dark" : "light");
2996
- }
2997
- };
2998
- mediaQuery.addEventListener("change", handleChange);
2999
- return () => mediaQuery.removeEventListener("change", handleChange);
3000
- }, [storageKey, listenToSystemChanges]);
3001
- const toggleTheme = react.useCallback(() => {
3002
- setThemeState((prev) => prev === "light" ? "dark" : "light");
3003
- }, []);
3004
- const setTheme = react.useCallback((newTheme) => {
3005
- setThemeState(newTheme);
3006
- }, []);
3007
- if (!mounted) {
3008
- return /* @__PURE__ */ jsxRuntime.jsx(ThemeContext.Provider, { value: { theme: defaultTheme ?? "light", toggleTheme, setTheme }, children });
3009
- }
3010
- return /* @__PURE__ */ jsxRuntime.jsx(ThemeContext.Provider, { value: { theme, toggleTheme, setTheme }, children });
2623
+ ]
2624
+ },
2625
+ option.id
2626
+ )) });
3011
2627
  }
3012
- function useTheme() {
3013
- const context = react.useContext(ThemeContext);
3014
- if (context === void 0) {
3015
- throw new Error("useTheme must be used within a ThemeProvider");
2628
+ function ActionIcon({ actionType, className = "w-4 h-4" }) {
2629
+ switch (actionType.action) {
2630
+ case "command_run":
2631
+ return /* @__PURE__ */ jsxRuntime.jsx(TerminalIcon, { className });
2632
+ case "file_read":
2633
+ return /* @__PURE__ */ jsxRuntime.jsx(FileIcon, { className });
2634
+ case "file_edit":
2635
+ return /* @__PURE__ */ jsxRuntime.jsx(EditIcon, { className });
2636
+ case "file_write":
2637
+ return /* @__PURE__ */ jsxRuntime.jsx(FilePlusIcon, { className });
2638
+ case "search":
2639
+ return /* @__PURE__ */ jsxRuntime.jsx(SearchIcon, { className });
2640
+ case "glob":
2641
+ return /* @__PURE__ */ jsxRuntime.jsx(FolderSearchIcon, { className });
2642
+ case "web_fetch":
2643
+ return /* @__PURE__ */ jsxRuntime.jsx(GlobeIcon, { className });
2644
+ case "web_search":
2645
+ return /* @__PURE__ */ jsxRuntime.jsx(SearchIcon, { className });
2646
+ case "mcp_tool":
2647
+ return /* @__PURE__ */ jsxRuntime.jsx(PlugIcon, { className });
2648
+ case "todo_write":
2649
+ return /* @__PURE__ */ jsxRuntime.jsx(ListChecksIcon, { className });
2650
+ case "agent_tool":
2651
+ return /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className });
2652
+ case "generic_tool":
2653
+ default:
2654
+ return /* @__PURE__ */ jsxRuntime.jsx(ToolIcon, { className });
3016
2655
  }
3017
- return context;
3018
2656
  }
3019
2657
 
3020
2658
  // src/design-tokens.ts
@@ -3120,6 +2758,21 @@ var typography = {
3120
2758
  relaxed: "1.75"
3121
2759
  }
3122
2760
  };
2761
+ var cssVars = {
2762
+ fontSize: {
2763
+ base: "--ash-font-size-base",
2764
+ sm: "--ash-font-size-sm",
2765
+ xs: "--ash-font-size-xs",
2766
+ code: "--ash-font-size-code"
2767
+ },
2768
+ accent: "--ash-accent",
2769
+ accentForeground: "--ash-accent-foreground",
2770
+ surfaceDark: "--ash-surface-dark",
2771
+ surfaceDarker: "--ash-surface-darker",
2772
+ surfaceCard: "--ash-surface-card",
2773
+ surfaceElevated: "--ash-surface-elevated",
2774
+ surfaceBorder: "--ash-surface-border"
2775
+ };
3123
2776
  var keyframes = {
3124
2777
  slideUp: {
3125
2778
  from: { opacity: "0", transform: "translateY(20px)" },
@@ -3238,42 +2891,332 @@ function tokensToCssVariables(prefix = "ash") {
3238
2891
  if (key !== "DEFAULT") vars[`--${prefix}-border-${key}`] = value;
3239
2892
  else vars[`--${prefix}-border`] = value;
3240
2893
  });
2894
+ vars[`--${prefix}-font-size-base`] = "14px";
2895
+ vars[`--${prefix}-font-size-sm`] = "12px";
2896
+ vars[`--${prefix}-font-size-xs`] = "11px";
2897
+ vars[`--${prefix}-font-size-code`] = "13px";
3241
2898
  return vars;
3242
2899
  }
3243
- var inlineStyles = {
3244
- // Glass panel effect
3245
- glassPanel: {
3246
- backgroundColor: "rgba(255, 255, 255, 0.05)",
3247
- backdropFilter: "blur(24px)",
3248
- WebkitBackdropFilter: "blur(24px)",
3249
- border: `1px solid ${colors.border.DEFAULT}`
3250
- },
3251
- // Accent button
3252
- accentButton: {
3253
- backgroundColor: colors.accent.DEFAULT,
3254
- color: "#000000",
3255
- border: `1px solid ${colors.accent.DEFAULT}`,
3256
- boxShadow: shadows.glow.md
3257
- },
3258
- // Ghost button
3259
- ghostButton: {
3260
- backgroundColor: "transparent",
3261
- color: colors.text.secondary,
3262
- border: "1px solid transparent"
3263
- },
3264
- // User message bubble
3265
- userMessage: {
3266
- backgroundColor: colors.user.bg,
3267
- border: `1px solid ${colors.user.border}`,
3268
- borderRadius: borderRadius.lg
3269
- },
3270
- // Assistant message bubble
3271
- assistantMessage: {
3272
- backgroundColor: colors.assistant.bg,
3273
- border: `1px solid ${colors.assistant.border}`,
3274
- borderRadius: borderRadius.lg
2900
+ var inlineStyles = {
2901
+ // Glass panel effect
2902
+ glassPanel: {
2903
+ backgroundColor: "rgba(255, 255, 255, 0.05)",
2904
+ backdropFilter: "blur(24px)",
2905
+ WebkitBackdropFilter: "blur(24px)",
2906
+ border: `1px solid ${colors.border.DEFAULT}`
2907
+ },
2908
+ // Accent button
2909
+ accentButton: {
2910
+ backgroundColor: colors.accent.DEFAULT,
2911
+ color: "#000000",
2912
+ border: `1px solid ${colors.accent.DEFAULT}`,
2913
+ boxShadow: shadows.glow.md
2914
+ },
2915
+ // Ghost button
2916
+ ghostButton: {
2917
+ backgroundColor: "transparent",
2918
+ color: colors.text.secondary,
2919
+ border: "1px solid transparent"
2920
+ },
2921
+ // User message bubble
2922
+ userMessage: {
2923
+ backgroundColor: colors.user.bg,
2924
+ border: `1px solid ${colors.user.border}`,
2925
+ borderRadius: borderRadius.lg
2926
+ },
2927
+ // Assistant message bubble
2928
+ assistantMessage: {
2929
+ backgroundColor: colors.assistant.bg,
2930
+ border: `1px solid ${colors.assistant.border}`,
2931
+ borderRadius: borderRadius.lg
2932
+ }
2933
+ };
2934
+
2935
+ // src/types.ts
2936
+ function isCommandRunAction(action) {
2937
+ return action.action === "command_run";
2938
+ }
2939
+ function isFileReadAction(action) {
2940
+ return action.action === "file_read";
2941
+ }
2942
+ function isFileEditAction(action) {
2943
+ return action.action === "file_edit";
2944
+ }
2945
+ function isFileWriteAction(action) {
2946
+ return action.action === "file_write";
2947
+ }
2948
+ function isSearchAction(action) {
2949
+ return action.action === "search";
2950
+ }
2951
+ function isGlobAction(action) {
2952
+ return action.action === "glob";
2953
+ }
2954
+ function isWebFetchAction(action) {
2955
+ return action.action === "web_fetch";
2956
+ }
2957
+ function isWebSearchAction(action) {
2958
+ return action.action === "web_search";
2959
+ }
2960
+ function isMcpToolAction(action) {
2961
+ return action.action === "mcp_tool";
2962
+ }
2963
+ function isGenericToolAction(action) {
2964
+ return action.action === "generic_tool";
2965
+ }
2966
+ function isTodoWriteAction(action) {
2967
+ return action.action === "todo_write";
2968
+ }
2969
+ function isAgentToolAction(action) {
2970
+ return action.action === "agent_tool";
2971
+ }
2972
+ function isToolCallEntry(entry) {
2973
+ return entry.type === "tool_call";
2974
+ }
2975
+ function isErrorEntry(entry) {
2976
+ return entry.type === "error";
2977
+ }
2978
+ function isWidgetEntry(entry) {
2979
+ return entry.type === "widget";
2980
+ }
2981
+ var DEFAULT_STYLE_CONFIG = {
2982
+ userVariant: "bubble",
2983
+ assistantVariant: "bubble",
2984
+ scale: "compact",
2985
+ showTimestamp: true,
2986
+ showAvatars: true
2987
+ };
2988
+ function normalizedEntryToMessage(entry) {
2989
+ const { id, entryType, content, timestamp } = entry;
2990
+ const createdAt = timestamp ? new Date(timestamp) : void 0;
2991
+ switch (entryType.type) {
2992
+ case "user_message":
2993
+ return {
2994
+ id,
2995
+ role: "user",
2996
+ content,
2997
+ createdAt
2998
+ };
2999
+ case "assistant_message":
3000
+ return {
3001
+ id,
3002
+ role: "assistant",
3003
+ content,
3004
+ createdAt
3005
+ };
3006
+ case "thinking":
3007
+ return {
3008
+ id,
3009
+ role: "assistant",
3010
+ content: "",
3011
+ reasoning: content,
3012
+ createdAt
3013
+ };
3014
+ case "tool_call": {
3015
+ const { toolCall } = entryType;
3016
+ const toolInvocation = {
3017
+ state: toolCall.status === "pending" ? "call" : "result",
3018
+ toolCallId: toolCall.id,
3019
+ toolName: toolCall.toolName,
3020
+ args: toolCall.input || {},
3021
+ result: toolCall.output
3022
+ };
3023
+ return {
3024
+ id,
3025
+ role: "assistant",
3026
+ content: "",
3027
+ toolInvocations: [toolInvocation],
3028
+ createdAt
3029
+ };
3030
+ }
3031
+ case "error":
3032
+ return {
3033
+ id,
3034
+ role: "system",
3035
+ content: `Error: ${entryType.message}${entryType.code ? ` (${entryType.code})` : ""}`,
3036
+ createdAt
3037
+ };
3038
+ case "widget":
3039
+ return {
3040
+ id,
3041
+ role: "data",
3042
+ content: JSON.stringify({
3043
+ widgetType: entryType.widgetType,
3044
+ widgetData: entryType.widgetData
3045
+ }),
3046
+ createdAt
3047
+ };
3048
+ default:
3049
+ return null;
3050
+ }
3051
+ }
3052
+ function messageToNormalizedEntry(message) {
3053
+ const { id, role, content, createdAt, toolInvocations, reasoning } = message;
3054
+ const timestamp = createdAt?.toISOString();
3055
+ if (toolInvocations && toolInvocations.length > 0) {
3056
+ const inv = toolInvocations[0];
3057
+ const toolCall = {
3058
+ id: inv.toolCallId,
3059
+ toolName: inv.toolName,
3060
+ actionType: {
3061
+ action: "generic_tool",
3062
+ toolName: inv.toolName,
3063
+ arguments: inv.args,
3064
+ result: inv.result ? { type: "json", value: inv.result } : void 0
3065
+ },
3066
+ status: inv.state === "result" ? "success" : "pending",
3067
+ summary: `${inv.toolName}(${Object.keys(inv.args).join(", ")})`,
3068
+ input: inv.args,
3069
+ output: inv.result
3070
+ };
3071
+ return {
3072
+ id,
3073
+ timestamp,
3074
+ entryType: { type: "tool_call", toolCall },
3075
+ content: toolCall.summary
3076
+ };
3077
+ }
3078
+ if (reasoning) {
3079
+ return {
3080
+ id,
3081
+ timestamp,
3082
+ entryType: { type: "thinking" },
3083
+ content: reasoning
3084
+ };
3085
+ }
3086
+ if (role === "user") {
3087
+ return {
3088
+ id,
3089
+ timestamp,
3090
+ entryType: { type: "user_message" },
3091
+ content
3092
+ };
3093
+ }
3094
+ if (role === "assistant") {
3095
+ return {
3096
+ id,
3097
+ timestamp,
3098
+ entryType: { type: "assistant_message" },
3099
+ content
3100
+ };
3101
+ }
3102
+ if (role === "system") {
3103
+ if (content.startsWith("Error:")) {
3104
+ return {
3105
+ id,
3106
+ timestamp,
3107
+ entryType: { type: "error", message: content.replace("Error: ", "") },
3108
+ content
3109
+ };
3110
+ }
3111
+ return {
3112
+ id,
3113
+ timestamp,
3114
+ entryType: { type: "assistant_message" },
3115
+ content
3116
+ };
3117
+ }
3118
+ if (role === "data") {
3119
+ try {
3120
+ const data = JSON.parse(content);
3121
+ if (data.widgetType) {
3122
+ return {
3123
+ id,
3124
+ timestamp,
3125
+ entryType: {
3126
+ type: "widget",
3127
+ widgetType: data.widgetType,
3128
+ widgetData: data.widgetData
3129
+ },
3130
+ content: ""
3131
+ };
3132
+ }
3133
+ } catch {
3134
+ }
3135
+ return {
3136
+ id,
3137
+ timestamp,
3138
+ entryType: { type: "assistant_message" },
3139
+ content
3140
+ };
3141
+ }
3142
+ return {
3143
+ id,
3144
+ timestamp,
3145
+ entryType: { type: "assistant_message" },
3146
+ content
3147
+ };
3148
+ }
3149
+ function normalizedEntriesToMessages(entries) {
3150
+ const messages = [];
3151
+ let currentAssistantMessage = null;
3152
+ for (const entry of entries) {
3153
+ const message = normalizedEntryToMessage(entry);
3154
+ if (!message) continue;
3155
+ if (message.role === "assistant") {
3156
+ if (currentAssistantMessage) {
3157
+ if (message.toolInvocations) {
3158
+ currentAssistantMessage.toolInvocations = [
3159
+ ...currentAssistantMessage.toolInvocations || [],
3160
+ ...message.toolInvocations
3161
+ ];
3162
+ }
3163
+ if (message.content) {
3164
+ currentAssistantMessage.content = currentAssistantMessage.content ? `${currentAssistantMessage.content}
3165
+ ${message.content}` : message.content;
3166
+ }
3167
+ if (message.reasoning) {
3168
+ currentAssistantMessage.reasoning = currentAssistantMessage.reasoning ? `${currentAssistantMessage.reasoning}
3169
+ ${message.reasoning}` : message.reasoning;
3170
+ }
3171
+ } else {
3172
+ currentAssistantMessage = { ...message };
3173
+ }
3174
+ } else {
3175
+ if (currentAssistantMessage) {
3176
+ messages.push(currentAssistantMessage);
3177
+ currentAssistantMessage = null;
3178
+ }
3179
+ messages.push(message);
3180
+ }
3275
3181
  }
3276
- };
3182
+ if (currentAssistantMessage) {
3183
+ messages.push(currentAssistantMessage);
3184
+ }
3185
+ return messages;
3186
+ }
3187
+ function messagesToNormalizedEntries(messages) {
3188
+ const entries = [];
3189
+ for (const message of messages) {
3190
+ if (message.reasoning) {
3191
+ entries.push({
3192
+ id: `${message.id}-thinking`,
3193
+ timestamp: message.createdAt?.toISOString(),
3194
+ entryType: { type: "thinking" },
3195
+ content: message.reasoning
3196
+ });
3197
+ }
3198
+ if (message.content || !message.toolInvocations?.length) {
3199
+ entries.push(messageToNormalizedEntry({
3200
+ ...message,
3201
+ toolInvocations: void 0,
3202
+ reasoning: void 0
3203
+ }));
3204
+ }
3205
+ if (message.toolInvocations) {
3206
+ for (const inv of message.toolInvocations) {
3207
+ const toolMessage = {
3208
+ id: inv.toolCallId,
3209
+ role: "assistant",
3210
+ content: "",
3211
+ toolInvocations: [inv],
3212
+ createdAt: message.createdAt
3213
+ };
3214
+ entries.push(messageToNormalizedEntry(toolMessage));
3215
+ }
3216
+ }
3217
+ }
3218
+ return entries;
3219
+ }
3277
3220
  function useMessageQueue({
3278
3221
  onProcessMessage,
3279
3222
  canProcess = true
@@ -3493,19 +3436,95 @@ function useFileUpload({
3493
3436
  openFilePicker
3494
3437
  };
3495
3438
  }
3439
+
3440
+ // src/hooks/middleware.ts
3441
+ async function applyRequestMiddleware(middlewares, request) {
3442
+ let current = { ...request };
3443
+ for (const mw of middlewares) {
3444
+ if (mw.onRequest) {
3445
+ try {
3446
+ const result = await mw.onRequest(current);
3447
+ if (result?.error) {
3448
+ return { request: current, error: result.error };
3449
+ }
3450
+ if (result) {
3451
+ current = {
3452
+ ...current,
3453
+ prompt: result.prompt ?? current.prompt,
3454
+ sessionContext: result.sessionContext ?? current.sessionContext,
3455
+ metadata: {
3456
+ ...current.metadata,
3457
+ ...result.metadata
3458
+ }
3459
+ };
3460
+ }
3461
+ } catch (err) {
3462
+ const errorMessage = err instanceof Error ? err.message : "Middleware error";
3463
+ return { request: current, error: errorMessage };
3464
+ }
3465
+ }
3466
+ }
3467
+ return { request: current };
3468
+ }
3469
+ async function applyEventMiddleware(middlewares, event) {
3470
+ let current = event;
3471
+ for (const mw of middlewares) {
3472
+ if (!current) break;
3473
+ if (mw.onEvent) {
3474
+ try {
3475
+ current = await mw.onEvent(current);
3476
+ } catch (err) {
3477
+ console.error("[middleware] onEvent error:", err);
3478
+ }
3479
+ }
3480
+ }
3481
+ return current;
3482
+ }
3483
+ async function callMiddlewareComplete(middlewares, sessionId) {
3484
+ for (const mw of middlewares) {
3485
+ if (mw.onComplete) {
3486
+ try {
3487
+ await mw.onComplete(sessionId);
3488
+ } catch (err) {
3489
+ console.error("[middleware] onComplete error:", err);
3490
+ }
3491
+ }
3492
+ }
3493
+ }
3494
+ async function callMiddlewareError(middlewares, error) {
3495
+ for (const mw of middlewares) {
3496
+ if (mw.onError) {
3497
+ try {
3498
+ await mw.onError(error);
3499
+ } catch (err) {
3500
+ console.error("[middleware] onError error:", err);
3501
+ }
3502
+ }
3503
+ }
3504
+ }
3505
+
3506
+ // src/hooks/useAgentChat.ts
3496
3507
  function useAgentChat(options) {
3497
3508
  const {
3498
3509
  createStream,
3510
+ subscribeToSession,
3499
3511
  initialSessionId,
3500
3512
  initialEntries = [],
3501
3513
  onSessionStart,
3502
3514
  onSessionEnd,
3503
3515
  onError,
3504
- onSandboxLog
3516
+ onSandboxLog,
3517
+ onReconnect,
3518
+ maxReconnectAttempts = 3,
3519
+ reconnectBaseDelay = 1e3,
3520
+ onBeforeSend,
3521
+ onEvent,
3522
+ middleware
3505
3523
  } = options;
3506
3524
  const [historyEntries, setHistoryEntries] = react.useState(initialEntries);
3507
3525
  const [streamingEntries, setStreamingEntries] = react.useState([]);
3508
3526
  const [isStreaming, setIsStreaming] = react.useState(false);
3527
+ const [isReconnecting, setIsReconnecting] = react.useState(false);
3509
3528
  const [error, setError] = react.useState(null);
3510
3529
  const [sessionId, setSessionId] = react.useState(initialSessionId || null);
3511
3530
  const abortControllerRef = react.useRef(null);
@@ -3513,6 +3532,16 @@ function useAgentChat(options) {
3513
3532
  const currentTextIdRef = react.useRef(null);
3514
3533
  const pendingToolCallsRef = react.useRef(/* @__PURE__ */ new Map());
3515
3534
  const hadToolCallSinceTextRef = react.useRef(false);
3535
+ const reconnectAttemptsRef = react.useRef(0);
3536
+ const eventCountRef = react.useRef(0);
3537
+ const sessionIdRef = react.useRef(sessionId);
3538
+ const streamingEntriesRef = react.useRef([]);
3539
+ react.useEffect(() => {
3540
+ sessionIdRef.current = sessionId;
3541
+ }, [sessionId]);
3542
+ react.useEffect(() => {
3543
+ streamingEntriesRef.current = streamingEntries;
3544
+ }, [streamingEntries]);
3516
3545
  const entries = [...historyEntries, ...streamingEntries];
3517
3546
  const emitStreamingEntries = react.useCallback((newEntries) => {
3518
3547
  setStreamingEntries([...newEntries]);
@@ -3627,7 +3656,7 @@ function useAgentChat(options) {
3627
3656
  setError(event.error);
3628
3657
  onError?.(event.error);
3629
3658
  newEntries.push({
3630
- id: `error-${Date.now()}`,
3659
+ id: `error-${Date.now()}-${Math.random().toString(36).slice(2)}`,
3631
3660
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3632
3661
  entryType: { type: "error", message: event.error, code: event.code },
3633
3662
  content: event.error
@@ -3646,12 +3675,112 @@ function useAgentChat(options) {
3646
3675
  }
3647
3676
  return newEntries;
3648
3677
  }, [sessionId, onSessionStart, onSessionEnd, onError, onSandboxLog, createTextEntry, resetStreamingState]);
3678
+ const attemptReconnect = react.useCallback(async (targetSessionId, currentEntries) => {
3679
+ if (!subscribeToSession) {
3680
+ return false;
3681
+ }
3682
+ const attempt = reconnectAttemptsRef.current + 1;
3683
+ if (attempt > maxReconnectAttempts) {
3684
+ console.warn(`[useAgentChat] Max reconnection attempts (${maxReconnectAttempts}) exceeded`);
3685
+ return false;
3686
+ }
3687
+ reconnectAttemptsRef.current = attempt;
3688
+ setIsReconnecting(true);
3689
+ onReconnect?.(attempt, targetSessionId);
3690
+ const delay = reconnectBaseDelay * Math.pow(2, attempt - 1);
3691
+ console.log(`[useAgentChat] Reconnection attempt ${attempt}/${maxReconnectAttempts} in ${delay}ms`);
3692
+ await new Promise((resolve) => setTimeout(resolve, delay));
3693
+ const controller = new AbortController();
3694
+ abortControllerRef.current = controller;
3695
+ try {
3696
+ const stream = subscribeToSession(targetSessionId, controller.signal);
3697
+ let localStreamingEntries = [...currentEntries];
3698
+ for await (const event of stream) {
3699
+ if (controller.signal.aborted) break;
3700
+ eventCountRef.current++;
3701
+ localStreamingEntries = processEvent(event, localStreamingEntries);
3702
+ emitStreamingEntries(localStreamingEntries);
3703
+ if (event.type === "complete" || event.type === "session_end" || event.type === "error") {
3704
+ reconnectAttemptsRef.current = 0;
3705
+ setIsReconnecting(false);
3706
+ if (event.type === "complete" && localStreamingEntries.length > 0) {
3707
+ setHistoryEntries((prev) => [...prev, ...localStreamingEntries]);
3708
+ setStreamingEntries([]);
3709
+ }
3710
+ return true;
3711
+ }
3712
+ }
3713
+ reconnectAttemptsRef.current = 0;
3714
+ setIsReconnecting(false);
3715
+ if (localStreamingEntries.length > 0) {
3716
+ setHistoryEntries((prev) => [...prev, ...localStreamingEntries]);
3717
+ setStreamingEntries([]);
3718
+ }
3719
+ return true;
3720
+ } catch (err) {
3721
+ const isAbort = err.name === "AbortError" || err.message?.includes("aborted") || err.message?.includes("BodyStreamBuffer");
3722
+ if (isAbort && !controller.signal.aborted) {
3723
+ console.log(`[useAgentChat] Reconnection stream interrupted, will retry...`);
3724
+ setIsReconnecting(false);
3725
+ return attemptReconnect(targetSessionId, streamingEntriesRef.current);
3726
+ }
3727
+ setIsReconnecting(false);
3728
+ if (!controller.signal.aborted) {
3729
+ const errorMessage = err instanceof Error ? err.message : "Reconnection failed";
3730
+ console.error(`[useAgentChat] Reconnection failed:`, errorMessage);
3731
+ }
3732
+ return false;
3733
+ }
3734
+ }, [subscribeToSession, maxReconnectAttempts, reconnectBaseDelay, onReconnect, processEvent, emitStreamingEntries]);
3649
3735
  const send = react.useCallback(async (prompt) => {
3650
3736
  if (isStreaming) return;
3737
+ let finalPrompt = prompt;
3738
+ let additionalMetadata = {};
3739
+ let additionalContext;
3740
+ if (onBeforeSend) {
3741
+ try {
3742
+ const result = await onBeforeSend({
3743
+ prompt,
3744
+ sessionId,
3745
+ entries: [...historyEntries, ...streamingEntries]
3746
+ });
3747
+ if (result?.cancel) {
3748
+ return;
3749
+ }
3750
+ if (result?.prompt !== void 0) finalPrompt = result.prompt;
3751
+ if (result?.metadata) additionalMetadata = result.metadata;
3752
+ if (result?.sessionContext) additionalContext = result.sessionContext;
3753
+ } catch (err) {
3754
+ const errorMessage = err instanceof Error ? err.message : "Send cancelled";
3755
+ setError(errorMessage);
3756
+ onError?.(errorMessage);
3757
+ return;
3758
+ }
3759
+ }
3760
+ if (middleware?.length) {
3761
+ const middlewareRequest = {
3762
+ prompt: finalPrompt,
3763
+ sessionId,
3764
+ metadata: additionalMetadata,
3765
+ sessionContext: additionalContext
3766
+ };
3767
+ const { request, error: middlewareError } = await applyRequestMiddleware(middleware, middlewareRequest);
3768
+ if (middlewareError) {
3769
+ setError(middlewareError);
3770
+ onError?.(middlewareError);
3771
+ await callMiddlewareError(middleware, middlewareError);
3772
+ return;
3773
+ }
3774
+ finalPrompt = request.prompt;
3775
+ additionalMetadata = request.metadata || {};
3776
+ additionalContext = request.sessionContext;
3777
+ }
3651
3778
  setIsStreaming(true);
3652
3779
  setError(null);
3780
+ reconnectAttemptsRef.current = 0;
3781
+ eventCountRef.current = 0;
3653
3782
  const userEntry = {
3654
- id: `user-${Date.now()}`,
3783
+ id: `user-${Date.now()}-${Math.random().toString(36).slice(2)}`,
3655
3784
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3656
3785
  entryType: { type: "user_message" },
3657
3786
  content: prompt
@@ -3661,39 +3790,91 @@ function useAgentChat(options) {
3661
3790
  const controller = new AbortController();
3662
3791
  abortControllerRef.current = controller;
3663
3792
  let localStreamingEntries = [];
3793
+ let completedSessionId = null;
3664
3794
  try {
3665
- const stream = createStream(prompt, sessionId || void 0, controller.signal);
3795
+ const stream = createStream(finalPrompt, sessionId || void 0, {
3796
+ signal: controller.signal,
3797
+ metadata: Object.keys(additionalMetadata).length > 0 ? additionalMetadata : void 0,
3798
+ sessionContext: additionalContext
3799
+ });
3666
3800
  for await (const event of stream) {
3667
3801
  if (controller.signal.aborted) break;
3668
- localStreamingEntries = processEvent(event, localStreamingEntries);
3802
+ eventCountRef.current++;
3803
+ if (event.type === "session_start" && event.sessionId) {
3804
+ completedSessionId = event.sessionId;
3805
+ }
3806
+ if (onEvent) {
3807
+ try {
3808
+ await onEvent(event);
3809
+ } catch (err) {
3810
+ console.error("[useAgentChat] onEvent error:", err);
3811
+ }
3812
+ }
3813
+ let processedEvent = event;
3814
+ if (middleware?.length) {
3815
+ processedEvent = await applyEventMiddleware(middleware, event);
3816
+ }
3817
+ if (!processedEvent) continue;
3818
+ localStreamingEntries = processEvent(processedEvent, localStreamingEntries);
3669
3819
  emitStreamingEntries(localStreamingEntries);
3670
3820
  }
3671
3821
  if (localStreamingEntries.length > 0) {
3672
3822
  setHistoryEntries((prev) => [...prev, ...localStreamingEntries]);
3673
3823
  setStreamingEntries([]);
3674
3824
  }
3825
+ reconnectAttemptsRef.current = 0;
3826
+ if (middleware?.length && (completedSessionId || sessionIdRef.current)) {
3827
+ await callMiddlewareComplete(middleware, completedSessionId || sessionIdRef.current || "");
3828
+ }
3675
3829
  } catch (err) {
3676
- if (err.name !== "AbortError") {
3830
+ const isAbort = err.name === "AbortError" || err.message?.includes("aborted") || err.message?.includes("BodyStreamBuffer");
3831
+ if (isAbort && !controller.signal.aborted) {
3832
+ const currentSessionId = sessionIdRef.current;
3833
+ const hadEvents = eventCountRef.current > 0;
3834
+ console.log(`[useAgentChat] Stream interrupted. sessionId=${currentSessionId}, events=${eventCountRef.current}`);
3835
+ if (currentSessionId && hadEvents && subscribeToSession) {
3836
+ console.log(`[useAgentChat] Attempting auto-reconnection...`);
3837
+ const reconnected = await attemptReconnect(currentSessionId, streamingEntriesRef.current);
3838
+ if (reconnected) {
3839
+ return;
3840
+ }
3841
+ const errorMsg = "Connection lost. Please try again.";
3842
+ setError(errorMsg);
3843
+ onError?.(errorMsg);
3844
+ if (middleware?.length) {
3845
+ await callMiddlewareError(middleware, errorMsg);
3846
+ }
3847
+ }
3848
+ } else if (!isAbort) {
3677
3849
  const errorMessage = err instanceof Error ? err.message : "Unknown error";
3678
3850
  setError(errorMessage);
3679
3851
  onError?.(errorMessage);
3852
+ if (middleware?.length) {
3853
+ await callMiddlewareError(middleware, errorMessage);
3854
+ }
3680
3855
  }
3681
3856
  } finally {
3682
3857
  setIsStreaming(false);
3858
+ setIsReconnecting(false);
3683
3859
  abortControllerRef.current = null;
3684
3860
  resetStreamingState();
3685
3861
  }
3686
- }, [isStreaming, sessionId, createStream, processEvent, emitStreamingEntries, resetStreamingState, onError]);
3862
+ }, [isStreaming, sessionId, historyEntries, streamingEntries, createStream, subscribeToSession, processEvent, emitStreamingEntries, resetStreamingState, onError, attemptReconnect, onBeforeSend, onEvent, middleware]);
3687
3863
  const stop = react.useCallback(() => {
3864
+ reconnectAttemptsRef.current = maxReconnectAttempts + 1;
3865
+ setIsReconnecting(false);
3688
3866
  if (abortControllerRef.current) {
3689
3867
  abortControllerRef.current.abort();
3690
3868
  }
3691
- }, []);
3869
+ }, [maxReconnectAttempts]);
3692
3870
  const clear = react.useCallback(() => {
3693
3871
  setHistoryEntries([]);
3694
3872
  resetStreamingState();
3695
3873
  setSessionId(initialSessionId || null);
3696
3874
  setError(null);
3875
+ setIsReconnecting(false);
3876
+ reconnectAttemptsRef.current = 0;
3877
+ eventCountRef.current = 0;
3697
3878
  }, [initialSessionId, resetStreamingState]);
3698
3879
  const setEntries = react.useCallback((newEntries) => {
3699
3880
  resetStreamingState();
@@ -3709,6 +3890,7 @@ function useAgentChat(options) {
3709
3890
  return {
3710
3891
  entries,
3711
3892
  isStreaming,
3893
+ isReconnecting,
3712
3894
  error,
3713
3895
  sessionId,
3714
3896
  send,
@@ -3717,6 +3899,254 @@ function useAgentChat(options) {
3717
3899
  setEntries
3718
3900
  };
3719
3901
  }
3902
+ function generateId() {
3903
+ return `msg-${Date.now()}-${Math.random().toString(36).slice(2)}`;
3904
+ }
3905
+ function useChat(options) {
3906
+ const {
3907
+ createStream,
3908
+ // subscribeToSession, // TODO: implement reconnection support
3909
+ initialSessionId,
3910
+ initialMessages = [],
3911
+ onToolCall,
3912
+ onFinish,
3913
+ onError,
3914
+ onSessionStart,
3915
+ maxReconnectAttempts = 3,
3916
+ // reconnectBaseDelay = 1000, // TODO: implement reconnection support
3917
+ middleware
3918
+ } = options;
3919
+ const [messages, setMessages] = react.useState(initialMessages);
3920
+ const [isLoading, setIsLoading] = react.useState(false);
3921
+ const [isReconnecting, setIsReconnecting] = react.useState(false);
3922
+ const [error, setError] = react.useState(void 0);
3923
+ const [sessionId, setSessionId] = react.useState(initialSessionId || null);
3924
+ const [input, setInput] = react.useState("");
3925
+ const abortControllerRef = react.useRef(null);
3926
+ const currentAssistantMessageRef = react.useRef(null);
3927
+ const reconnectAttemptsRef = react.useRef(0);
3928
+ const sessionIdRef = react.useRef(sessionId);
3929
+ react.useEffect(() => {
3930
+ sessionIdRef.current = sessionId;
3931
+ }, [sessionId]);
3932
+ const entries = messagesToNormalizedEntries(messages);
3933
+ const updateStreamingMessage = react.useCallback((updater) => {
3934
+ if (!currentAssistantMessageRef.current) return;
3935
+ currentAssistantMessageRef.current = updater(currentAssistantMessageRef.current);
3936
+ const currentMsg = currentAssistantMessageRef.current;
3937
+ setMessages((prev) => {
3938
+ const lastIndex = prev.length - 1;
3939
+ const lastMessage = prev[lastIndex];
3940
+ if (lastIndex >= 0 && lastMessage && currentMsg && lastMessage.id === currentMsg.id) {
3941
+ return [...prev.slice(0, lastIndex), currentMsg];
3942
+ }
3943
+ return prev;
3944
+ });
3945
+ }, []);
3946
+ const processEvent = react.useCallback((event) => {
3947
+ switch (event.type) {
3948
+ case "session_start":
3949
+ if (event.sessionId) {
3950
+ setSessionId(event.sessionId);
3951
+ onSessionStart?.(event.sessionId);
3952
+ }
3953
+ break;
3954
+ case "text_delta":
3955
+ if (event.delta) {
3956
+ updateStreamingMessage((msg) => ({
3957
+ ...msg,
3958
+ content: msg.content + event.delta
3959
+ }));
3960
+ }
3961
+ break;
3962
+ case "tool_use":
3963
+ if (event.toolId && event.toolName) {
3964
+ const toolInvocation = {
3965
+ state: "call",
3966
+ toolCallId: event.toolId,
3967
+ toolName: event.toolName,
3968
+ args: event.input || {}
3969
+ };
3970
+ updateStreamingMessage((msg) => ({
3971
+ ...msg,
3972
+ toolInvocations: [...msg.toolInvocations || [], toolInvocation]
3973
+ }));
3974
+ onToolCall?.({ toolCall: toolInvocation });
3975
+ }
3976
+ break;
3977
+ case "tool_result":
3978
+ if (event.toolId) {
3979
+ updateStreamingMessage((msg) => ({
3980
+ ...msg,
3981
+ toolInvocations: msg.toolInvocations?.map(
3982
+ (inv) => inv.toolCallId === event.toolId ? { ...inv, state: "result", result: event.toolResult } : inv
3983
+ )
3984
+ }));
3985
+ }
3986
+ break;
3987
+ case "text":
3988
+ if (event.text && !currentAssistantMessageRef.current?.content) {
3989
+ updateStreamingMessage((msg) => ({
3990
+ ...msg,
3991
+ content: event.text || ""
3992
+ }));
3993
+ }
3994
+ break;
3995
+ case "error":
3996
+ if (event.error) {
3997
+ const err = new Error(event.error);
3998
+ setError(err);
3999
+ onError?.(err);
4000
+ }
4001
+ break;
4002
+ case "complete":
4003
+ if (currentAssistantMessageRef.current) {
4004
+ onFinish?.(currentAssistantMessageRef.current);
4005
+ }
4006
+ break;
4007
+ }
4008
+ }, [updateStreamingMessage, onToolCall, onFinish, onError, onSessionStart]);
4009
+ const append = react.useCallback(async (messageOrContent, options2) => {
4010
+ if (isLoading) return null;
4011
+ const userMessage = typeof messageOrContent === "string" ? {
4012
+ id: generateId(),
4013
+ role: "user",
4014
+ content: messageOrContent,
4015
+ createdAt: /* @__PURE__ */ new Date()
4016
+ } : {
4017
+ id: messageOrContent.id || generateId(),
4018
+ role: messageOrContent.role,
4019
+ content: messageOrContent.content,
4020
+ toolInvocations: messageOrContent.toolInvocations,
4021
+ reasoning: messageOrContent.reasoning,
4022
+ createdAt: /* @__PURE__ */ new Date()
4023
+ };
4024
+ setMessages((prev) => [...prev, userMessage]);
4025
+ setIsLoading(true);
4026
+ setError(void 0);
4027
+ reconnectAttemptsRef.current = 0;
4028
+ const assistantMessage = {
4029
+ id: generateId(),
4030
+ role: "assistant",
4031
+ content: "",
4032
+ createdAt: /* @__PURE__ */ new Date()
4033
+ };
4034
+ currentAssistantMessageRef.current = assistantMessage;
4035
+ setMessages((prev) => [...prev, assistantMessage]);
4036
+ const controller = new AbortController();
4037
+ abortControllerRef.current = controller;
4038
+ try {
4039
+ let finalPrompt = userMessage.content;
4040
+ let additionalMetadata = options2?.body || {};
4041
+ if (middleware?.length) {
4042
+ const middlewareRequest = {
4043
+ prompt: finalPrompt,
4044
+ sessionId: sessionIdRef.current,
4045
+ metadata: additionalMetadata
4046
+ };
4047
+ const { request, error: middlewareError } = await applyRequestMiddleware(middleware, middlewareRequest);
4048
+ if (middlewareError) {
4049
+ const err = new Error(middlewareError);
4050
+ setError(err);
4051
+ onError?.(err);
4052
+ setIsLoading(false);
4053
+ return null;
4054
+ }
4055
+ finalPrompt = request.prompt;
4056
+ additionalMetadata = request.metadata || {};
4057
+ }
4058
+ const streamOptions = {
4059
+ signal: controller.signal,
4060
+ metadata: Object.keys(additionalMetadata).length > 0 ? additionalMetadata : void 0
4061
+ };
4062
+ const stream = createStream(finalPrompt, sessionIdRef.current || void 0, streamOptions);
4063
+ for await (const event of stream) {
4064
+ if (controller.signal.aborted) break;
4065
+ let processedEvent = event;
4066
+ if (middleware?.length) {
4067
+ processedEvent = await applyEventMiddleware(middleware, event);
4068
+ }
4069
+ if (processedEvent) {
4070
+ processEvent(processedEvent);
4071
+ }
4072
+ }
4073
+ if (middleware?.length && sessionIdRef.current) {
4074
+ await callMiddlewareComplete(middleware, sessionIdRef.current);
4075
+ }
4076
+ return assistantMessage.id;
4077
+ } catch (err) {
4078
+ const isAbort = err.name === "AbortError";
4079
+ if (!isAbort) {
4080
+ const error2 = err instanceof Error ? err : new Error(String(err));
4081
+ setError(error2);
4082
+ onError?.(error2);
4083
+ if (middleware?.length) {
4084
+ await callMiddlewareError(middleware, error2.message);
4085
+ }
4086
+ }
4087
+ return null;
4088
+ } finally {
4089
+ setIsLoading(false);
4090
+ setIsReconnecting(false);
4091
+ abortControllerRef.current = null;
4092
+ currentAssistantMessageRef.current = null;
4093
+ }
4094
+ }, [isLoading, createStream, processEvent, middleware, onError]);
4095
+ const stop = react.useCallback(() => {
4096
+ reconnectAttemptsRef.current = maxReconnectAttempts + 1;
4097
+ setIsReconnecting(false);
4098
+ if (abortControllerRef.current) {
4099
+ abortControllerRef.current.abort();
4100
+ }
4101
+ }, [maxReconnectAttempts]);
4102
+ const reload = react.useCallback(async () => {
4103
+ let lastUserMessageIndex = -1;
4104
+ for (let i = messages.length - 1; i >= 0; i--) {
4105
+ if (messages[i]?.role === "user") {
4106
+ lastUserMessageIndex = i;
4107
+ break;
4108
+ }
4109
+ }
4110
+ if (lastUserMessageIndex === -1) return null;
4111
+ const lastUserMessage = messages[lastUserMessageIndex];
4112
+ if (!lastUserMessage) return null;
4113
+ setMessages(messages.slice(0, lastUserMessageIndex + 1));
4114
+ return append(lastUserMessage);
4115
+ }, [messages, append]);
4116
+ const handleInputChange = react.useCallback((e) => {
4117
+ setInput(e.target.value);
4118
+ }, []);
4119
+ const handleSubmit = react.useCallback((e, options2) => {
4120
+ e?.preventDefault();
4121
+ if (!input.trim()) return;
4122
+ const message = input;
4123
+ setInput("");
4124
+ append(message, options2);
4125
+ }, [input, append]);
4126
+ react.useEffect(() => {
4127
+ return () => {
4128
+ if (abortControllerRef.current) {
4129
+ abortControllerRef.current.abort();
4130
+ }
4131
+ };
4132
+ }, []);
4133
+ return {
4134
+ messages,
4135
+ isLoading,
4136
+ error,
4137
+ sessionId,
4138
+ append,
4139
+ stop,
4140
+ setMessages,
4141
+ reload,
4142
+ input,
4143
+ setInput,
4144
+ handleInputChange,
4145
+ handleSubmit,
4146
+ isReconnecting,
4147
+ entries
4148
+ };
4149
+ }
3720
4150
  function textToBase64(text) {
3721
4151
  const encoder = new TextEncoder();
3722
4152
  const bytes = encoder.encode(text);
@@ -3791,10 +4221,14 @@ function useLongTextConversion({
3791
4221
  }
3792
4222
 
3793
4223
  exports.ActionIcon = ActionIcon;
3794
- exports.AgentToolCard = AgentToolCard;
3795
4224
  exports.AlertCircleIcon = AlertCircleIcon;
3796
4225
  exports.AlertTriangleIcon = AlertTriangleIcon;
3797
- exports.AssistantMessage = AssistantMessage;
4226
+ exports.ArrowUpIcon = ArrowUpIcon;
4227
+ exports.Attachment = Attachment;
4228
+ exports.AttachmentInfo = AttachmentInfo;
4229
+ exports.AttachmentPreview = AttachmentPreview;
4230
+ exports.AttachmentRemove = AttachmentRemove;
4231
+ exports.Attachments = Attachments;
3798
4232
  exports.BotIcon = BotIcon;
3799
4233
  exports.BrainIcon = BrainIcon;
3800
4234
  exports.BugIcon = BugIcon;
@@ -3809,73 +4243,92 @@ exports.ClipboardListIcon = ClipboardListIcon;
3809
4243
  exports.ClockIcon = ClockIcon;
3810
4244
  exports.CodeBlock = CodeBlock;
3811
4245
  exports.CodeIcon = CodeIcon;
3812
- exports.CompactToolRow = CompactToolRow;
3813
- exports.CompactToolStatusLine = CompactToolStatusLine;
4246
+ exports.Conversation = Conversation;
4247
+ exports.ConversationContent = ConversationContent;
4248
+ exports.ConversationEmptyState = ConversationEmptyState;
4249
+ exports.ConversationScrollButton = ConversationScrollButton;
3814
4250
  exports.CopyIcon = CopyIcon;
3815
- exports.DEFAULT_DISPLAY_CONFIG = DEFAULT_DISPLAY_CONFIG;
3816
- exports.DisplayModeProvider = DisplayModeProvider;
3817
- exports.DisplayModeToggle = DisplayModeToggle;
4251
+ exports.DEFAULT_STYLE_CONFIG = DEFAULT_STYLE_CONFIG;
3818
4252
  exports.EditIcon = EditIcon;
3819
4253
  exports.EnvVarsPanel = EnvVarsPanel;
3820
4254
  exports.ErrorIcon = ErrorIcon;
3821
- exports.ErrorMessage = ErrorMessage;
3822
- exports.FileBadge = FileBadge;
4255
+ exports.FileBadgeCompact = FileBadgeCompact;
3823
4256
  exports.FileIcon = FileIcon;
3824
4257
  exports.FilePlusIcon = FilePlusIcon;
3825
4258
  exports.FolderSearchIcon = FolderSearchIcon;
3826
4259
  exports.GlobeIcon = GlobeIcon;
4260
+ exports.HomeIcon = HomeIcon;
3827
4261
  exports.InfoIcon = InfoIcon;
3828
4262
  exports.JsonDisplay = JsonDisplay;
3829
4263
  exports.LazyMarkdown = LazyMarkdown;
3830
4264
  exports.ListChecksIcon = ListChecksIcon;
3831
4265
  exports.LoaderIcon = LoaderIcon;
3832
- exports.LoadingIndicator = LoadingIndicator;
4266
+ exports.LoadingDots = LoadingDots;
4267
+ exports.LoadingSpinner = LoadingSpinner;
3833
4268
  exports.LogsPanel = LogsPanel;
3834
- exports.MessageEntry = MessageEntry;
3835
- exports.MessageList = MessageList;
4269
+ exports.Message = Message;
4270
+ exports.MessageAction = MessageAction;
4271
+ exports.MessageActions = MessageActions;
4272
+ exports.MessageAvatar = MessageAvatar;
4273
+ exports.MessageContent = MessageContent;
4274
+ exports.MessageResponse = MessageResponse;
4275
+ exports.MessageShimmer = MessageShimmer;
3836
4276
  exports.MessageSquareIcon = MessageSquareIcon;
4277
+ exports.MessageTimestamp = MessageTimestamp;
4278
+ exports.MicrophoneIcon = MicrophoneIcon;
3837
4279
  exports.MoonIcon = MoonIcon;
3838
4280
  exports.OptionCards = OptionCards;
3839
4281
  exports.PaperclipIcon = PaperclipIcon;
3840
4282
  exports.PlugIcon = PlugIcon;
3841
- exports.RichContentRenderer = RichContentRenderer;
4283
+ exports.Reasoning = Reasoning;
4284
+ exports.ReasoningContent = ReasoningContent;
4285
+ exports.ReasoningTrigger = ReasoningTrigger;
3842
4286
  exports.SearchIcon = SearchIcon;
3843
4287
  exports.SendIcon = SendIcon;
4288
+ exports.Shimmer = Shimmer;
4289
+ exports.ShimmerBlock = ShimmerBlock;
4290
+ exports.ShimmerLine = ShimmerLine;
4291
+ exports.ShimmerText = ShimmerText;
3844
4292
  exports.SparklesIcon = SparklesIcon;
3845
4293
  exports.SpinnerIcon = SpinnerIcon;
3846
4294
  exports.StatusIndicator = StatusIndicator;
3847
- exports.StepAccordion = StepAccordion;
3848
4295
  exports.StopCircleIcon = StopCircleIcon;
3849
- exports.StreamingText = StreamingText;
3850
4296
  exports.SunIcon = SunIcon;
4297
+ exports.Task = Task;
4298
+ exports.TaskContent = TaskContent;
4299
+ exports.TaskItem = TaskItem;
4300
+ exports.TaskList = TaskList;
4301
+ exports.TaskTrigger = TaskTrigger;
3851
4302
  exports.TerminalIcon = TerminalIcon;
3852
- exports.ThemeProvider = ThemeProvider;
3853
- exports.ThinkingMessage = ThinkingMessage;
3854
- exports.TodoPanel = TodoPanel;
3855
- exports.ToolCallCard = ToolCallCard;
3856
- exports.ToolCallMessage = ToolCallMessage;
3857
- exports.ToolExecutionGroup = ToolExecutionGroup;
4303
+ exports.ThinkingIndicator = ThinkingIndicator;
4304
+ exports.Tool = Tool;
4305
+ exports.ToolHeader = ToolHeader;
3858
4306
  exports.ToolIcon = ToolIcon;
3859
- exports.TypewriterText = TypewriterText;
4307
+ exports.ToolInput = ToolInput;
4308
+ exports.ToolList = ToolList;
4309
+ exports.ToolOutput = ToolOutput;
3860
4310
  exports.UserIcon = UserIcon;
3861
- exports.UserMessage = UserMessage;
3862
4311
  exports.XCircleIcon = XCircleIcon;
3863
4312
  exports.XIcon = XIcon;
3864
4313
  exports.allKeyframesCss = allKeyframesCss;
4314
+ exports.applyEventMiddleware = applyEventMiddleware;
4315
+ exports.applyRequestMiddleware = applyRequestMiddleware;
3865
4316
  exports.borderRadius = borderRadius;
4317
+ exports.callMiddlewareComplete = callMiddlewareComplete;
4318
+ exports.callMiddlewareError = callMiddlewareError;
3866
4319
  exports.cn = cn;
3867
4320
  exports.colors = colors;
3868
4321
  exports.createToolCall = createToolCall;
4322
+ exports.cssVars = cssVars;
3869
4323
  exports.extractTextContent = extractTextContent;
3870
- exports.extractToolCallsFromGroup = extractToolCallsFromGroup;
3871
4324
  exports.formatElapsedTime = formatElapsedTime;
3872
4325
  exports.formatFileSize = formatFileSize;
4326
+ exports.formatFileSizeHook = formatFileSize2;
3873
4327
  exports.formatTimestamp = formatTimestamp;
3874
4328
  exports.formatToolName = formatToolName;
3875
4329
  exports.generateToolSummary = generateToolSummary;
3876
4330
  exports.getActionIcon = getActionIcon;
3877
4331
  exports.getActionLabel = getActionLabel;
3878
- exports.groupEntriesForCompactMode = groupEntriesForCompactMode;
3879
4332
  exports.inlineStyles = inlineStyles;
3880
4333
  exports.isAgentToolAction = isAgentToolAction;
3881
4334
  exports.isCommandRunAction = isCommandRunAction;
@@ -3895,7 +4348,11 @@ exports.isWidgetEntry = isWidgetEntry;
3895
4348
  exports.keyframes = keyframes;
3896
4349
  exports.keyframesCss = keyframesCss;
3897
4350
  exports.mapToolToActionType = mapToolToActionType;
4351
+ exports.messageToNormalizedEntry = messageToNormalizedEntry;
4352
+ exports.messagesToNormalizedEntries = messagesToNormalizedEntries;
3898
4353
  exports.normalizeToolResult = normalizeToolResult;
4354
+ exports.normalizedEntriesToMessages = normalizedEntriesToMessages;
4355
+ exports.normalizedEntryToMessage = normalizedEntryToMessage;
3899
4356
  exports.parseCommandResult = parseCommandResult;
3900
4357
  exports.parseMcpToolName = parseMcpToolName;
3901
4358
  exports.parseOptionsFromContent = parseOptionsFromContent;
@@ -3907,13 +4364,17 @@ exports.truncate = truncate;
3907
4364
  exports.typography = typography;
3908
4365
  exports.updateToolCallWithResult = updateToolCallWithResult;
3909
4366
  exports.useAgentChat = useAgentChat;
3910
- exports.useDisplayConfig = useDisplayConfig;
3911
- exports.useDisplayMode = useDisplayMode;
4367
+ exports.useAttachment = useAttachment;
4368
+ exports.useChat = useChat;
4369
+ exports.useConversation = useConversation;
3912
4370
  exports.useFileUpload = useFileUpload;
3913
4371
  exports.useLongTextConversion = useLongTextConversion;
4372
+ exports.useMessage = useMessage;
3914
4373
  exports.useMessageQueue = useMessageQueue;
4374
+ exports.useReasoning = useReasoning;
3915
4375
  exports.useStopExecution = useStopExecution;
3916
- exports.useTheme = useTheme;
4376
+ exports.useTask = useTask;
4377
+ exports.useTool = useTool;
3917
4378
  exports.widget = widget;
3918
4379
  exports.zIndex = zIndex;
3919
4380
  //# sourceMappingURL=index.cjs.map