@ash-cloud/ash-ui 0.0.3 → 0.0.5

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
@@ -636,6 +636,16 @@ function ClipboardListIcon({ className }) {
636
636
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 16h.01" })
637
637
  ] });
638
638
  }
639
+ function SpinnerIcon({ className }) {
640
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) });
641
+ }
642
+ function ErrorIcon({ className }) {
643
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
644
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
645
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "15", y1: "9", x2: "9", y2: "15" }),
646
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", y1: "9", x2: "15", y2: "15" })
647
+ ] });
648
+ }
639
649
  function StatusIndicator({ status, size = "sm", className }) {
640
650
  const sizeClasses = {
641
651
  sm: "w-2 h-2",
@@ -1534,6 +1544,163 @@ function ToolExecutionGroup({
1534
1544
  }
1535
1545
  );
1536
1546
  }
1547
+ function formatDuration(startMs, endMs) {
1548
+ const duration = endMs - startMs;
1549
+ if (duration < 1e3) return `${duration}ms`;
1550
+ return `${(duration / 1e3).toFixed(1)}s`;
1551
+ }
1552
+ function getToolLabel(toolName, summary) {
1553
+ if (summary && summary.length > 0 && summary.length < 50) {
1554
+ return summary;
1555
+ }
1556
+ const cleaned = toolName.replace(/^mcp__[^_]+__/, "").replace(/Tool$/, "").replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2");
1557
+ return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);
1558
+ }
1559
+ function toStepStatus(status) {
1560
+ switch (status) {
1561
+ case "pending":
1562
+ return "running";
1563
+ case "success":
1564
+ return "success";
1565
+ case "failed":
1566
+ return "error";
1567
+ default:
1568
+ return "pending";
1569
+ }
1570
+ }
1571
+ function StepIcon({ status }) {
1572
+ const iconClass = "w-3.5 h-3.5";
1573
+ switch (status) {
1574
+ case "running":
1575
+ return /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, { className: cn(iconClass, "animate-spin text-[var(--ash-accent)]") });
1576
+ case "success":
1577
+ return /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: cn(iconClass, "text-[var(--ash-accent)]") });
1578
+ case "error":
1579
+ return /* @__PURE__ */ jsxRuntime.jsx(ErrorIcon, { className: cn(iconClass, "text-red-500") });
1580
+ default:
1581
+ return /* @__PURE__ */ jsxRuntime.jsx(ToolIcon, { className: cn(iconClass, "text-white/40") });
1582
+ }
1583
+ }
1584
+ function StepAccordion({
1585
+ toolCalls,
1586
+ defaultExpanded = false,
1587
+ isExpanded: controlledExpanded,
1588
+ onToggle,
1589
+ className
1590
+ }) {
1591
+ const [internalExpanded, setInternalExpanded] = react.useState(defaultExpanded);
1592
+ const isExpanded = controlledExpanded !== void 0 ? controlledExpanded : internalExpanded;
1593
+ const handleToggle = react.useCallback(() => {
1594
+ if (onToggle) {
1595
+ onToggle();
1596
+ } else {
1597
+ setInternalExpanded((prev) => !prev);
1598
+ }
1599
+ }, [onToggle]);
1600
+ if (toolCalls.length === 0) {
1601
+ return null;
1602
+ }
1603
+ const completedSteps = toolCalls.filter((tc) => tc.status === "success" || tc.status === "failed").length;
1604
+ const runningStep = toolCalls.find((tc) => tc.status === "pending");
1605
+ const hasError = toolCalls.some((tc) => tc.status === "failed");
1606
+ const allComplete = completedSteps === toolCalls.length;
1607
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1608
+ "div",
1609
+ {
1610
+ className: cn(
1611
+ "rounded-xl border overflow-hidden",
1612
+ hasError ? "border-red-500/30" : allComplete ? "border-[var(--ash-accent)]/30" : "border-yellow-500/30",
1613
+ className
1614
+ ),
1615
+ children: [
1616
+ /* @__PURE__ */ jsxRuntime.jsxs(
1617
+ "button",
1618
+ {
1619
+ type: "button",
1620
+ onClick: handleToggle,
1621
+ className: "w-full flex items-center gap-2 px-3 py-2 bg-white/5 hover:bg-white/10 transition-colors text-left cursor-pointer",
1622
+ children: [
1623
+ /* @__PURE__ */ jsxRuntime.jsx(
1624
+ ChevronDownIcon,
1625
+ {
1626
+ className: cn(
1627
+ "w-4 h-4 text-white/40 transition-transform duration-200 flex-shrink-0",
1628
+ !isExpanded && "-rotate-90"
1629
+ )
1630
+ }
1631
+ ),
1632
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0 flex items-center gap-2", children: runningStep ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1633
+ /* @__PURE__ */ jsxRuntime.jsx(SpinnerIcon, { className: "w-3.5 h-3.5 animate-spin text-[var(--ash-accent)] flex-shrink-0" }),
1634
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-white/80 truncate", children: getToolLabel(runningStep.toolName, runningStep.summary) })
1635
+ ] }) : hasError ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1636
+ /* @__PURE__ */ jsxRuntime.jsx(ErrorIcon, { className: "w-3.5 h-3.5 text-red-500 flex-shrink-0" }),
1637
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-red-400 truncate", children: "Completed with errors" })
1638
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1639
+ /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: "w-3.5 h-3.5 text-[var(--ash-accent)] flex-shrink-0" }),
1640
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-white/70 truncate", children: [
1641
+ completedSteps,
1642
+ " step",
1643
+ completedSteps !== 1 ? "s" : "",
1644
+ " completed"
1645
+ ] })
1646
+ ] }) }),
1647
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs px-1.5 py-0.5 rounded-full bg-white/10 text-white/50 flex-shrink-0", children: [
1648
+ completedSteps,
1649
+ "/",
1650
+ toolCalls.length
1651
+ ] })
1652
+ ]
1653
+ }
1654
+ ),
1655
+ isExpanded && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-white/10 ash-accordion-content", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-white/5", children: toolCalls.map((toolCall, index) => {
1656
+ const stepStatus = toStepStatus(toolCall.status);
1657
+ const startTime = toolCall.startedAt ? new Date(toolCall.startedAt).getTime() : 0;
1658
+ const endTime = toolCall.completedAt ? new Date(toolCall.completedAt).getTime() : 0;
1659
+ const hasDuration = startTime > 0 && endTime > 0;
1660
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1661
+ "div",
1662
+ {
1663
+ className: cn(
1664
+ "px-3 py-2 flex items-start gap-2",
1665
+ stepStatus === "running" && "bg-[var(--ash-accent)]/5",
1666
+ stepStatus === "error" && "bg-red-500/5"
1667
+ ),
1668
+ children: [
1669
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 flex-shrink-0 pt-0.5", children: [
1670
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-white/40 w-4 text-right", children: [
1671
+ index + 1,
1672
+ "."
1673
+ ] }),
1674
+ /* @__PURE__ */ jsxRuntime.jsx(StepIcon, { status: stepStatus })
1675
+ ] }),
1676
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
1677
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1678
+ /* @__PURE__ */ jsxRuntime.jsx(
1679
+ "span",
1680
+ {
1681
+ className: cn(
1682
+ "text-sm",
1683
+ stepStatus === "running" && "text-[var(--ash-accent)]",
1684
+ stepStatus === "success" && "text-white/70",
1685
+ stepStatus === "error" && "text-red-400",
1686
+ stepStatus === "pending" && "text-white/40"
1687
+ ),
1688
+ children: getToolLabel(toolCall.toolName, toolCall.summary)
1689
+ }
1690
+ ),
1691
+ hasDuration && (stepStatus === "success" || stepStatus === "error") && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-white/40", children: formatDuration(startTime, endTime) })
1692
+ ] }),
1693
+ toolCall.isError && toolCall.actionType && "result" in toolCall.actionType && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs mt-1 text-red-400/80 truncate", children: String(toolCall.actionType.result?.value || "Error") })
1694
+ ] })
1695
+ ]
1696
+ },
1697
+ toolCall.id
1698
+ );
1699
+ }) }) })
1700
+ ]
1701
+ }
1702
+ );
1703
+ }
1537
1704
 
