@keboola/design 11.4.0 → 11.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/AGENTS.md +198 -7
  2. package/CHANGELOG.md +383 -0
  3. package/COMPONENT-API.md +73 -52
  4. package/dist/{AIKit-DDg5DBCG.js → AIKit-BExvt9pt.js} +390 -70
  5. package/dist/AIKit-BExvt9pt.js.map +1 -0
  6. package/dist/{Alert-DHJFrWnS.js → Alert-Ba5b2YPq.js} +2 -2
  7. package/dist/{Alert-DHJFrWnS.js.map → Alert-Ba5b2YPq.js.map} +1 -1
  8. package/dist/{Badge-Ch26CXoP.js → Badge-D0pXdekt.js} +2 -2
  9. package/dist/Badge-D0pXdekt.js.map +1 -0
  10. package/dist/{BrandEditor-Go7w8Zj2.js → BrandEditor-CSANsxUz.js} +6 -6
  11. package/dist/{BrandEditor-Go7w8Zj2.js.map → BrandEditor-CSANsxUz.js.map} +1 -1
  12. package/dist/{BrandLogo-Dmi7jo21.js → BrandLogo-CoKBk4MI.js} +4 -4
  13. package/dist/BrandLogo-CoKBk4MI.js.map +1 -0
  14. package/dist/{Breadcrumb-uEkM6adI.js → Breadcrumb-g_xn_-Nf.js} +2 -2
  15. package/dist/{Breadcrumb-uEkM6adI.js.map → Breadcrumb-g_xn_-Nf.js.map} +1 -1
  16. package/dist/{Checkbox-fC0BuO2B.js → Checkbox-CUIn2jap.js} +2 -2
  17. package/dist/{Checkbox-fC0BuO2B.js.map → Checkbox-CUIn2jap.js.map} +1 -1
  18. package/dist/{Clipboard-ChPdiUy3.js → Clipboard-BTW6C-b_.js} +2 -2
  19. package/dist/{Clipboard-ChPdiUy3.js.map → Clipboard-BTW6C-b_.js.map} +1 -1
  20. package/dist/{ClipboardButton-D9pDjgkB.js → ClipboardButton-CwZJdRaz.js} +2 -2
  21. package/dist/{ClipboardButton-D9pDjgkB.js.map → ClipboardButton-CwZJdRaz.js.map} +1 -1
  22. package/dist/{CodeEditor-Cxy3HnKH.js → CodeEditor-C9zy7PxK.js} +6 -6
  23. package/dist/{CodeEditor-Cxy3HnKH.js.map → CodeEditor-C9zy7PxK.js.map} +1 -1
  24. package/dist/{ColorInput-Bxj-1eLT.js → ColorInput-BZV-u_94.js} +3 -3
  25. package/dist/{ColorInput-Bxj-1eLT.js.map → ColorInput-BZV-u_94.js.map} +1 -1
  26. package/dist/{Combobox-Cs_1pj2q.js → Combobox-BfIfaVMA.js} +273 -104
  27. package/dist/Combobox-BfIfaVMA.js.map +1 -0
  28. package/dist/{Command-B7ML9YMv.js → Command-Bfa1kpbt.js} +2 -2
  29. package/dist/{Command-B7ML9YMv.js.map → Command-Bfa1kpbt.js.map} +1 -1
  30. package/dist/{CopyField-BO_sh-HO.js → CopyField-DpT0Ngz1.js} +2 -2
  31. package/dist/{CopyField-BO_sh-HO.js.map → CopyField-DpT0Ngz1.js.map} +1 -1
  32. package/dist/{DateInput-ZdaMQo5i.js → DateInput-DiEd5uCL.js} +3 -3
  33. package/dist/{DateInput-ZdaMQo5i.js.map → DateInput-DiEd5uCL.js.map} +1 -1
  34. package/dist/{DateRangeInput-DkvNh_sT.js → DateRangeInput-DPfVYchl.js} +3 -3
  35. package/dist/{DateRangeInput-DkvNh_sT.js.map → DateRangeInput-DPfVYchl.js.map} +1 -1
  36. package/dist/DiagramSource-C5_d8Gdj.js +11 -0
  37. package/dist/DiagramSource-C5_d8Gdj.js.map +1 -0
  38. package/dist/{FieldItem-CCd9-xnp.js → FieldItem-B7XMmZSz.js} +3 -3
  39. package/dist/{FieldItem-CCd9-xnp.js.map → FieldItem-B7XMmZSz.js.map} +1 -1
  40. package/dist/{Form-k6PH-em3.js → Form-CsttTHn4.js} +7 -7
  41. package/dist/Form-CsttTHn4.js.map +1 -0
  42. package/dist/{FormGroup-DbcDtrgU.js → FormGroup-DHvTFdno.js} +3 -3
  43. package/dist/{FormGroup-DbcDtrgU.js.map → FormGroup-DHvTFdno.js.map} +1 -1
  44. package/dist/{FullScreenModal-BvPuYibe.js → FullScreenModal-CN9nAs72.js} +50 -2
  45. package/dist/FullScreenModal-CN9nAs72.js.map +1 -0
  46. package/dist/{InputGroup-BnLHY0eq.js → InputGroup-DcZRlVnN.js} +22 -14
  47. package/dist/InputGroup-DcZRlVnN.js.map +1 -0
  48. package/dist/{Kbd-CvSM3D3-.js → Kbd-Cvvt91Q8.js} +14 -4
  49. package/dist/Kbd-Cvvt91Q8.js.map +1 -0
  50. package/dist/{MermaidDiagram-WkonYErO.js → MermaidDiagram-DSI7xMu2.js} +21 -19
  51. package/dist/MermaidDiagram-DSI7xMu2.js.map +1 -0
  52. package/dist/{MultiActionButtonGroup-CxWH7yWK.js → MultiActionButtonGroup-DokHbPgM.js} +2 -2
  53. package/dist/{MultiActionButtonGroup-CxWH7yWK.js.map → MultiActionButtonGroup-DokHbPgM.js.map} +1 -1
  54. package/dist/{Navbar-B2UsolVU.js → Navbar-Liy1TPI7.js} +6 -6
  55. package/dist/{Navbar-B2UsolVU.js.map → Navbar-Liy1TPI7.js.map} +1 -1
  56. package/dist/PageHeader-Q3IpCnQd.js.map +1 -1
  57. package/dist/{PageToolbar-DnbFt14Q.js → PageToolbar-DExqEYGl.js} +2 -2
  58. package/dist/{PageToolbar-DnbFt14Q.js.map → PageToolbar-DExqEYGl.js.map} +1 -1
  59. package/dist/{Popconfirm-CILjmzKI.js → Popconfirm-5pBzT_HQ.js} +10 -11
  60. package/dist/{Popconfirm-CILjmzKI.js.map → Popconfirm-5pBzT_HQ.js.map} +1 -1
  61. package/dist/{RadioGroup-XFzFd1ti.js → RadioGroup-DXPbSj8P.js} +2 -2
  62. package/dist/{RadioGroup-XFzFd1ti.js.map → RadioGroup-DXPbSj8P.js.map} +1 -1
  63. package/dist/{Search-4Bxtt8hS.js → Search-5uQAiNIP.js} +2 -2
  64. package/dist/{Search-4Bxtt8hS.js.map → Search-5uQAiNIP.js.map} +1 -1
  65. package/dist/{SearchInput-f_oY9T4Z.js → SearchInput-D4UgAXJK.js} +3 -3
  66. package/dist/{SearchInput-f_oY9T4Z.js.map → SearchInput-D4UgAXJK.js.map} +1 -1
  67. package/dist/{SectionNav-BpJlisbT.js → SectionNav-OkKCjSA-.js} +3 -3
  68. package/dist/{SectionNav-BpJlisbT.js.map → SectionNav-OkKCjSA-.js.map} +1 -1
  69. package/dist/{Select-DpcgKqKB.js → Select-DV3Hak_l.js} +3 -4
  70. package/dist/Select-DV3Hak_l.js.map +1 -0
  71. package/dist/{Sidebar-Cm6I4dE1.js → Sidebar-D3cY1kha.js} +44 -20
  72. package/dist/Sidebar-D3cY1kha.js.map +1 -0
  73. package/dist/{Slider-pPC_3gz9.js → Slider-CRf-bB4y.js} +2 -2
  74. package/dist/{Slider-pPC_3gz9.js.map → Slider-CRf-bB4y.js.map} +1 -1
  75. package/dist/{Switch-ChfPKcTE.js → Switch-B0WdGSjz.js} +7 -7
  76. package/dist/{Switch-ChfPKcTE.js.map → Switch-B0WdGSjz.js.map} +1 -1
  77. package/dist/{TextInput-DHM6dHmn.js → TextInput-DH5SkbsE.js} +3 -3
  78. package/dist/{TextInput-DHM6dHmn.js.map → TextInput-DH5SkbsE.js.map} +1 -1
  79. package/dist/{ThemeProvider-DrQJtzpd.js → ThemeProvider-2RMdVGxO.js} +17 -12
  80. package/dist/{ThemeProvider-DrQJtzpd.js.map → ThemeProvider-2RMdVGxO.js.map} +1 -1
  81. package/dist/{Timeline-6btyJl4D.js → Timeline-BG5Xogxh.js} +11 -11
  82. package/dist/{Timeline-6btyJl4D.js.map → Timeline-BG5Xogxh.js.map} +1 -1
  83. package/dist/{Workbench-BzE-Vvd9.js → Workbench-UR2FBC7I.js} +3 -1
  84. package/dist/Workbench-UR2FBC7I.js.map +1 -0
  85. package/dist/{ZoomableSvg-BWnDkR61.js → ZoomableSvg-pooPb1Cu.js} +3 -3
  86. package/dist/{ZoomableSvg-BWnDkR61.js.map → ZoomableSvg-pooPb1Cu.js.map} +1 -1
  87. package/dist/editors.js +4 -4
  88. package/dist/flint.js +136 -9
  89. package/dist/flint.js.map +1 -1
  90. package/dist/index.d.ts +801 -324
  91. package/dist/index.js +41 -40
  92. package/dist/internal/blocks/AIKit.js +2 -2
  93. package/dist/internal/blocks/AuthLayout.js +118 -0
  94. package/dist/internal/blocks/AuthLayout.js.map +1 -0
  95. package/dist/internal/blocks/BrandEditor.js +1 -1
  96. package/dist/internal/blocks/ClipboardButton.js +1 -1
  97. package/dist/internal/blocks/ColorInput.js +1 -1
  98. package/dist/internal/blocks/Command.js +1 -1
  99. package/dist/internal/blocks/CopyField.js +1 -1
  100. package/dist/internal/blocks/DateInput.js +1 -1
  101. package/dist/internal/blocks/DateRangeInput.js +1 -1
  102. package/dist/internal/blocks/ExportMenu.js +1 -1
  103. package/dist/internal/blocks/Form/connectors/MultiComboboxField.js +1 -1
  104. package/dist/internal/blocks/Form.js +1 -1
  105. package/dist/internal/blocks/FormGroup.js +1 -1
  106. package/dist/internal/blocks/MultiActionButtonGroup.js +1 -1
  107. package/dist/internal/blocks/MultiCombobox.js +107 -48
  108. package/dist/internal/blocks/MultiCombobox.js.map +1 -1
  109. package/dist/internal/blocks/NumberInput.js +1 -1
  110. package/dist/internal/blocks/PageToolbar.js +1 -1
  111. package/dist/internal/blocks/PasswordInput.js +2 -2
  112. package/dist/internal/blocks/SearchInput.js +1 -1
  113. package/dist/internal/blocks/SearchInputWithNavigation.js +2 -2
  114. package/dist/internal/blocks/SectionNav.js +1 -1
  115. package/dist/internal/blocks/TextInput.js +1 -1
  116. package/dist/internal/blocks/Textarea.js +1 -1
  117. package/dist/internal/blocks/TimeInput.js +2 -2
  118. package/dist/internal/components/Alert.js +1 -1
  119. package/dist/internal/components/Avatar.js +1 -1
  120. package/dist/internal/components/Badge.js +1 -1
  121. package/dist/internal/components/BrandLogo.js +1 -1
  122. package/dist/internal/components/Breadcrumb.js +1 -1
  123. package/dist/internal/components/Checkbox.js +1 -1
  124. package/dist/internal/components/Clipboard.js +1 -1
  125. package/dist/internal/components/CollapsiblePanel.js +1 -1
  126. package/dist/internal/components/Combobox.js +2 -2
  127. package/dist/internal/components/FullScreenModal.js +1 -1
  128. package/dist/internal/components/InputGroup.js +1 -1
  129. package/dist/internal/components/Kbd.js +1 -1
  130. package/dist/internal/components/Navbar.js +1 -1
  131. package/dist/internal/components/Popconfirm.js +1 -1
  132. package/dist/internal/components/Progress.js +3 -1
  133. package/dist/internal/components/Progress.js.map +1 -1
  134. package/dist/internal/components/RadioGroup.js +1 -1
  135. package/dist/internal/components/Search.js +1 -1
  136. package/dist/internal/components/Select.js +1 -1
  137. package/dist/internal/components/Sidebar.js +1 -1
  138. package/dist/internal/components/Slider.js +1 -1
  139. package/dist/internal/components/Switch.js +1 -1
  140. package/dist/internal/components/ThemeProvider.js +1 -1
  141. package/dist/internal/components/Timeline.js +1 -1
  142. package/dist/internal/components/Tooltip.js +1 -1
  143. package/dist/internal/components/Truncated.js +1 -1
  144. package/dist/internal/components/Workbench.js +1 -1
  145. package/dist/internal/components/ZoomableSvg.js +1 -1
  146. package/dist/internal/utils.js +2 -2
  147. package/dist/markdown-editor.js +29 -23
  148. package/dist/markdown-editor.js.map +1 -1
  149. package/dist/mermaid.js +1 -1
  150. package/dist/rjsf.js +22 -23
  151. package/dist/rjsf.js.map +1 -1
  152. package/dist/styles.css +1 -1
  153. package/dist/{svgTransforms-C9njt92h.js → svgTransforms-CJAYPk7o.js} +2 -10
  154. package/dist/svgTransforms-CJAYPk7o.js.map +1 -0
  155. package/dist/testing.js +1 -1
  156. package/dist/{useColorSchemeState-klihG9-i.js → themeScopeContext-D7fSyb06.js} +16 -3
  157. package/dist/themeScopeContext-D7fSyb06.js.map +1 -0
  158. package/dist/{themeTokens-D70HeNVF.js → themeTokens-qP1-55O7.js} +37 -10
  159. package/dist/themeTokens-qP1-55O7.js.map +1 -0
  160. package/dist/{utils-B5Z52hYs.js → utils-DSvrhU8E.js} +6 -2
  161. package/dist/{utils-B5Z52hYs.js.map → utils-DSvrhU8E.js.map} +1 -1
  162. package/dist/vega.d.ts +1 -1
  163. package/dist/vega.js +3 -3
  164. package/dist/vega.js.map +1 -1
  165. package/dist/{vegaTheme-DfoEQhOy.js → vegaTheme-CSvFEMBl.js} +6 -6
  166. package/dist/vegaTheme-CSvFEMBl.js.map +1 -0
  167. package/package.json +11 -9
  168. package/src/styles.css +3 -0
  169. package/dist/AIKit-DDg5DBCG.js.map +0 -1
  170. package/dist/Badge-Ch26CXoP.js.map +0 -1
  171. package/dist/BrandLogo-Dmi7jo21.js.map +0 -1
  172. package/dist/Combobox-Cs_1pj2q.js.map +0 -1
  173. package/dist/Form-k6PH-em3.js.map +0 -1
  174. package/dist/FullScreenModal-BvPuYibe.js.map +0 -1
  175. package/dist/InputGroup-BnLHY0eq.js.map +0 -1
  176. package/dist/Kbd-CvSM3D3-.js.map +0 -1
  177. package/dist/MermaidDiagram-WkonYErO.js.map +0 -1
  178. package/dist/Select-DpcgKqKB.js.map +0 -1
  179. package/dist/Sidebar-Cm6I4dE1.js.map +0 -1
  180. package/dist/Workbench-BzE-Vvd9.js.map +0 -1
  181. package/dist/svgTransforms-C9njt92h.js.map +0 -1
  182. package/dist/themeTokens-D70HeNVF.js.map +0 -1
  183. package/dist/useColorSchemeState-klihG9-i.js.map +0 -1
  184. package/dist/vegaTheme-DfoEQhOy.js.map +0 -1
