@ash-cloud/ash-ui 0.1.1 → 0.2.0

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