@ash-cloud/ash-ui 0.1.1 → 0.2.1

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