@@ -1,26 +1,95 @@
1
1
  import { cn } from "./classNames-BzrkDnOD.js";
2
- import { Tooltip, TooltipProvider } from "./utils-B5Z52hYs.js";
3
- import { AlertCircle, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, CircleCheck, CirclePause, CircleX, Loader2, Maximize2, Minimize2, Play, Send, Sparkles, TriangleAlert, X, XCircle } from "./icons-Cek151jC.js";
2
+ import { Tooltip, TooltipProvider } from "./utils-DSvrhU8E.js";
3
+ import { AlertCircle, ChevronDown, ChevronLeft, ChevronRight, CircleCheck, CirclePause, CircleX, Loader2, Maximize2, Minimize2, Play, Send, Sparkles, ThumbsDown, ThumbsUp, TriangleAlert, X, XCircle } from "./icons-Cek151jC.js";
4
4
  import { Button } from "./Button-DsRrU5F1.js";
5
- import { ClipboardButton } from "./ClipboardButton-D9pDjgkB.js";
6
- import { FullScreenModal, FullScreenModalBody, FullScreenModalHeader } from "./FullScreenModal-BvPuYibe.js";
5
+ import { ButtonGroup } from "./internal/components/ButtonGroup.js";
6
+ import { ClipboardButton } from "./ClipboardButton-CwZJdRaz.js";
7
+ import { FullScreenModal, FullScreenModalBody, FullScreenModalHeader } from "./FullScreenModal-CN9nAs72.js";
7
8
  import { Truncated } from "./internal/components/Truncated.js";