1538
1705
  // src/types.ts
1539
1706
  function isCommandRunAction(action) {
@@ -1575,6 +1742,9 @@ function isToolCallEntry(entry) {
1575
1742
  function isErrorEntry(entry) {
1576
1743
  return entry.type === "error";
1577
1744
  }
1745
+ function isWidgetEntry(entry) {
1746
+ return entry.type === "widget";
1747
+ }
1578
1748
  var DEFAULT_DISPLAY_CONFIG = {
1579
1749
  mode: "inline",
1580
1750
  breakEveryNToolCalls: 0,
@@ -1630,10 +1800,25 @@ function MessageList({
1630
1800
  streamingContent,
1631
1801
  displayConfig: displayConfigProp,
1632
1802
  onOptionSelect,
1803
+ renderWidget,
1804
+ onWidgetAction,
1633
1805
  className
1634
1806
  }) {
1635
1807
  const contextConfig = useDisplayConfig();
1636
1808
  const config = displayConfigProp || contextConfig;
1809
+ const createWidgetActionHandler = react.useCallback(
1810
+ (entryId, widgetType) => {
1811
+ if (!onWidgetAction) return void 0;
1812
+ return (action) => {
1813
+ onWidgetAction({
1814
+ ...action,
1815
+ entryId,
1816
+ widgetType
1817
+ });
1818
+ };
1819
+ },
1820
+ [onWidgetAction]
1821
+ );
1637
1822
  const groupedEntries = react.useMemo(() => {
1638
1823
  if (config.mode === "inline") {
1639
1824
  return entries.map((entry) => ({
@@ -1647,12 +1832,31 @@ function MessageList({
1647
1832
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex-1 overflow-y-auto p-4 space-y-4 ash-scrollbar", className), children: [
1648
1833
  groupedEntries.map((groupedEntry) => {
1649
1834
  if (groupedEntry.type === "single") {
1650
- return /* @__PURE__ */ jsxRuntime.jsx(MessageEntry, { entry: groupedEntry.entry, onOptionSelect }, groupedEntry.entry.id);
1835
+ const entry = groupedEntry.entry;
1836
+ if (entry.entryType.type === "widget" && renderWidget) {
1837
+ const widgetEntry = entry.entryType;
1838
+ const widgetContent = renderWidget({
1839
+ entry,
1840
+ widgetType: widgetEntry.widgetType,
1841
+ widgetData: widgetEntry.widgetData,
1842
+ onAction: createWidgetActionHandler(entry.id, widgetEntry.widgetType)
1843
+ });
1844
+ if (widgetContent !== null) {
1845
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ash-animate-fade-in", children: widgetContent }, entry.id);
1846
+ }
1847
+ }
1848
+ return /* @__PURE__ */ jsxRuntime.jsx(MessageEntry, { entry, onOptionSelect }, entry.id);
1651
1849
  }
1652
1850
  const toolCalls = extractToolCallsFromGroup(groupedEntry.entries);
1653
1851
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 ash-animate-fade-in", children: [
1654
1852
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7 h-7 rounded-full bg-[var(--ash-accent)]/20 flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(BotIcon, { className: "w-4 h-4 text-[var(--ash-accent)]" }) }),
1655
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
1853
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: config.mode === "accordion" ? /* @__PURE__ */ jsxRuntime.jsx(
1854
+ StepAccordion,
1855
+ {
1856
+ toolCalls,
1857
+ defaultExpanded: config.defaultExpanded
1858
+ }
1859
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1656
1860
  ToolExecutionGroup,
1657
1861
  {
1658
1862
  toolCalls,
@@ -2072,19 +2276,22 @@ function EnvVarsPanel({
2072
2276
  function DisplayModeToggle({
2073
2277
  className,
2074
2278
  showLabel = true,
2075
- labels = { inline: "Inline", compact: "Compact" }
2279
+ labels = { inline: "Inline", compact: "Compact", accordion: "Steps" },
2280
+ modes = ["inline", "compact", "accordion"]
2076
2281
  }) {
2077
- const { config, toggleMode } = useDisplayMode();
2078
- const isInline = config.mode === "inline";
2079
- return /* @__PURE__ */ jsxRuntime.jsx(
2080
- "button",
2081
- {
2082
- type: "button",
2083
- onClick: toggleMode,
2084
- className: cn("ash-display-mode-toggle", className),
2085
- title: isInline ? "Switch to compact mode" : "Switch to inline mode",
2086
- children: isInline ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2087
- /* @__PURE__ */ jsxRuntime.jsx(
2282
+ const { config, setMode } = useDisplayMode();
2283
+ const currentMode = config.mode;
2284
+ const currentIndex = modes.indexOf(currentMode);
2285
+ const effectiveIndex = currentIndex === -1 ? 0 : currentIndex;
2286
+ const nextIndex = (effectiveIndex + 1) % modes.length;
2287
+ const nextMode = modes[nextIndex];
2288
+ const handleClick = () => {
2289
+ setMode(nextMode);
2290
+ };
2291
+ const getIcon = (mode) => {
2292
+ switch (mode) {
2293
+ case "inline":
2294
+ return /* @__PURE__ */ jsxRuntime.jsx(
2088
2295
  "svg",
2089
2296
  {
2090
2297
  className: "ash-display-mode-icon",
@@ -2101,10 +2308,9 @@ function DisplayModeToggle({
2101
2308
  }
2102
2309
  )
2103
2310
  }
2104
- ),
2105
- showLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ash-display-mode-label", children: labels.inline })
2106
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2107
- /* @__PURE__ */ jsxRuntime.jsx(
2311
+ );
2312
+ case "compact":
2313
+ return /* @__PURE__ */ jsxRuntime.jsx(
2108
2314
  "svg",
2109
2315
  {
2110
2316
  className: "ash-display-mode-icon",
@@ -2121,9 +2327,42 @@ function DisplayModeToggle({
2121
2327
  }
2122
2328
  )
2123
2329
  }
2124
- ),
2125
- showLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ash-display-mode-label", children: labels.compact })
2126
- ] })
2330
+ );
2331
+ case "accordion":
2332
+ return /* @__PURE__ */ jsxRuntime.jsx(
2333
+ "svg",
2334
+ {
2335
+ className: "ash-display-mode-icon",
2336
+ viewBox: "0 0 24 24",
2337
+ fill: "none",
2338
+ stroke: "currentColor",
2339
+ strokeWidth: "1.5",
2340
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2341
+ "path",
2342
+ {
2343
+ strokeLinecap: "round",
2344
+ strokeLinejoin: "round",
2345
+ 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"
2346
+ }
2347
+ )
2348
+ }
2349
+ );
2350
+ }
2351
+ };
2352
+ const getLabel = (mode) => {
2353
+ return labels[mode] || mode.charAt(0).toUpperCase() + mode.slice(1);
2354
+ };
2355
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2356
+ "button",
2357
+ {
2358
+ type: "button",
2359
+ onClick: handleClick,
2360
+ className: cn("ash-display-mode-toggle", className),
2361
+ title: `Switch to ${nextMode} mode`,
2362
+ children: [
2363
+ getIcon(currentMode),
2364
+ showLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ash-display-mode-label", children: getLabel(currentMode) })
2365
+ ]
2127
2366
  }
2128
2367
  );
2129
2368
  }
@@ -2759,6 +2998,7 @@ exports.DisplayModeProvider = DisplayModeProvider;
2759
2998
  exports.DisplayModeToggle = DisplayModeToggle;
2760
2999
  exports.EditIcon = EditIcon;
2761
3000
  exports.EnvVarsPanel = EnvVarsPanel;
3001
+ exports.ErrorIcon = ErrorIcon;
2762
3002
  exports.ErrorMessage = ErrorMessage;
2763
3003
  exports.FileIcon = FileIcon;
2764
3004
  exports.FilePlusIcon = FilePlusIcon;
@@ -2780,7 +3020,9 @@ exports.PlugIcon = PlugIcon;
2780
3020
  exports.SearchIcon = SearchIcon;
2781
3021
  exports.SendIcon = SendIcon;
2782
3022
  exports.SparklesIcon = SparklesIcon;
3023
+ exports.SpinnerIcon = SpinnerIcon;
2783
3024
  exports.StatusIndicator = StatusIndicator;
3025
+ exports.StepAccordion = StepAccordion;
2784
3026
  exports.StopCircleIcon = StopCircleIcon;
2785
3027
  exports.StreamingText = StreamingText;
2786
3028
  exports.SunIcon = SunIcon;
@@ -2825,6 +3067,7 @@ exports.isTodoWriteAction = isTodoWriteAction;
2825
3067
  exports.isToolCallEntry = isToolCallEntry;
2826
3068
  exports.isWebFetchAction = isWebFetchAction;
2827
3069
  exports.isWebSearchAction = isWebSearchAction;
3070
+ exports.isWidgetEntry = isWidgetEntry;
2828
3071
  exports.keyframes = keyframes;
2829
3072
  exports.keyframesCss = keyframesCss;
2830
3073
  exports.mapToolToActionType = mapToolToActionType;