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