8
9
  import { HoverCard } from "./HoverCard-B9ZMJxjn.js";
9
- import { Command } from "./Command-B7ML9YMv.js";
10
- import { TextInput } from "./TextInput-DHM6dHmn.js";
11
- import { Checkbox } from "./Checkbox-fC0BuO2B.js";
12
- import { Badge } from "./Badge-Ch26CXoP.js";
10
+ import { Command } from "./Command-Bfa1kpbt.js";
11
+ import { TextInput } from "./TextInput-DH5SkbsE.js";
12
+ import { Checkbox } from "./Checkbox-CUIn2jap.js";
13
+ import { Badge } from "./Badge-D0pXdekt.js";
13
14
  import { Textarea } from "./internal/blocks/Textarea.js";
14
15
  import { Table } from "./Table-CvYuTuNU.js";
15
16
  import { DiagramSkeleton } from "./internal/blocks/DiagramShared/DiagramSkeleton.js";
17
+ import { DiagramSource } from "./DiagramSource-C5_d8Gdj.js";
16
18
  import { Progress } from "./internal/components/Progress.js";
17
19
  import { Slot } from "@radix-ui/react-slot";
18
- import { Suspense, isValidElement, lazy, useEffect, useId, useRef, useState } from "react";
20
+ import { isValidElement, useCallback, useEffect, useId, useRef, useState } from "react";
19
21
  import { cva } from "class-variance-authority";
20
22
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
21
23
  import TextareaAutosize from "react-textarea-autosize";
22
24
  import ReactMarkdown from "react-markdown";
23
25
  import remarkGfm from "remark-gfm";
26
+ //#region src/blocks/AIKit/AnswerActions.tsx
27
+ const VOTE_BUTTONS = [{
28
+ vote: "up",
29
+ label: "Good answer",
30
+ icon: /* @__PURE__ */ jsx(ThumbsUp, { className: "tw:size-4" })
31
+ }, {
32
+ vote: "down",
33
+ label: "Bad answer",
34
+ icon: /* @__PURE__ */ jsx(ThumbsDown, { className: "tw:size-4" })
35
+ }];
36
+ /**
37
+ * What a reader can do with a finished answer: take it away, and say whether it was any good.
38
+ *
39
+ * - **Always rendered, never hover-only.** A reveal-on-hover row does not exist for anyone
40
+ * reaching it without a pointer. The buttons are quiet (`invisible`) rather than hidden.
41
+ * - Copy sits first: it is the one action with no round trip.
42
+ * - **A vote can be changed but not withdrawn.** `aria-pressed` says which one is recorded, and
43
+ * both buttons stay focusable so a reader can switch. Pressing the pressed one re-sends the
44
+ * same vote.
45
+ * - The recorded vote and the write are the caller's: the two chats vote against different
46
+ * endpoints, and only the caller knows whether a turn is finished enough to rate.
47
+ */
48
+ const AnswerActions = ({ copyText, vote, onVote }) => {
49
+ return /* @__PURE__ */ jsxs(ButtonGroup, {
50
+ space: "extra-small",
51
+ "aria-label": "Answer actions",
52
+ children: [copyText ? /* @__PURE__ */ jsx(ClipboardButton, {
53
+ text: copyText,
54
+ tooltip: "Copy answer",
55
+ size: "icon-xs",
56
+ variant: "invisible"
57
+ }) : null, VOTE_BUTTONS.map(({ vote: candidate, label, icon }) => {
58
+ const isRecorded = vote === candidate;
59
+ return /* @__PURE__ */ jsx(Tooltip, {
60
+ tooltip: label,
61
+ placement: "top",
62
+ triggerAsChild: true,
63
+ children: /* @__PURE__ */ jsx(Button, {
64
+ size: "icon-xs",
65
+ variant: isRecorded ? "secondary" : "invisible",
66
+ "aria-label": label,
67
+ "aria-pressed": isRecorded,
68
+ onClick: () => onVote(candidate),
69
+ children: icon
70
+ })
71
+ }, candidate);
72
+ })]
73
+ });
74
+ };
75
+ //#endregion
76
+ //#region src/blocks/AIKit/CollapsibleBody.tsx
77
+ const CollapsibleBody = ({ isExpanded, isClosing, onClosed, children }) => {
78
+ const handleTransitionEnd = (event) => {
79
+ if (event.target !== event.currentTarget) return;
80
+ if (event.propertyName !== "grid-template-rows") return;
81
+ if (!isExpanded) onClosed();
82
+ };
83
+ return /* @__PURE__ */ jsx("div", {
84
+ className: cn("tw:grid tw:transition-[grid-template-rows] tw:duration-300 tw:ease-out tw:motion-reduce:transition-none", isExpanded ? "tw:grid-rows-[1fr]" : "tw:grid-rows-[0fr]"),
85
+ onTransitionEnd: handleTransitionEnd,
86
+ children: /* @__PURE__ */ jsx("div", {
87
+ className: "tw:min-h-0 tw:overflow-hidden",
88
+ children: (isExpanded || isClosing) && children
89
+ })
90
+ });
91
+ };
92
+ //#endregion
24
93
  //#region src/blocks/AIKit/CollapsibleStepHeader.tsx
