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