25
94
  const variantClasses = {
26
95
  success: "tw:text-muted-foreground tw:focus-visible:outline-ring tw:not-disabled:hover:text-foreground",
@@ -31,8 +100,7 @@ const variantClasses = {
31
100
  const iconClasses = "tw:size-3.5 tw:shrink-0 tw:basis-3.5 tw:opacity-70";
32
101
  const renderVariantIcon$1 = (variant) => {
33
102
  if (variant === "processing") return /* @__PURE__ */ jsx(Loader2, { className: cn(iconClasses, "tw:animate-spin") });
34
- if (variant === "error") return /* @__PURE__ */ jsx(XCircle, { className: iconClasses });
35
- if (variant === "dismissed") return /* @__PURE__ */ jsx(XCircle, { className: cn(iconClasses, "tw:text-neutral-500") });
103
+ if (variant === "error" || variant === "dismissed") return /* @__PURE__ */ jsx(XCircle, { className: iconClasses });
36
104
  return /* @__PURE__ */ jsx(CircleCheck, { className: iconClasses });
37
105
  };
38
106
  const CollapsibleStepHeader = ({ isExpanded, onToggle, label, variant = "success" }) => {
@@ -51,6 +119,9 @@ const CollapsibleStepHeader = ({ isExpanded, onToggle, label, variant = "success
51
119
  });
52
120
  };
53
121
  //#endregion
122
+ //#region src/blocks/AIKit/hasMotion.ts
123
+ const hasMotion = () => typeof window.matchMedia === "function" && !window.matchMedia("(prefers-reduced-motion: reduce)").matches;
124
+ //#endregion
54
125
  //#region src/blocks/AIKit/InlineStepDetail.tsx
55
126
  const InlineStepDetail = ({ children }) => /* @__PURE__ */ jsx("div", {
56
127
  className: "tw:ml-2.5 tw:whitespace-pre-wrap tw:border-l tw:border-border tw:pl-3 tw:text-xs tw:text-muted-foreground",
@@ -80,38 +151,122 @@ const SECTION_LABELS = {
80
151
  success: "Answered questions",
81
152
  dismissed: "Dismissed questions"
82
153
  };
83
- const AnswerSummary = ({ items, variant = "success", noAnswerLabel = "No answer" }) => {
154
+ const AnswerSummary = ({ items, variant = "success", noAnswerLabel = "No answer", onExpandToggle }) => {
84
155
  const [isExpanded, setIsExpanded] = useState(false);
85
- const toggle = () => setIsExpanded((prev) => !prev);
156
+ const [isClosing, setIsClosing] = useState(false);
157
+ const toggle = () => {
158
+ const next = !isExpanded;
159
+ if (!next) setIsClosing(hasMotion());
160
+ setIsExpanded(next);
161
+ onExpandToggle?.(next);
162
+ };
86
163
  const count = items.length;
87
164
  const [firstItem] = items;
88
165
  const label = count === 1 && firstItem ? firstItem.question : `${count} questions`;
89
166
  return /* @__PURE__ */ jsxs("section", {
90
- className: "tw:mt-4 tw:flex tw:flex-col tw:gap-4",
167
+ className: "tw:mt-4 tw:flex tw:flex-col",
91
168
  "aria-label": SECTION_LABELS[variant],
92
169
  children: [/* @__PURE__ */ jsx(CollapsibleStepHeader, {
93
170
  isExpanded,
94
171
  onToggle: toggle,
95
172
  variant,
96
173
  label
97
- }), isExpanded && /* @__PURE__ */ jsx("div", {
98
- className: "tw:flex tw:flex-col tw:gap-4 tw:pl-5",
99
- children: items.map(({ question, answer }, index) => /* @__PURE__ */ jsxs("div", {
100
- className: "tw:flex tw:flex-col tw:gap-1",
101
- children: [/* @__PURE__ */ jsx(InlineStepLabel, {
102
- variant,
103
- children: question
104
- }), /* @__PURE__ */ jsx(InlineStepDetail, { children: answer && answer.length > 0 ? answer : noAnswerLabel })]
105
- }, `${index}-${question}`))
174
+ }), /* @__PURE__ */ jsx(CollapsibleBody, {
175
+ isExpanded,
176
+ isClosing,
177
+ onClosed: () => setIsClosing(false),
178
+ children: /* @__PURE__ */ jsx("div", {
179
+ className: "tw:flex tw:flex-col tw:gap-4 tw:pt-4 tw:pl-5",
180
+ children: items.map(({ question, answer }, index) => /* @__PURE__ */ jsxs("div", {
181
+ className: "tw:flex tw:flex-col tw:gap-1",
182
+ children: [/* @__PURE__ */ jsx(InlineStepLabel, {
183
+ variant,
184
+ children: question
185
+ }), /* @__PURE__ */ jsx(InlineStepDetail, { children: answer && answer.length > 0 ? answer : noAnswerLabel })]
186
+ }, `${index}-${question}`))
187
+ })
106
188
  })]
107
189
  });
108
190
  };
109
191
  //#endregion
192
+ //#region src/blocks/AIKit/DiagramBlock.tsx
193
+ /**
194
+ * One diagram block: a skeleton while its chunk loads, the fence source plus a retry if it fails.
195
+ *
196
+ * - `load` runs once per mount and once per press of the retry button. The loader drops its own
197
+ * rejected promise, so every attempt is a fresh fetch.
198
+ * - Recovery is a gesture, not a timer: nothing on the page can tell when a chunk became
199
+ * reachable again, and a tab left open across a deploy points at a URL that is gone for good —
200
+ * a background loop would retry that one forever.
201
+ * - The failure is already logged by the loader, which knows the chunk's name.
202
+ * - A new `load` resets the block: every fence renders this same component type, so a block that
203
+ * switches dialect would otherwise keep drawing through the dialect it had before.
204
+ * - Both the renderer and the loader are functions held in state, so they go in through the
205
+ * setter form — React runs a bare function as a lazy initializer or an updater instead.
206
+ */
207
+ const DiagramBlock = ({ load, code }) => {
208
+ const [Diagram, setDiagram] = useState(null);
209
+ const [failed, setFailed] = useState(false);
210
+ const [attempt, setAttempt] = useState(0);
211
+ const [prevLoad, setPrevLoad] = useState(() => load);
212
+ if (load !== prevLoad) {
213
+ setPrevLoad(() => load);
214
+ setDiagram(null);
215
+ setFailed(false);
216
+ }
217
+ useEffect(() => {
218
+ let cancelled = false;
219
+ load().then((component) => {
220
+ if (!cancelled) setDiagram(() => component);
221
+ }, () => {
222
+ if (!cancelled) setFailed(true);
223
+ });
224
+ return () => {
225
+ cancelled = true;
226
+ };
227
+ }, [load, attempt]);
228
+ if (Diagram) return /* @__PURE__ */ jsx(Diagram, { code });
229
+ if (failed) return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(DiagramSource, { code }), /* @__PURE__ */ jsx(Button, {
230
+ variant: "inline",
231
+ size: "small",
232
+ className: "tw:mb-3",
233
+ onClick: () => {
234
+ setFailed(false);
235
+ setAttempt((count) => count + 1);
236
+ },
237
+ children: "Try again"
238
+ })] });
239
+ return /* @__PURE__ */ jsx(DiagramSkeleton, {});
240
+ };
241
+ //#endregion
242
+ //#region src/blocks/AIKit/diagramChunk.ts
243
+ /**
244
+ * An on-demand diagram chunk — shared between mounts, and retryable.
245
+ *
246
+ * - `lazy()` is deliberately not used here: it memoizes the settled import for the life of the
247
+ * page, so one failed fetch (a dev server restarting, a stale tab after a deploy, a flaky
248
+ * network) leaves every diagram in that tab rendering raw source until a full reload.
249
+ * - A resolved import is kept, so the chunk is fetched once however many blocks mount.
250
+ * - A failed one is cleared, so the next mount imports again. Same contract as
251
+ * `getFlintRuntime()` one level down, inside `FlintChart`.
252
+ * - The failure is logged here, where the chunk's name is known, so the swap to plain source is
253
+ * never silent and a retry that fails again says so.
254
+ */
255
+ const diagramChunk = (name, load) => {
256
+ let pending = null;
257
+ return () => {
258
+ pending ??= load().catch((error) => {
259
+ console.error(`[AIKit] failed to load the ${name} chunk`, error);
260
+ pending = null;
261
+ throw error;
262
+ });
263
+ return pending;
264
+ };
265
+ };
266
+ //#endregion
110
267
  //#region src/blocks/AIKit/Markdown.tsx
111
- const MermaidDiagram = lazy(() => import("@keboola/design/mermaid").then((module) => ({ default: module.MermaidDiagram }), (error) => {
112
- console.error("[AIKit] failed to load the MermaidDiagram chunk", error);
113
- return { default: ({ code }) => /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: code }) }) };
114
- }));
268
+ const loadMermaid = diagramChunk("MermaidDiagram", () => import("@keboola/design/mermaid").then((module) => module.MermaidDiagram));
269
+ const loadFlint = diagramChunk("FlintChart", () => import("@keboola/design/flint").then((module) => module.FlintChart));
115
270
  const proseClass = "tw:prose tw:prose-sm tw:max-w-none tw:break-words tw:[&_pre]:bg-muted tw:[&_pre]:text-foreground tw:[&_:not(pre)>code]:bg-muted tw:[&_:not(pre)>code]:text-foreground tw:[&_:not(pre)>code]:rounded tw:[&_:not(pre)>code]:px-1 tw:[&_:not(pre)>code]:py-0.5 tw:[&_:not(pre)>code]:before:content-none tw:[&_:not(pre)>code]:after:content-none";
116
271
  const getText = (node) => {
117
272
  if (typeof node === "string") return node;
@@ -135,19 +290,48 @@ const getLanguage = (element) => {
135
290
  * - A caller's own plugins are appended to these, never substituted for them.
136
291
  */
137
292
  const baseRemarkPlugins = [remarkGfm];
293
+ /**
294
+ * Fence language → the on-demand chunk that draws it.
295
+ *
296
+ * - Both dialects are the agent's, not this package's: kai-agent's prompt tells the model to
297
+ * write ```mermaid for structure and ```flint for quantitative data, so a chat that renders
298
+ * one and not the other drops output the agent was instructed to produce.
299
+ * - `flint-chart` is an alias — it is the fence tag flint's own docs use, so a model emits it
300
+ * unprompted often enough to matter. `markdownComponents.tsx` in kbc-ui carries the same pair.
301
+ * - Read through `Object.hasOwn`, never by plain index: a fence tagged ```constructor or
302
+ * ```valueOf otherwise answers with an `Object.prototype` member, which is truthy, and calling
303
+ * it as a loader throws — taking the whole message subtree down with it.
304
+ */
305
+ const DIAGRAM_CHUNKS = {
306
+ mermaid: loadMermaid,
307
+ flint: loadFlint,
308
+ "flint-chart": loadFlint
309
+ };
310
+ /**
311
+ * The fence languages this renderer draws instead of printing.
312
+ *
313
+ * - For a caller that streams: a diagram mounts a lazily loaded chunk, so an answer arriving a
314
+ * token at a time has to keep a fence that is still open an ordinary code block, or every
315
+ * keystroke fetches the chunk. Sharing the list is what keeps that hold-back from missing a
316
+ * dialect this map has and the caller does not.
317
+ * - Frozen, because it is that shared contract: a consumer that pushed onto it would change how
318
+ * every other app in the same bundle holds a fence back.
319
+ */
320
+ const DIAGRAM_LANGUAGES = Object.freeze(Object.keys(DIAGRAM_CHUNKS));
138
321
  const baseComponents = {
139
322
  pre({ children, ...props }) {
140
323
  const child = Array.isArray(children) ? children[0] : children;
141
- if (getLanguage(child) === "mermaid") {
142
- const code = getText(child).replace(/\n$/, "");
324
+ const code = getText(child).replace(/\n$/, "");
325
+ const language = getLanguage(child);
326
+ const load = language !== void 0 && Object.hasOwn(DIAGRAM_CHUNKS, language) ? DIAGRAM_CHUNKS[language] : void 0;
327
+ if (load) {
143
328
  if (!code.trim()) return null;
144
- return /* @__PURE__ */ jsx(Suspense, {
145
- fallback: /* @__PURE__ */ jsx(DiagramSkeleton, {}),
146
- children: /* @__PURE__ */ jsx(MermaidDiagram, { code })
329
+ return /* @__PURE__ */ jsx(DiagramBlock, {
330
+ load,
331
+ code
147
332
  });
148
333
  }
149
334
  const { node: _node, ...rest } = props;
150
- const code = getText(child).replace(/\n$/, "");
151
335
  return /* @__PURE__ */ jsxs("div", {
152
336
  className: "tw:group tw:relative",
153
337
  children: [/* @__PURE__ */ jsx("span", {
@@ -229,6 +413,95 @@ const Markdown = ({ children, className, remarkPlugins, components: componentOve
229
413
  });
230
414
  };
231
415
  //#endregion
416
+ //#region src/blocks/AIKit/MarkdownTable.tsx
417
+ /** How much wider than its column a table has to be before the column is the wrong surface. */
418
+ const EXPAND_RATIO = 1.5;
419
+ /**
420
+ * The token the host paints the table on, which is the colour the fade has to end in.
421
+ *
422
+ * - A gradient hides the cut-off cells by ending in the surface behind them. End it in any other
423
+ * token and it draws a contrasting strip over the data instead of hiding it.
424
+ * - Full class strings, not a composed `tw:from-${token}`: Tailwind reads literals.
425
+ */
426
+ const FADE_SURFACE = {
427
+ card: "tw:from-card",
428
+ background: "tw:from-background"
429
+ };
430
+ /**
431
+ * A table in an answer, with the fact that it is cut off on the right said out loud.
432
+ *
433
+ * - `overflow-x: auto` scrolls silently, so nothing on screen says there is more to the right.
434
+ * - The controls sit in a rail above the grid, never over the header row or the last row — the
435
+ * data a reader reaches for them to get at. The rail holds its height, so revealing hover
436
+ * controls or crossing the ratio cannot shift the table under the pointer.
437
+ * - The fade marks the cut edge only while content remains, so it never reads as a border.
438
+ * - A painted gradient rather than the `tw:scroll-fade-x` mask, which needs scroll-driven
439
+ * animations: the build floor is `baseline-widely-available` (Safari 16, Firefox 104), and there
440
+ * the utility either fades both edges of a table that fits or, behind the `supports-` guard
441
+ * `PageLayout` uses, says nothing at all — which is the state this component exists to end. The
442
+ * mask also has to sit on the scrolling element, and that is `Table`'s own container, not ours.
443
+ * - Past `EXPAND_RATIO` the fade is joined by `onExpand`'s action, drawn without waiting for a
444
+ * hover: it reports that content is cut off, which the reader needs before going looking.
445
+ */
446
+ const MarkdownTable = ({ children, actions, onExpand, surface = "card", className }) => {
447
+ const frameRef = useRef(null);
448
+ const [overflow, setOverflow] = useState({
449
+ scrollable: false,
450
+ atEnd: true,
451
+ ratio: 1
452
+ });
453
+ const measure = useCallback(() => {
454
+ const box = frameRef.current?.firstElementChild;
455
+ if (!box) return;
456
+ const { scrollLeft, scrollWidth, clientWidth } = box;
457
+ const hidden = scrollWidth - clientWidth;
458
+ setOverflow({
459
+ scrollable: hidden > 1,
460
+ atEnd: scrollLeft >= hidden - 1,
461
+ ratio: clientWidth > 0 ? scrollWidth / clientWidth : 1
462
+ });
463
+ }, []);
464
+ useEffect(() => {
465
+ const box = frameRef.current?.firstElementChild;
466
+ if (!box) return;
467
+ measure();
468
+ box.addEventListener("scroll", measure, { passive: true });
469
+ const observer = new ResizeObserver(measure);
470
+ observer.observe(box);
471
+ if (box.firstElementChild) observer.observe(box.firstElementChild);
472
+ return () => {
473
+ box.removeEventListener("scroll", measure);
474
+ observer.disconnect();
475
+ };
476
+ }, [measure]);
477
+ const showExpand = !!onExpand && overflow.scrollable && overflow.ratio >= EXPAND_RATIO;
478
+ return /* @__PURE__ */ jsxs("div", {
479
+ className,
480
+ children: [(actions || onExpand) && /* @__PURE__ */ jsxs("div", {
481
+ className: "tw:flex tw:h-8 tw:items-center tw:justify-end tw:gap-1",
482
+ children: [actions, showExpand && /* @__PURE__ */ jsx(Tooltip, {
483
+ tooltip: "This table is wider than the space it has",
484
+ placement: "top",
485
+ triggerAsChild: true,
486
+ children: /* @__PURE__ */ jsxs(Button, {
487
+ size: "small",
488
+ variant: "invisible",
489
+ onClick: onExpand,
490
+ children: [/* @__PURE__ */ jsx(Maximize2, { className: "tw:size-3.5" }), "Open wider"]
491
+ })
492
+ })]
493
+ }), /* @__PURE__ */ jsxs("div", {
494
+ ref: frameRef,
495
+ className: "tw:relative",
496
+ children: [children, overflow.scrollable && !overflow.atEnd && /* @__PURE__ */ jsx("span", {
497
+ "aria-hidden": true,
498
+ "data-testid": "markdown-table-fade",
499
+ className: cn("tw:pointer-events-none tw:absolute tw:inset-y-px tw:right-px tw:w-12 tw:rounded-r-[10px] tw:bg-gradient-to-l tw:to-transparent", FADE_SURFACE[surface])
500
+ })]
501
+ })]
502
+ });
503
+ };
504
+ //#endregion
232
505
  //#region src/blocks/AIKit/MessageBubble.tsx
233
506
  const containerClasses = cva([
234
507
  "tw:flex",
@@ -335,7 +608,7 @@ const ExpandToggle = ({ isExpanded, onToggle, showMoreLabel, showLessLabel }) =>
335
608
  });
336
609
  const ChangeRequestForm = ({ value, onValueChange, onSubmit, onKeyDown, placeholder, submitLabel, formLabel, canSubmit, isSubmitting }) => /* @__PURE__ */ jsx("form", {
337
610
  onSubmit,
338
- className: "tw:border-0 tw:border-t tw:border-solid tw:border-neutral-100 tw:bg-white tw:px-4 tw:py-3",
611
+ className: "tw:border-0 tw:border-t tw:border-solid tw:border-border tw:bg-card tw:text-card-foreground tw:px-4 tw:py-3",
339
612
  "aria-label": formLabel,
340
613
  children: /* @__PURE__ */ jsxs("div", {
341
614
  className: "tw:relative",
@@ -415,8 +688,10 @@ const PlanApproval = ({ status, title, summary, stepCount, planBody, defaultExpa
415
688
  const canShowPlanBody = planBody != null;
416
689
  const trimmedChangeRequest = changeRequest.trim();
417
690
  const canSubmitChangeRequest = trimmedChangeRequest.length > 0 && !isSubmitting;
691
+ const [isClosing, setIsClosing] = useState(false);
418
692
  const toggleExpanded = () => {
419
693
  const next = !isExpanded;
694
+ if (!next) setIsClosing(hasMotion());
420
695
  if (isExpandedProp === void 0) setUncontrolledExpanded(next);
421
696
  onExpandToggle?.(next);
422
697
  };
@@ -465,7 +740,7 @@ const PlanApproval = ({ status, title, summary, stepCount, planBody, defaultExpa
465
740
  });
466
741
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("section", {
467
742
  "aria-label": title,
468
- className: cn("tw:flex tw:w-full tw:max-w-[800px] tw:flex-col tw:overflow-hidden tw:rounded-xl tw:border tw:border-solid tw:border-border tw:bg-neutral-50", className),
743
+ className: cn("tw:flex tw:w-full tw:max-w-[800px] tw:flex-col tw:overflow-hidden tw:rounded-xl tw:border tw:border-solid tw:border-border tw:bg-background tw:text-foreground", className),
469
744
  "data-testid": "plan-approval-root",
470
745
  "data-plan-status": status,
471
746
  children: [
@@ -482,11 +757,11 @@ const PlanApproval = ({ status, title, summary, stepCount, planBody, defaultExpa
482
757
  dismissedLabel: dismissedPillLabel,
483
758
  changesRequestedLabel: changesRequestedPillLabel
484
759
  }), stepCount != null && stepCount > 0 && /* @__PURE__ */ jsx("span", {
485
- className: "tw:text-xs tw:leading-4 tw:text-neutral-500",
760
+ className: "tw:text-xs tw:leading-4 tw:text-muted-foreground",
486
761
  children: stepCountLabel(stepCount)
487
762
  })]
488
763
  }), /* @__PURE__ */ jsx("p", {
489
- className: "tw:m-0 tw:text-base tw:font-semibold tw:leading-6 tw:text-neutral-800",
764
+ className: "tw:m-0 tw:text-base tw:font-semibold tw:leading-6",
490
765
  children: title
491
766
  })]
492
767
  }), /* @__PURE__ */ jsxs("div", {
@@ -515,15 +790,20 @@ const PlanApproval = ({ status, title, summary, stepCount, planBody, defaultExpa
515
790
  })]
516
791
  }),
517
792
  summary != null && /* @__PURE__ */ jsx("div", {
518
- className: cn("tw:whitespace-pre-wrap tw:border-0 tw:border-t tw:border-solid tw:border-neutral-100 tw:px-4 tw:pt-4 tw:text-sm tw:text-neutral-700", isExpanded && canShowPlanBody ? "tw:pb-4" : "tw:pb-2"),
793
+ className: cn("tw:whitespace-pre-wrap tw:border-0 tw:border-t tw:border-solid tw:border-border tw:px-4 tw:pt-4 tw:text-sm", isExpanded && canShowPlanBody ? "tw:pb-4" : "tw:pb-2"),
519
794
  children: summary
520
795
  }),
521
- isExpanded && canShowPlanBody && /* @__PURE__ */ jsx("div", {
522
- className: "tw:max-h-[400px] tw:overflow-y-auto tw:border-0 tw:border-t tw:border-solid tw:border-neutral-100 tw:bg-white tw:p-4 tw:text-sm tw:text-neutral-700",
523
- children: planBody
796
+ canShowPlanBody && /* @__PURE__ */ jsx(CollapsibleBody, {
797
+ isExpanded,
798
+ isClosing,
799
+ onClosed: () => setIsClosing(false),
800
+ children: /* @__PURE__ */ jsx("div", {
801
+ className: "tw:max-h-[400px] tw:overflow-y-auto tw:border-0 tw:border-t tw:border-solid tw:border-border tw:bg-card tw:p-4 tw:text-sm tw:text-card-foreground",
802
+ children: planBody
803
+ })
524
804
  }),
525
805
  !isFullScreen && /* @__PURE__ */ jsxs(Fragment$1, { children: [showChangeRequestForm && changeRequestForm, /* @__PURE__ */ jsxs("div", {
526
- className: cn("tw:flex tw:items-center tw:gap-2 tw:overflow-x-auto tw:border-0 tw:border-t tw:border-solid tw:border-neutral-100 tw:bg-white tw:px-4 tw:py-3", canShowPlanBody ? "tw:justify-between" : "tw:justify-end"),
806
+ className: cn("tw:flex tw:items-center tw:gap-2 tw:overflow-x-auto tw:border-0 tw:border-t tw:border-solid tw:border-border tw:bg-card tw:px-4 tw:py-3 tw:text-card-foreground", canShowPlanBody ? "tw:justify-between" : "tw:justify-end"),
527
807
  children: [canShowPlanBody && /* @__PURE__ */ jsx("div", {
528
808
  className: "tw:shrink-0",
529
809
  children: /* @__PURE__ */ jsx(ExpandToggle, {
@@ -585,14 +865,14 @@ const PlanApproval = ({ status, title, summary, stepCount, planBody, defaultExpa
585
865
  })
586
866
  }),
587
867
  /* @__PURE__ */ jsx(FullScreenModalBody, {
588
- className: "tw:bg-white tw:px-6 tw:py-4",
868
+ className: "tw:bg-card tw:px-6 tw:py-4 tw:text-card-foreground",
589
869
  children: /* @__PURE__ */ jsx("div", {
590
- className: "tw:mx-auto tw:w-full tw:max-w-[800px] tw:text-sm tw:text-neutral-700",
870
+ className: "tw:mx-auto tw:w-full tw:max-w-[800px] tw:text-sm",
591
871
  children: planBody
592
872
  })
593
873
  }),
594
874
  (showChangeRequestForm || showActions) && /* @__PURE__ */ jsx("div", {
595
- className: "tw:shrink-0 tw:border-0 tw:border-t tw:border-solid tw:border-neutral-100 tw:bg-white",
875
+ className: "tw:shrink-0 tw:border-0 tw:border-t tw:border-solid tw:border-border tw:bg-card tw:text-card-foreground",
596
876
  children: /* @__PURE__ */ jsxs("div", {
597
877
  className: "tw:mx-auto tw:w-full tw:max-w-[800px]",
598
878
  children: [showChangeRequestForm && changeRequestForm, showActions && /* @__PURE__ */ jsx("div", {
@@ -758,11 +1038,15 @@ const wrapperVariants = cva(null, {
758
1038
  inline: ""
759
1039
  } }
760
1040
  });
761
- const PromptInput = ({ onSend, onAbort, onContinue, prefix, variant, suffix, disabled, disableSubmit, value = "", onValueChange, status = "ready", maxRows = 6, minRows, commands, autoFocus, ...textareaProps }) => {
1041
+ const PromptInput = ({ onSend, onAbort, onContinue, prefix, variant, suffix, disabled, disableSubmit, value = "", onValueChange, status = "ready", maxRows = 6, minRows, commands, autoFocus, textareaRef: publishedRef, ...textareaProps }) => {
762
1042
  const defaultMinRows = variant === "inline" ? 1 : 2;
763
1043
  const isDisabled = (disabled || disableSubmit || !value.trim()) && status !== "streaming" && status !== "interrupted";
764
1044
  const textareaId = useId();
765
1045
  const textareaRef = useRef(null);
1046
+ const setTextareaNode = useCallback((node) => {
1047
+ textareaRef.current = node;
1048
+ if (publishedRef) publishedRef.current = node;
1049
+ }, [publishedRef]);
766
1050
  const [dismissedValue, setDismissedValue] = useState(null);
767
1051
  const autoFocusRef = useRef(autoFocus);
768
1052
  useEffect(() => {
@@ -833,7 +1117,7 @@ const PromptInput = ({ onSend, onAbort, onContinue, prefix, variant, suffix, dis
833
1117
  minRows: minRows ?? defaultMinRows,
834
1118
  maxRows,
835
1119
  id: textareaId,
836
- ref: textareaRef,
1120
+ ref: setTextareaNode,
837
1121
  value,
838
1122
  onChange: handleOnChange,
839
1123
  onKeyDown: handleOnKeyDown,
@@ -1123,11 +1407,24 @@ const SuggestedPrompt = ({ label, disabled, onClick, icon, className }) => {
1123
1407
  };
1124
1408
  //#endregion
1125
1409
  //#region src/blocks/AIKit/ThinkingIndicator.tsx
1410
+ /**
1411
+ * The agent's turn is in flight and has nothing to show yet.
1412
+ *
1413
+ * - Quiet chrome, matching the step rows it precedes (muted ink, no italic), so the hand-off
1414
+ * from "Thinking..." to the first step is seamless.
1415
+ * - `role="status"` because a spinner is silence to a screen reader, and for much of a turn
1416
+ * this is all a transcript shows. The label is the announcement, so it is never decorative.
1417
+ * - The icon is hidden from the tree: it says the same thing as the label beside it.
1418
+ */
1126
1419
  const ThinkingIndicator = ({ label = "Thinking..." }) => {
1127
1420
  return /* @__PURE__ */ jsxs("div", {
1128
1421
  className: "tw:flex tw:items-center tw:gap-1.5",
1422
+ role: "status",
1129
1423
  "data-testid": "chat-thinking-indicator",
1130
- children: [/* @__PURE__ */ jsx(Loader2, { className: "tw:size-3.5 tw:animate-spin tw:text-muted-foreground" }), /* @__PURE__ */ jsx("span", {
1424
+ children: [/* @__PURE__ */ jsx(Loader2, {
1425
+ "aria-hidden": true,
1426
+ className: "tw:size-3.5 tw:animate-spin tw:text-muted-foreground"
1427
+ }), /* @__PURE__ */ jsx("span", {
1131
1428
  className: "tw:text-xs tw:font-medium tw:text-muted-foreground",
1132
1429
  children: label
1133
1430
  })]
@@ -1331,14 +1628,33 @@ const ToolCallStatusHeader = ({ icon: IconComponent, iconSpin, status, text, nee
1331
1628
  className: "tw:text-xs tw:font-medium tw:text-warning",
1332
1629
  children: "Confirmation required"
1333
1630
  }), /* @__PURE__ */ jsx(TriangleAlert, { className: "tw:size-3.5 tw:text-warning" })]
1334
- }), isCollapsible && (isCollapsed ? /* @__PURE__ */ jsx(ChevronDown, { className: "tw:size-3.5 tw:shrink-0 tw:text-muted-foreground" }) : /* @__PURE__ */ jsx(ChevronUp, { className: "tw:size-3.5 tw:shrink-0 tw:text-muted-foreground" }))]
1631
+ }), isCollapsible && /* @__PURE__ */ jsx(ChevronDown, { className: cn("tw:size-3.5 tw:shrink-0 tw:text-muted-foreground", "tw:transition-transform tw:duration-300 tw:ease-out tw:motion-reduce:transition-none", !isCollapsed && "tw:rotate-180") })]
1335
1632
  })]
1336
1633
  });
1337
1634
  };
1338
1635
  //#endregion
1339
1636
  //#region src/blocks/AIKit/usageStatsHelpers.ts
1340
- /** Clamp a percentage to 0–100 and normalize non-finite input to 0. */
1637
+ /**
1638
+ * Clamp a percentage to 0–100 and normalize non-finite input to 0.
1639
+ *
1640
+ * - `Progress`'s indicator is unclamped, so a value over 100 slides the bar off-screen.
1641
+ * - `NaN` normalizes to 0 — `Math.min`/`Math.max` propagate it, and it renders as no bar at all.
1642
+ */
1341
1643
  const clampPercentage = (value) => Number.isFinite(value) ? Math.min(100, Math.max(0, value)) : 0;
1644
+ /**
1645
+ * A percentage as text — `72%`, `0.2%`, `99%`.
1646
+ *
1647
+ * - Whole numbers from 1% up; a decimal only below 1%, where rounding would read as `0%`.
1648
+ * - A non-zero share never prints as `0.0%` — it floors at `0.1%`.
1649
+ * - Never `100%` for a value under 100: the text would disagree with the at-limit state computed
1650
+ * from the raw value, exactly at the boundary everything else turns on.
1651
+ */
1652
+ const formatPercentage = (value) => {
1653
+ const oneDecimal = Math.round(value * 10) / 10;
1654
+ if (value > 0 && oneDecimal < 1) return `${Math.max(.1, oneDecimal).toFixed(1)}%`;
1655
+ const rounded = Math.round(value);
1656
+ return `${value < 100 ? Math.min(rounded, 99) : rounded}%`;
1657
+ };
1342
1658
  //#endregion
1343
1659
  //#region src/blocks/AIKit/UsageStatsRing.tsx
1344
1660
  const UsageStatsRing = ({ value, size = 16, strokeWidth = 2.5, className }) => {
@@ -1357,7 +1673,7 @@ const UsageStatsRing = ({ value, size = 16, strokeWidth = 2.5, className }) => {
1357
1673
  r: radius,
1358
1674
  fill: "none",
1359
1675
  strokeWidth,
1360
- className: "tw:stroke-neutral-400 tw:opacity-50"
1676
+ className: "tw:stroke-muted-foreground tw:opacity-50"
1361
1677
  }), /* @__PURE__ */ jsx("circle", {
1362
1678
  cx: size / 2,
1363
1679
  cy: size / 2,
@@ -1373,28 +1689,32 @@ const UsageStatsRing = ({ value, size = 16, strokeWidth = 2.5, className }) => {
1373
1689
  };
1374
1690
  //#endregion
1375
1691
  //#region src/blocks/AIKit/UsageStats.tsx
1376
- const UsageStats = ({ value, alert = false, children, className, ariaLabel, align = "end", side = "top", sideOffset = 8 }) => {
1692
+ const UsageStats = ({ value, alert = false, children, className, ariaLabel, align = "end", side = "top", sideOffset = 8, open, onOpenChange }) => {
1377
1693
  const hasAlert = Boolean(alert);
1378
1694
  const isWarning = alert === "warning";
1379
1695
  const defaultLabel = `Usage: ${Math.round(clampPercentage(value))}%${hasAlert ? " (usage alert)" : ""}`;
1380
- return /* @__PURE__ */ jsxs(HoverCard, { children: [/* @__PURE__ */ jsx(HoverCard.Trigger, {
1381
- asChild: true,
1382
- children: /* @__PURE__ */ jsxs("span", {
1383
- "aria-label": ariaLabel ?? defaultLabel,
1384
- className: cn("tw:relative tw:inline-flex tw:size-8 tw:items-center tw:justify-center", className),
1385
- children: [/* @__PURE__ */ jsx(UsageStatsRing, { value }), hasAlert && /* @__PURE__ */ jsx(AlertCircle, {
1386
- "data-testid": "usage-stats-alert",
1387
- className: cn("tw:absolute tw:-right-0.5 tw:-top-0.5 tw:size-3", isWarning ? "tw:text-warning-500" : "tw:text-error-500")
1388
- })]
1389
- })
1390
- }), /* @__PURE__ */ jsx(HoverCard.Content, {
1391
- align,
1392
- side,
1393
- sideOffset,
1394
- children
1395
- })] });
1696
+ return /* @__PURE__ */ jsxs(HoverCard, {
1697
+ open,
1698
+ onOpenChange,
1699
+ children: [/* @__PURE__ */ jsx(HoverCard.Trigger, {
1700
+ asChild: true,
1701
+ children: /* @__PURE__ */ jsxs("span", {
1702
+ "aria-label": ariaLabel ?? defaultLabel,
1703
+ className: cn("tw:relative tw:inline-flex tw:size-8 tw:items-center tw:justify-center", className),
1704
+ children: [/* @__PURE__ */ jsx(UsageStatsRing, { value }), hasAlert && /* @__PURE__ */ jsx(AlertCircle, {
1705
+ "data-testid": "usage-stats-alert",
1706
+ className: cn("tw:absolute tw:-right-0.5 tw:-top-0.5 tw:size-3", isWarning ? "tw:text-warning-500" : "tw:text-error-500")
1707
+ })]
1708
+ })
1709
+ }), /* @__PURE__ */ jsx(HoverCard.Content, {
1710
+ align,
1711
+ side,
1712
+ sideOffset,
1713
+ children
1714
+ })]
1715
+ });
1396
1716
  };
1397
1717
  //#endregion
1398
- export { AnswerSummary, CollapsibleStepHeader, InlineStepDetail, InlineStepLabel, Markdown, MessageBubble, PlanApproval, PlanRewritingIndicator, PromptInput, Question, QuestionBlock, SuggestedPrompt, ThinkingIndicator, ToolCallApproval, ToolCallChip, ToolCallStatusHeader, UsageStats, UsageStatsRing };
1718
+ export { AnswerActions, AnswerSummary, CollapsibleStepHeader, DIAGRAM_LANGUAGES, InlineStepDetail, InlineStepLabel, Markdown, MarkdownTable, MessageBubble, PlanApproval, PlanRewritingIndicator, PromptInput, Question, QuestionBlock, SuggestedPrompt, ThinkingIndicator, ToolCallApproval, ToolCallChip, ToolCallStatusHeader, UsageStats, UsageStatsRing, clampPercentage, formatPercentage };
1399
1719
 
1400
- //# sourceMappingURL=AIKit-DDg5DBCG.js.map
1720
+ //# sourceMappingURL=AIKit-BExvt9pt.js.map