@flanksource/clicky-ui 0.3.5 → 0.3.7

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 (277) hide show
  1. package/README.md +42 -0
  2. package/dist/ai.cjs +15 -0
  3. package/dist/ai.cjs.map +1 -1
  4. package/dist/ai.js +15 -0
  5. package/dist/ai.js.map +1 -1
  6. package/dist/components/DateField.d.ts +1 -1
  7. package/dist/components/DatePicker.d.ts +1 -1
  8. package/dist/components/DateTimePicker.d.ts +1 -1
  9. package/dist/components/FormLookupProvider.cjs +12 -0
  10. package/dist/components/FormLookupProvider.cjs.map +1 -0
  11. package/dist/components/FormLookupProvider.d.ts +7 -0
  12. package/dist/components/FormLookupProvider.d.ts.map +1 -0
  13. package/dist/components/FormLookupProvider.js +12 -0
  14. package/dist/components/FormLookupProvider.js.map +1 -0
  15. package/dist/components/JsonSchemaForm.cjs +2 -2
  16. package/dist/components/JsonSchemaForm.cjs.map +1 -1
  17. package/dist/components/JsonSchemaForm.js +1 -1
  18. package/dist/components/JsonSchemaForm.js.map +1 -1
  19. package/dist/components/ListMenu.cjs +145 -0
  20. package/dist/components/ListMenu.cjs.map +1 -0
  21. package/dist/components/ListMenu.d.ts +54 -0
  22. package/dist/components/ListMenu.d.ts.map +1 -0
  23. package/dist/components/ListMenu.js +145 -0
  24. package/dist/components/ListMenu.js.map +1 -0
  25. package/dist/components/MdxEditorField.cjs +128 -0
  26. package/dist/components/MdxEditorField.cjs.map +1 -0
  27. package/dist/components/MdxEditorField.d.ts +4 -0
  28. package/dist/components/MdxEditorField.d.ts.map +1 -0
  29. package/dist/components/MdxEditorField.js +106 -0
  30. package/dist/components/MdxEditorField.js.map +1 -0
  31. package/dist/components/MdxEditorToolbar.cjs +76 -0
  32. package/dist/components/MdxEditorToolbar.cjs.map +1 -0
  33. package/dist/components/MdxEditorToolbar.d.ts +8 -0
  34. package/dist/components/MdxEditorToolbar.d.ts.map +1 -0
  35. package/dist/components/MdxEditorToolbar.js +76 -0
  36. package/dist/components/MdxEditorToolbar.js.map +1 -0
  37. package/dist/components/button-variants.d.ts +1 -1
  38. package/dist/components/button.d.ts +1 -1
  39. package/dist/components/form-lookup-context.cjs +1 -8
  40. package/dist/components/form-lookup-context.cjs.map +1 -1
  41. package/dist/components/form-lookup-context.d.ts +1 -5
  42. package/dist/components/form-lookup-context.d.ts.map +1 -1
  43. package/dist/components/form-lookup-context.js +1 -8
  44. package/dist/components/form-lookup-context.js.map +1 -1
  45. package/dist/components/jotai-bindings-core.cjs +197 -0
  46. package/dist/components/jotai-bindings-core.cjs.map +1 -0
  47. package/dist/components/jotai-bindings-core.d.ts +45 -0
  48. package/dist/components/jotai-bindings-core.d.ts.map +1 -0
  49. package/dist/components/jotai-bindings-core.js +197 -0
  50. package/dist/components/jotai-bindings-core.js.map +1 -0
  51. package/dist/components/jotai-bindings.cjs +17 -0
  52. package/dist/components/jotai-bindings.cjs.map +1 -0
  53. package/dist/components/jotai-bindings.d.ts +4 -0
  54. package/dist/components/jotai-bindings.d.ts.map +1 -0
  55. package/dist/components/jotai-bindings.js +17 -0
  56. package/dist/components/jotai-bindings.js.map +1 -0
  57. package/dist/components/json-schema-form-fields.cjs +37 -0
  58. package/dist/components/json-schema-form-fields.cjs.map +1 -1
  59. package/dist/components/json-schema-form-fields.d.ts +6 -0
  60. package/dist/components/json-schema-form-fields.d.ts.map +1 -1
  61. package/dist/components/json-schema-form-fields.js +38 -1
  62. package/dist/components/json-schema-form-fields.js.map +1 -1
  63. package/dist/components/json-schema-form-render.cjs +3 -1
  64. package/dist/components/json-schema-form-render.cjs.map +1 -1
  65. package/dist/components/json-schema-form-render.d.ts.map +1 -1
  66. package/dist/components/json-schema-form-render.js +4 -2
  67. package/dist/components/json-schema-form-render.js.map +1 -1
  68. package/dist/components/json-schema-form-resolve.cjs +9 -0
  69. package/dist/components/json-schema-form-resolve.cjs.map +1 -1
  70. package/dist/components/json-schema-form-resolve.d.ts.map +1 -1
  71. package/dist/components/json-schema-form-resolve.js +9 -0
  72. package/dist/components/json-schema-form-resolve.js.map +1 -1
  73. package/dist/components/json-schema-form-types.d.ts +4 -1
  74. package/dist/components/json-schema-form-types.d.ts.map +1 -1
  75. package/dist/components/mdx-editor-field-plugins.cjs +124 -0
  76. package/dist/components/mdx-editor-field-plugins.cjs.map +1 -0
  77. package/dist/components/mdx-editor-field-plugins.d.ts +6 -0
  78. package/dist/components/mdx-editor-field-plugins.d.ts.map +1 -0
  79. package/dist/components/mdx-editor-field-plugins.js +124 -0
  80. package/dist/components/mdx-editor-field-plugins.js.map +1 -0
  81. package/dist/components/mdx-editor-options.d.ts +77 -0
  82. package/dist/components/mdx-editor-options.d.ts.map +1 -0
  83. package/dist/components/use-list-menu-selection.cjs +77 -0
  84. package/dist/components/use-list-menu-selection.cjs.map +1 -0
  85. package/dist/components/use-list-menu-selection.d.ts +40 -0
  86. package/dist/components/use-list-menu-selection.d.ts.map +1 -0
  87. package/dist/components/use-list-menu-selection.js +77 -0
  88. package/dist/components/use-list-menu-selection.js.map +1 -0
  89. package/dist/components.cjs +10 -1
  90. package/dist/components.cjs.map +1 -1
  91. package/dist/components.d.ts +4 -1
  92. package/dist/components.d.ts.map +1 -1
  93. package/dist/components.js +10 -1
  94. package/dist/components.js.map +1 -1
  95. package/dist/data/MarkdownEditor.model.d.ts +1 -1
  96. package/dist/data/ProgressBars.cjs +194 -0
  97. package/dist/data/ProgressBars.cjs.map +1 -0
  98. package/dist/data/ProgressBars.d.ts +85 -0
  99. package/dist/data/ProgressBars.d.ts.map +1 -0
  100. package/dist/data/ProgressBars.js +194 -0
  101. package/dist/data/ProgressBars.js.map +1 -0
  102. package/dist/data/ProgressBars.model.cjs +27 -0
  103. package/dist/data/ProgressBars.model.cjs.map +1 -0
  104. package/dist/data/ProgressBars.model.d.ts +20 -0
  105. package/dist/data/ProgressBars.model.d.ts.map +1 -0
  106. package/dist/data/ProgressBars.model.js +27 -0
  107. package/dist/data/ProgressBars.model.js.map +1 -0
  108. package/dist/data/PromptBanner.cjs +199 -0
  109. package/dist/data/PromptBanner.cjs.map +1 -0
  110. package/dist/data/PromptBanner.d.ts +17 -0
  111. package/dist/data/PromptBanner.d.ts.map +1 -0
  112. package/dist/data/PromptBanner.js +199 -0
  113. package/dist/data/PromptBanner.js.map +1 -0
  114. package/dist/data/PromptBanner.model.cjs +132 -0
  115. package/dist/data/PromptBanner.model.cjs.map +1 -0
  116. package/dist/data/PromptBanner.model.d.ts +23 -0
  117. package/dist/data/PromptBanner.model.d.ts.map +1 -0
  118. package/dist/data/PromptBanner.model.js +132 -0
  119. package/dist/data/PromptBanner.model.js.map +1 -0
  120. package/dist/data/PromptDialog.cjs +43 -0
  121. package/dist/data/PromptDialog.cjs.map +1 -0
  122. package/dist/data/PromptDialog.d.ts +14 -0
  123. package/dist/data/PromptDialog.d.ts.map +1 -0
  124. package/dist/data/PromptDialog.js +43 -0
  125. package/dist/data/PromptDialog.js.map +1 -0
  126. package/dist/data/TaskManager.cjs +8 -1
  127. package/dist/data/TaskManager.cjs.map +1 -1
  128. package/dist/data/TaskManager.d.ts.map +1 -1
  129. package/dist/data/TaskManager.js +8 -1
  130. package/dist/data/TaskManager.js.map +1 -1
  131. package/dist/data/TaskProgress.cjs +15 -2
  132. package/dist/data/TaskProgress.cjs.map +1 -1
  133. package/dist/data/TaskProgress.d.ts +0 -10
  134. package/dist/data/TaskProgress.d.ts.map +1 -1
  135. package/dist/data/TaskProgress.js +15 -2
  136. package/dist/data/TaskProgress.js.map +1 -1
  137. package/dist/data/TimeseriesCoreBars.cjs +19 -173
  138. package/dist/data/TimeseriesCoreBars.cjs.map +1 -1
  139. package/dist/data/TimeseriesCoreBars.d.ts +9 -26
  140. package/dist/data/TimeseriesCoreBars.d.ts.map +1 -1
  141. package/dist/data/TimeseriesCoreBars.js +20 -174
  142. package/dist/data/TimeseriesCoreBars.js.map +1 -1
  143. package/dist/data/WorkloadCard.model.cjs.map +1 -1
  144. package/dist/data/WorkloadCard.model.d.ts +2 -2
  145. package/dist/data/WorkloadCard.model.d.ts.map +1 -1
  146. package/dist/data/WorkloadCard.model.js.map +1 -1
  147. package/dist/data/ai/ChatWindow.cjs +53 -9
  148. package/dist/data/ai/ChatWindow.cjs.map +1 -1
  149. package/dist/data/ai/ChatWindow.d.ts.map +1 -1
  150. package/dist/data/ai/ChatWindow.js +53 -9
  151. package/dist/data/ai/ChatWindow.js.map +1 -1
  152. package/dist/data/ai/SessionViewer.cjs +275 -0
  153. package/dist/data/ai/SessionViewer.cjs.map +1 -0
  154. package/dist/data/ai/SessionViewer.d.ts +33 -0
  155. package/dist/data/ai/SessionViewer.d.ts.map +1 -0
  156. package/dist/data/ai/SessionViewer.fixtures.d.ts +4 -0
  157. package/dist/data/ai/SessionViewer.fixtures.d.ts.map +1 -0
  158. package/dist/data/ai/SessionViewer.js +275 -0
  159. package/dist/data/ai/SessionViewer.js.map +1 -0
  160. package/dist/data/ai/SessionViewer.model.cjs +275 -0
  161. package/dist/data/ai/SessionViewer.model.cjs.map +1 -0
  162. package/dist/data/ai/SessionViewer.model.d.ts +87 -0
  163. package/dist/data/ai/SessionViewer.model.d.ts.map +1 -0
  164. package/dist/data/ai/SessionViewer.model.js +275 -0
  165. package/dist/data/ai/SessionViewer.model.js.map +1 -0
  166. package/dist/data/ai/SessionViewerMenu.cjs +190 -0
  167. package/dist/data/ai/SessionViewerMenu.cjs.map +1 -0
  168. package/dist/data/ai/SessionViewerMenu.d.ts +24 -0
  169. package/dist/data/ai/SessionViewerMenu.d.ts.map +1 -0
  170. package/dist/data/ai/SessionViewerMenu.js +190 -0
  171. package/dist/data/ai/SessionViewerMenu.js.map +1 -0
  172. package/dist/data/ai/ToolPreferences.cjs +232 -39
  173. package/dist/data/ai/ToolPreferences.cjs.map +1 -1
  174. package/dist/data/ai/ToolPreferences.d.ts +4 -7
  175. package/dist/data/ai/ToolPreferences.d.ts.map +1 -1
  176. package/dist/data/ai/ToolPreferences.js +234 -41
  177. package/dist/data/ai/ToolPreferences.js.map +1 -1
  178. package/dist/data/ai/index.d.ts +3 -0
  179. package/dist/data/ai/index.d.ts.map +1 -1
  180. package/dist/data/ai/session-categories.cjs +219 -0
  181. package/dist/data/ai/session-categories.cjs.map +1 -0
  182. package/dist/data/ai/session-categories.d.ts +36 -0
  183. package/dist/data/ai/session-categories.d.ts.map +1 -0
  184. package/dist/data/ai/session-categories.js +219 -0
  185. package/dist/data/ai/session-categories.js.map +1 -0
  186. package/dist/data/chat/clickyOperationsToTools.cjs +89 -3
  187. package/dist/data/chat/clickyOperationsToTools.cjs.map +1 -1
  188. package/dist/data/chat/clickyOperationsToTools.d.ts +1 -1
  189. package/dist/data/chat/clickyOperationsToTools.d.ts.map +1 -1
  190. package/dist/data/chat/clickyOperationsToTools.js +89 -3
  191. package/dist/data/chat/clickyOperationsToTools.js.map +1 -1
  192. package/dist/data/chat/types.cjs.map +1 -1
  193. package/dist/data/chat/types.d.ts +7 -1
  194. package/dist/data/chat/types.d.ts.map +1 -1
  195. package/dist/data/chat/types.js.map +1 -1
  196. package/dist/data/task-status.cjs +19 -12
  197. package/dist/data/task-status.cjs.map +1 -1
  198. package/dist/data/task-status.d.ts +2 -1
  199. package/dist/data/task-status.d.ts.map +1 -1
  200. package/dist/data/task-status.js +19 -12
  201. package/dist/data/task-status.js.map +1 -1
  202. package/dist/data/test-runner/TestRunner.fixtures.d.ts +11 -4
  203. package/dist/data/test-runner/TestRunner.fixtures.d.ts.map +1 -1
  204. package/dist/data/version-info.cjs +3 -3
  205. package/dist/data/version-info.cjs.map +1 -1
  206. package/dist/data/version-info.js +3 -3
  207. package/dist/data/version-info.js.map +1 -1
  208. package/dist/data.cjs +11 -2
  209. package/dist/data.cjs.map +1 -1
  210. package/dist/data.d.ts +6 -2
  211. package/dist/data.d.ts.map +1 -1
  212. package/dist/data.js +11 -2
  213. package/dist/data.js.map +1 -1
  214. package/dist/hooks/use-prompts.cjs +83 -0
  215. package/dist/hooks/use-prompts.cjs.map +1 -0
  216. package/dist/hooks/use-prompts.d.ts +43 -0
  217. package/dist/hooks/use-prompts.d.ts.map +1 -0
  218. package/dist/hooks/use-prompts.js +83 -0
  219. package/dist/hooks/use-prompts.js.map +1 -0
  220. package/dist/hooks.cjs +3 -0
  221. package/dist/hooks.cjs.map +1 -1
  222. package/dist/hooks.d.ts +1 -0
  223. package/dist/hooks.d.ts.map +1 -1
  224. package/dist/hooks.js +3 -0
  225. package/dist/hooks.js.map +1 -1
  226. package/dist/index.cjs +21 -3
  227. package/dist/index.cjs.map +1 -1
  228. package/dist/index.js +21 -3
  229. package/dist/index.js.map +1 -1
  230. package/dist/jotai.cjs +9 -0
  231. package/dist/jotai.cjs.map +1 -0
  232. package/dist/jotai.d.ts +4 -0
  233. package/dist/jotai.d.ts.map +1 -0
  234. package/dist/jotai.js +9 -0
  235. package/dist/jotai.js.map +1 -0
  236. package/dist/lib/utils.cjs +18 -1
  237. package/dist/lib/utils.cjs.map +1 -1
  238. package/dist/lib/utils.d.ts.map +1 -1
  239. package/dist/lib/utils.js +18 -1
  240. package/dist/lib/utils.js.map +1 -1
  241. package/dist/mdx-editor.cjs +5 -0
  242. package/dist/mdx-editor.cjs.map +1 -0
  243. package/dist/mdx-editor.css +2 -0
  244. package/dist/mdx-editor.d.ts +2 -0
  245. package/dist/mdx-editor.d.ts.map +1 -0
  246. package/dist/mdx-editor.js +5 -0
  247. package/dist/mdx-editor.js.map +1 -0
  248. package/dist/rpc/apiClient.cjs +1 -1
  249. package/dist/rpc/apiClient.cjs.map +1 -1
  250. package/dist/rpc/apiClient.js +1 -1
  251. package/dist/rpc/apiClient.js.map +1 -1
  252. package/dist/rpc/surfaceIconMap.cjs +12 -1
  253. package/dist/rpc/surfaceIconMap.cjs.map +1 -1
  254. package/dist/rpc/surfaceIconMap.d.ts.map +1 -1
  255. package/dist/rpc/surfaceIconMap.js +12 -1
  256. package/dist/rpc/surfaceIconMap.js.map +1 -1
  257. package/dist/rpc/types.cjs.map +1 -1
  258. package/dist/rpc/types.d.ts +1 -0
  259. package/dist/rpc/types.d.ts.map +1 -1
  260. package/dist/rpc/types.js.map +1 -1
  261. package/dist/styles.css +1 -1
  262. package/oxlint-plugins/README.md +148 -0
  263. package/oxlint-plugins/clicky-ui-no-adhoc-overlay.js +88 -0
  264. package/oxlint-plugins/clicky-ui-prefer-components.js +46 -0
  265. package/oxlint-plugins/clicky-ui-prefer-icons.js +121 -0
  266. package/oxlint-plugins/clicky-ui-prefer-tailwind.js +98 -0
  267. package/oxlint-plugins/clicky-ui-prefer-theme.js +134 -0
  268. package/oxlint-plugins/clicky-ui-shared.js +132 -0
  269. package/oxlint-plugins/clicky-ui.js +34 -0
  270. package/oxlint-plugins/no-iconify-names.js +37 -0
  271. package/package.json +21 -1
  272. package/dist/data/TimeseriesCoreBars.model.cjs +0 -27
  273. package/dist/data/TimeseriesCoreBars.model.cjs.map +0 -1
  274. package/dist/data/TimeseriesCoreBars.model.d.ts +0 -20
  275. package/dist/data/TimeseriesCoreBars.model.d.ts.map +0 -1
  276. package/dist/data/TimeseriesCoreBars.model.js +0 -27
  277. package/dist/data/TimeseriesCoreBars.model.js.map +0 -1
@@ -0,0 +1,194 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ import { Icon } from "./Icon.js";
4
+ import { deriveProgressBars } from "./ProgressBars.model.js";
5
+ import { GaugeHoverCard } from "./GaugeHoverCard.js";
6
+ function toneClass(pct, [warn, danger]) {
7
+ if (pct >= danger) return "bg-red-500";
8
+ if (pct >= warn) return "bg-amber-500";
9
+ return "bg-emerald-500";
10
+ }
11
+ function ProgressBarsIcon({ icon }) {
12
+ if (typeof icon === "string") {
13
+ return /* @__PURE__ */ jsx(
14
+ Icon,
15
+ {
16
+ name: icon,
17
+ width: 14,
18
+ height: 14,
19
+ className: "text-muted-foreground"
20
+ }
21
+ );
22
+ }
23
+ return /* @__PURE__ */ jsx(
24
+ Icon,
25
+ {
26
+ icon,
27
+ width: 14,
28
+ height: 14,
29
+ className: "text-muted-foreground"
30
+ }
31
+ );
32
+ }
33
+ function defaultFormatUnits(units) {
34
+ return Number.isInteger(units) ? String(units) : units.toFixed(1);
35
+ }
36
+ function ProgressBars({
37
+ usage,
38
+ max,
39
+ unit,
40
+ title,
41
+ icon,
42
+ thresholds = [75, 90],
43
+ variant = "default",
44
+ showLabel = true,
45
+ showValue = true,
46
+ showCapacity = false,
47
+ orientation,
48
+ stats,
49
+ hoverCard = true,
50
+ hoverFooter,
51
+ className
52
+ }) {
53
+ const perBar = (unit == null ? void 0 : unit.perBar) ?? 1e3;
54
+ const unitLabel = (unit == null ? void 0 : unit.label) ?? "cores";
55
+ const barLabel = (unit == null ? void 0 : unit.barLabel) ?? "core";
56
+ const formatUnits = (unit == null ? void 0 : unit.format) ?? defaultFormatUnits;
57
+ const model = deriveProgressBars(usage, max, perBar);
58
+ const tone = toneClass(model.pct, thresholds);
59
+ const usageText = formatUnits(model.usageUnits);
60
+ const capacityText = model.limitUnits !== void 0 ? formatUnits(model.limitUnits) : "?";
61
+ const caption = !model.hasUsage ? "—" : showCapacity ? `${usageText} / ${capacityText} ${unitLabel}` : `${usageText} ${unitLabel}`;
62
+ const compactCaption = !model.hasUsage ? "—" : showCapacity ? `${usageText}/${capacityText} ${unitLabel}` : `${usageText} ${unitLabel}`;
63
+ const titleText = `${title}: ${compactCaption}`;
64
+ const isCell = variant === "cell";
65
+ const pctTone = model.limitUnits === void 0 ? void 0 : model.pct >= thresholds[1] ? "text-red-500" : model.pct >= thresholds[0] ? "text-amber-500" : "text-emerald-500";
66
+ const fmt = (n) => `${formatUnits(n)} ${unitLabel}`;
67
+ const hoverRows = model.hasUsage ? [
68
+ { label: "Current", value: fmt(model.usageUnits), ...pctTone ? { tone: pctTone } : {} },
69
+ ...stats ? [
70
+ { label: "Min", value: fmt(stats.min / perBar) },
71
+ { label: "Max", value: fmt(stats.max / perBar) },
72
+ { label: "Avg", value: fmt(stats.avg / perBar) }
73
+ ] : [],
74
+ ...model.limitUnits !== void 0 ? [{ label: "Capacity", value: fmt(model.limitUnits) }] : []
75
+ ] : [];
76
+ const resolvedOrientation = orientation ?? "vertical";
77
+ const isDense = model.barCount > 4;
78
+ const valueText = isCell ? compactCaption : caption;
79
+ const hiddenAccessibleText = !showLabel || !showValue ? titleText : void 0;
80
+ const bars = /* @__PURE__ */ jsx(
81
+ "span",
82
+ {
83
+ className: cn(
84
+ "flex shrink-0",
85
+ resolvedOrientation === "vertical" ? cn("items-end", isCell ? "h-4 gap-px" : "h-10 gap-0.5") : cn(
86
+ "flex-col justify-center",
87
+ isCell ? "h-4 w-10 gap-px" : "h-10 w-16 gap-0.5"
88
+ )
89
+ ),
90
+ "aria-hidden": isCell ? "true" : void 0,
91
+ "data-orientation": resolvedOrientation,
92
+ "data-bar-count": model.barCount,
93
+ "data-bar-density": isDense ? "compact" : "default",
94
+ children: model.bars.map((bar, i) => {
95
+ const barPct = Math.round(bar.fill * 100);
96
+ return /* @__PURE__ */ jsx(
97
+ "span",
98
+ {
99
+ className: cn(
100
+ "relative overflow-hidden bg-muted",
101
+ resolvedOrientation === "vertical" ? cn(
102
+ "h-full",
103
+ isCell ? isDense ? "w-[3px] rounded-[2px]" : "w-1.5 rounded-[2px]" : isDense ? "w-1 rounded-sm" : "w-2 rounded-sm"
104
+ ) : cn("min-h-px w-full flex-1 rounded-[2px]")
105
+ ),
106
+ title: `${barLabel} ${i + 1}: ${barPct}%`,
107
+ "aria-label": !isCell ? `${barLabel} ${i + 1}: ${barPct}%` : void 0,
108
+ "data-fill": bar.fill,
109
+ children: /* @__PURE__ */ jsx(
110
+ "span",
111
+ {
112
+ className: cn(
113
+ "absolute bottom-0 left-0 transition-all duration-300",
114
+ tone
115
+ ),
116
+ style: resolvedOrientation === "vertical" ? { height: `${barPct}%`, right: 0 } : { width: `${barPct}%`, top: 0 }
117
+ }
118
+ )
119
+ },
120
+ i
121
+ );
122
+ })
123
+ }
124
+ );
125
+ const valueNode = showValue ? /* @__PURE__ */ jsx(
126
+ "span",
127
+ {
128
+ className: cn(
129
+ "shrink-0 font-semibold tabular-nums text-foreground",
130
+ isCell ? null : "text-xs"
131
+ ),
132
+ children: valueText
133
+ }
134
+ ) : null;
135
+ const label = icon || showLabel ? /* @__PURE__ */ jsxs(
136
+ "span",
137
+ {
138
+ className: cn(
139
+ "inline-flex min-w-0 items-center gap-1 text-xs text-muted-foreground"
140
+ ),
141
+ children: [
142
+ icon ? /* @__PURE__ */ jsx(ProgressBarsIcon, { icon }) : null,
143
+ showLabel ? /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: title }) : null
144
+ ]
145
+ }
146
+ ) : null;
147
+ const useHover = hoverCard && hoverRows.length > 0;
148
+ if (isCell) {
149
+ const cell = /* @__PURE__ */ jsxs(
150
+ "span",
151
+ {
152
+ className: cn(
153
+ "inline-flex min-w-0 items-center gap-1.5 whitespace-nowrap align-middle text-xs",
154
+ className
155
+ ),
156
+ ...useHover ? {} : { title: titleText },
157
+ "aria-label": useHover ? titleText : hiddenAccessibleText,
158
+ children: [
159
+ label,
160
+ bars,
161
+ valueNode
162
+ ]
163
+ }
164
+ );
165
+ return useHover ? /* @__PURE__ */ jsx(GaugeHoverCard, { title, rows: hoverRows, footer: hoverFooter, trigger: cell }) : cell;
166
+ }
167
+ const block = /* @__PURE__ */ jsxs(
168
+ "div",
169
+ {
170
+ className: cn("flex flex-col items-center gap-1", className),
171
+ ...useHover ? {} : { title: hiddenAccessibleText },
172
+ "aria-label": useHover ? titleText : hiddenAccessibleText,
173
+ children: [
174
+ bars,
175
+ valueNode,
176
+ label
177
+ ]
178
+ }
179
+ );
180
+ return useHover ? /* @__PURE__ */ jsx(
181
+ GaugeHoverCard,
182
+ {
183
+ title,
184
+ rows: hoverRows,
185
+ footer: hoverFooter,
186
+ trigger: block,
187
+ className: "w-full justify-center"
188
+ }
189
+ ) : block;
190
+ }
191
+ export {
192
+ ProgressBars
193
+ };
194
+ //# sourceMappingURL=ProgressBars.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProgressBars.js","sources":["../../src/data/ProgressBars.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\nimport { deriveProgressBars } from \"./ProgressBars.model\";\nimport { GaugeHoverCard, type GaugeHoverRow } from \"./GaugeHoverCard\";\n\nexport type ProgressBarsVariant = \"default\" | \"cell\";\nexport type ProgressBarsOrientation = \"horizontal\" | \"vertical\";\n\n/**\n * Describes what one bar represents, letting the same component render CPU cores,\n * memory gigabytes, or any other quantised capacity. Defaults model CPU cores.\n */\nexport interface ProgressBarsUnit {\n /** Raw value that fills one bar, in the domain of `usage`/`max`. Default 1000 (millicores → 1 core). */\n perBar?: number;\n /** Caption/tooltip suffix naming the unit, plural. Default \"cores\". */\n label?: string;\n /** Per-bar hover tooltip noun, singular. Default \"core\". */\n barLabel?: string;\n /** Format a bar-unit count for the caption. Default: integers as-is, else 1 decimal. */\n format?: (units: number) => string;\n}\n\n/** Summary statistics for the hover card, in the same domain as `usage`/`max`. */\nexport interface ProgressBarsStats {\n /** Smallest value over the window. */\n min: number;\n /** Largest value over the window. */\n max: number;\n /** Mean value over the window. */\n avg: number;\n}\n\nexport interface ProgressBarsProps {\n /** Usage that fills the bars, in the raw domain (e.g. CPU millicores). */\n usage: number | undefined;\n /**\n * The capacity, in the same raw domain as `usage`. Determines the number of\n * bars (one per `unit.perBar`). When omitted or 0, the bar count falls back to\n * the ceiling of the usage so an unbounded reading still renders.\n */\n max?: number;\n /** What one bar represents. Defaults to CPU cores (perBar 1000, label \"cores\"). */\n unit?: ProgressBarsUnit;\n title: string;\n /** Iconify name or static icon component shown beside the label. */\n icon?: string | StaticIconComponent;\n /**\n * Utilisation thresholds (percent of capacity) at which the fill turns amber\n * then red. Defaults to [75, 90].\n */\n thresholds?: [warning: number, danger: number];\n /** Visual density/layout. `cell` is a compact inline form for table/grid cells. */\n variant?: ProgressBarsVariant;\n /** Show the title text. Icons and values remain visible unless separately hidden. */\n showLabel?: boolean;\n /** Show the value/capacity caption. Icons and bars remain visible when false. */\n showValue?: boolean;\n /**\n * Include the capacity in the caption (\"2.3 / 4 cores\"). Defaults to false — the\n * caption shows only the current value (\"2.3 cores\") since the bars already\n * convey capacity. Set true to append the \"/ <capacity>\".\n */\n showCapacity?: boolean;\n /** Bar direction. Vertical bars fill bottom-up; horizontal bars fill left-to-right. */\n orientation?: ProgressBarsOrientation;\n /**\n * Summary stats for the hover card's Min/Max/Avg rows, in the SAME raw domain as\n * `usage`/`max` (NOT divided by perBar). Pass null to omit those rows.\n */\n stats?: ProgressBarsStats | null;\n /**\n * Wrap the bars in a hover card summarising the metric (label + current / min /\n * max / avg / capacity). Defaults to true.\n */\n hoverCard?: boolean;\n /** Footer note in the hover card, e.g. the look-back window (\"over last 1h\"). */\n hoverFooter?: ReactNode;\n className?: string;\n}\n\nfunction toneClass(pct: number, [warn, danger]: [number, number]): string {\n if (pct >= danger) return \"bg-red-500\";\n if (pct >= warn) return \"bg-amber-500\";\n return \"bg-emerald-500\";\n}\n\nfunction ProgressBarsIcon({ icon }: { icon: string | StaticIconComponent }) {\n if (typeof icon === \"string\") {\n return (\n <Icon\n name={icon}\n width={14}\n height={14}\n className=\"text-muted-foreground\"\n />\n );\n }\n return (\n <Icon\n icon={icon}\n width={14}\n height={14}\n className=\"text-muted-foreground\"\n />\n );\n}\n\nfunction defaultFormatUnits(units: number): string {\n return Number.isInteger(units) ? String(units) : units.toFixed(1);\n}\n\n/**\n * ProgressBars renders quantised utilisation as a row of bars — one bar per unit\n * of capacity (the ceiling of the limit) — filled left to right by the usage. The\n * bar that straddles the usage boundary is partially filled (\"blocked out\") to\n * show the fractional unit in use, and the trailing bars stay empty to show\n * headroom; the bar colour crosses warning and danger thresholds. A \"<used> /\n * <capacity>\" caption sits below with the icon+title. It is purely presentational\n * — pass plain `usage`/`max` numbers (TimeseriesCoreBars wraps it with live data).\n */\nexport function ProgressBars({\n usage,\n max,\n unit,\n title,\n icon,\n thresholds = [75, 90],\n variant = \"default\",\n showLabel = true,\n showValue = true,\n showCapacity = false,\n orientation,\n stats,\n hoverCard = true,\n hoverFooter,\n className,\n}: ProgressBarsProps) {\n const perBar = unit?.perBar ?? 1000;\n const unitLabel = unit?.label ?? \"cores\";\n const barLabel = unit?.barLabel ?? \"core\";\n const formatUnits = unit?.format ?? defaultFormatUnits;\n\n const model = deriveProgressBars(usage, max, perBar);\n const tone = toneClass(model.pct, thresholds);\n const usageText = formatUnits(model.usageUnits);\n const capacityText =\n model.limitUnits !== undefined ? formatUnits(model.limitUnits) : \"?\";\n const caption = !model.hasUsage\n ? \"—\"\n : showCapacity\n ? `${usageText} / ${capacityText} ${unitLabel}`\n : `${usageText} ${unitLabel}`;\n const compactCaption = !model.hasUsage\n ? \"—\"\n : showCapacity\n ? `${usageText}/${capacityText} ${unitLabel}`\n : `${usageText} ${unitLabel}`;\n const titleText = `${title}: ${compactCaption}`;\n const isCell = variant === \"cell\";\n\n // Hover card: surface current/min/max/avg/capacity in bar-units (stats are in\n // the raw domain, so divide by perBar) so they read in the same unit as the\n // caption. A bounded reading colours the current row by its threshold.\n const pctTone =\n model.limitUnits === undefined\n ? undefined\n : model.pct >= thresholds[1]\n ? \"text-red-500\"\n : model.pct >= thresholds[0]\n ? \"text-amber-500\"\n : \"text-emerald-500\";\n const fmt = (n: number) => `${formatUnits(n)} ${unitLabel}`;\n const hoverRows: GaugeHoverRow[] = model.hasUsage\n ? [\n { label: \"Current\", value: fmt(model.usageUnits), ...(pctTone ? { tone: pctTone } : {}) },\n ...(stats\n ? [\n { label: \"Min\", value: fmt(stats.min / perBar) },\n { label: \"Max\", value: fmt(stats.max / perBar) },\n { label: \"Avg\", value: fmt(stats.avg / perBar) },\n ]\n : []),\n ...(model.limitUnits !== undefined\n ? [{ label: \"Capacity\", value: fmt(model.limitUnits) }]\n : []),\n ]\n : [];\n\n const resolvedOrientation = orientation ?? \"vertical\";\n const isDense = model.barCount > 4;\n const valueText = isCell ? compactCaption : caption;\n const hiddenAccessibleText = !showLabel || !showValue ? titleText : undefined;\n const bars = (\n <span\n className={cn(\n \"flex shrink-0\",\n resolvedOrientation === \"vertical\"\n ? cn(\"items-end\", isCell ? \"h-4 gap-px\" : \"h-10 gap-0.5\")\n : cn(\n \"flex-col justify-center\",\n isCell ? \"h-4 w-10 gap-px\" : \"h-10 w-16 gap-0.5\",\n ),\n )}\n aria-hidden={isCell ? \"true\" : undefined}\n data-orientation={resolvedOrientation}\n data-bar-count={model.barCount}\n data-bar-density={isDense ? \"compact\" : \"default\"}\n >\n {model.bars.map((bar, i) => {\n const barPct = Math.round(bar.fill * 100);\n return (\n <span\n key={i}\n className={cn(\n \"relative overflow-hidden bg-muted\",\n resolvedOrientation === \"vertical\"\n ? cn(\n \"h-full\",\n isCell\n ? isDense\n ? \"w-[3px] rounded-[2px]\"\n : \"w-1.5 rounded-[2px]\"\n : isDense\n ? \"w-1 rounded-sm\"\n : \"w-2 rounded-sm\",\n )\n : cn(\"min-h-px w-full flex-1 rounded-[2px]\"),\n )}\n title={`${barLabel} ${i + 1}: ${barPct}%`}\n aria-label={!isCell ? `${barLabel} ${i + 1}: ${barPct}%` : undefined}\n data-fill={bar.fill}\n >\n <span\n className={cn(\n \"absolute bottom-0 left-0 transition-all duration-300\",\n tone,\n )}\n style={\n resolvedOrientation === \"vertical\"\n ? { height: `${barPct}%`, right: 0 }\n : { width: `${barPct}%`, top: 0 }\n }\n />\n </span>\n );\n })}\n </span>\n );\n const valueNode = showValue ? (\n <span\n className={cn(\n \"shrink-0 font-semibold tabular-nums text-foreground\",\n isCell ? null : \"text-xs\",\n )}\n >\n {valueText}\n </span>\n ) : null;\n const label =\n icon || showLabel ? (\n <span\n className={cn(\n \"inline-flex min-w-0 items-center gap-1 text-xs text-muted-foreground\",\n )}\n >\n {icon ? <ProgressBarsIcon icon={icon} /> : null}\n {showLabel ? <span className=\"min-w-0 truncate\">{title}</span> : null}\n </span>\n ) : null;\n\n const useHover = hoverCard && hoverRows.length > 0;\n\n if (isCell) {\n const cell = (\n <span\n className={cn(\n \"inline-flex min-w-0 items-center gap-1.5 whitespace-nowrap align-middle text-xs\",\n className,\n )}\n {...(useHover ? {} : { title: titleText })}\n aria-label={useHover ? titleText : hiddenAccessibleText}\n >\n {label}\n {bars}\n {valueNode}\n </span>\n );\n return useHover ? (\n <GaugeHoverCard title={title} rows={hoverRows} footer={hoverFooter} trigger={cell} />\n ) : (\n cell\n );\n }\n\n const block = (\n <div\n className={cn(\"flex flex-col items-center gap-1\", className)}\n {...(useHover ? {} : { title: hiddenAccessibleText })}\n aria-label={useHover ? titleText : hiddenAccessibleText}\n >\n {bars}\n {valueNode}\n {label}\n </div>\n );\n return useHover ? (\n <GaugeHoverCard\n title={title}\n rows={hoverRows}\n footer={hoverFooter}\n trigger={block}\n className=\"w-full justify-center\"\n />\n ) : (\n block\n );\n}\n"],"names":[],"mappings":";;;;;AAkFA,SAAS,UAAU,KAAa,CAAC,MAAM,MAAM,GAA6B;AACxE,MAAI,OAAO,OAAQ,QAAO;AAC1B,MAAI,OAAO,KAAM,QAAO;AACxB,SAAO;AACT;AAEA,SAAS,iBAAiB,EAAE,QAAgD;AAC1E,MAAI,OAAO,SAAS,UAAU;AAC5B,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAM;AAAA,QACN,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,WAAU;AAAA,MAAA;AAAA,IAAA;AAAA,EAGhB;AACA,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,WAAU;AAAA,IAAA;AAAA,EAAA;AAGhB;AAEA,SAAS,mBAAmB,OAAuB;AACjD,SAAO,OAAO,UAAU,KAAK,IAAI,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAClE;AAWO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa,CAAC,IAAI,EAAE;AAAA,EACpB,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AACF,GAAsB;AACpB,QAAM,UAAS,6BAAM,WAAU;AAC/B,QAAM,aAAY,6BAAM,UAAS;AACjC,QAAM,YAAW,6BAAM,aAAY;AACnC,QAAM,eAAc,6BAAM,WAAU;AAEpC,QAAM,QAAQ,mBAAmB,OAAO,KAAK,MAAM;AACnD,QAAM,OAAO,UAAU,MAAM,KAAK,UAAU;AAC5C,QAAM,YAAY,YAAY,MAAM,UAAU;AAC9C,QAAM,eACJ,MAAM,eAAe,SAAY,YAAY,MAAM,UAAU,IAAI;AACnE,QAAM,UAAU,CAAC,MAAM,WACnB,MACA,eACE,GAAG,SAAS,MAAM,YAAY,IAAI,SAAS,KAC3C,GAAG,SAAS,IAAI,SAAS;AAC/B,QAAM,iBAAiB,CAAC,MAAM,WAC1B,MACA,eACE,GAAG,SAAS,IAAI,YAAY,IAAI,SAAS,KACzC,GAAG,SAAS,IAAI,SAAS;AAC/B,QAAM,YAAY,GAAG,KAAK,KAAK,cAAc;AAC7C,QAAM,SAAS,YAAY;AAK3B,QAAM,UACJ,MAAM,eAAe,SACjB,SACA,MAAM,OAAO,WAAW,CAAC,IACvB,iBACA,MAAM,OAAO,WAAW,CAAC,IACvB,mBACA;AACV,QAAM,MAAM,CAAC,MAAc,GAAG,YAAY,CAAC,CAAC,IAAI,SAAS;AACzD,QAAM,YAA6B,MAAM,WACrC;AAAA,IACE,EAAE,OAAO,WAAW,OAAO,IAAI,MAAM,UAAU,GAAG,GAAI,UAAU,EAAE,MAAM,QAAA,IAAY,CAAA,EAAC;AAAA,IACrF,GAAI,QACA;AAAA,MACE,EAAE,OAAO,OAAO,OAAO,IAAI,MAAM,MAAM,MAAM,EAAA;AAAA,MAC7C,EAAE,OAAO,OAAO,OAAO,IAAI,MAAM,MAAM,MAAM,EAAA;AAAA,MAC7C,EAAE,OAAO,OAAO,OAAO,IAAI,MAAM,MAAM,MAAM,EAAA;AAAA,IAAE,IAEjD,CAAA;AAAA,IACJ,GAAI,MAAM,eAAe,SACrB,CAAC,EAAE,OAAO,YAAY,OAAO,IAAI,MAAM,UAAU,EAAA,CAAG,IACpD,CAAA;AAAA,EAAC,IAEP,CAAA;AAEJ,QAAM,sBAAsB,eAAe;AAC3C,QAAM,UAAU,MAAM,WAAW;AACjC,QAAM,YAAY,SAAS,iBAAiB;AAC5C,QAAM,uBAAuB,CAAC,aAAa,CAAC,YAAY,YAAY;AACpE,QAAM,OACJ;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,wBAAwB,aACpB,GAAG,aAAa,SAAS,eAAe,cAAc,IACtD;AAAA,UACE;AAAA,UACA,SAAS,oBAAoB;AAAA,QAAA;AAAA,MAC/B;AAAA,MAEN,eAAa,SAAS,SAAS;AAAA,MAC/B,oBAAkB;AAAA,MAClB,kBAAgB,MAAM;AAAA,MACtB,oBAAkB,UAAU,YAAY;AAAA,MAEvC,UAAA,MAAM,KAAK,IAAI,CAAC,KAAK,MAAM;AAC1B,cAAM,SAAS,KAAK,MAAM,IAAI,OAAO,GAAG;AACxC,eACE;AAAA,UAAC;AAAA,UAAA;AAAA,YAEC,WAAW;AAAA,cACT;AAAA,cACA,wBAAwB,aACpB;AAAA,gBACE;AAAA,gBACA,SACI,UACE,0BACA,wBACF,UACE,mBACA;AAAA,cAAA,IAER,GAAG,sCAAsC;AAAA,YAAA;AAAA,YAE/C,OAAO,GAAG,QAAQ,IAAI,IAAI,CAAC,KAAK,MAAM;AAAA,YACtC,cAAY,CAAC,SAAS,GAAG,QAAQ,IAAI,IAAI,CAAC,KAAK,MAAM,MAAM;AAAA,YAC3D,aAAW,IAAI;AAAA,YAEf,UAAA;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,WAAW;AAAA,kBACT;AAAA,kBACA;AAAA,gBAAA;AAAA,gBAEF,OACE,wBAAwB,aACpB,EAAE,QAAQ,GAAG,MAAM,KAAK,OAAO,EAAA,IAC/B,EAAE,OAAO,GAAG,MAAM,KAAK,KAAK,EAAA;AAAA,cAAE;AAAA,YAAA;AAAA,UAEtC;AAAA,UA9BK;AAAA,QAAA;AAAA,MAiCX,CAAC;AAAA,IAAA;AAAA,EAAA;AAGL,QAAM,YAAY,YAChB;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,SAAS,OAAO;AAAA,MAAA;AAAA,MAGjB,UAAA;AAAA,IAAA;AAAA,EAAA,IAED;AACJ,QAAM,QACJ,QAAQ,YACN;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,MAAA;AAAA,MAGD,UAAA;AAAA,QAAA,OAAO,oBAAC,kBAAA,EAAiB,KAAA,CAAY,IAAK;AAAA,QAC1C,YAAY,oBAAC,QAAA,EAAK,WAAU,oBAAoB,iBAAM,IAAU;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,IAEjE;AAEN,QAAM,WAAW,aAAa,UAAU,SAAS;AAEjD,MAAI,QAAQ;AACV,UAAM,OACJ;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QAAA;AAAA,QAED,GAAI,WAAW,CAAA,IAAK,EAAE,OAAO,UAAA;AAAA,QAC9B,cAAY,WAAW,YAAY;AAAA,QAElC,UAAA;AAAA,UAAA;AAAA,UACA;AAAA,UACA;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAGL,WAAO,WACL,oBAAC,gBAAA,EAAe,OAAc,MAAM,WAAW,QAAQ,aAAa,SAAS,KAAA,CAAM,IAEnF;AAAA,EAEJ;AAEA,QAAM,QACJ;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,GAAG,oCAAoC,SAAS;AAAA,MAC1D,GAAI,WAAW,CAAA,IAAK,EAAE,OAAO,qBAAA;AAAA,MAC9B,cAAY,WAAW,YAAY;AAAA,MAElC,UAAA;AAAA,QAAA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGL,SAAO,WACL;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,WAAU;AAAA,IAAA;AAAA,EAAA,IAGZ;AAEJ;"}
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const clamp01 = (n) => Math.min(1, Math.max(0, n));
4
+ function deriveProgressBars(usage, limit, perBar = 1e3) {
5
+ const hasUsage = usage !== void 0;
6
+ const usageUnits = (usage ?? 0) / perBar;
7
+ const hasLimit = limit !== void 0 && limit > 0;
8
+ const limitUnits = hasLimit ? limit / perBar : void 0;
9
+ const barCount = Math.max(
10
+ 1,
11
+ Math.ceil(hasLimit ? limitUnits : usageUnits)
12
+ );
13
+ const pct = hasLimit ? Math.min(100, Math.round(usageUnits / limitUnits * 100)) : 0;
14
+ const bars = Array.from({ length: barCount }, (_, i) => ({
15
+ fill: clamp01(usageUnits - i)
16
+ }));
17
+ return {
18
+ hasUsage,
19
+ usageUnits,
20
+ barCount,
21
+ pct,
22
+ bars,
23
+ ...limitUnits !== void 0 && { limitUnits }
24
+ };
25
+ }
26
+ exports.deriveProgressBars = deriveProgressBars;
27
+ //# sourceMappingURL=ProgressBars.model.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProgressBars.model.cjs","sources":["../../src/data/ProgressBars.model.ts"],"sourcesContent":["export interface ProgressBarFill {\n /** Fraction of this bar that is in use, in [0, 1]. */\n fill: number;\n}\n\nexport interface ProgressBarsModel {\n /** Whether a usage reading was available. */\n hasUsage: boolean;\n /** Usage expressed in bar-units (raw / perBar). */\n usageUnits: number;\n /** Capacity in bar-units; undefined when no limit was resolved. */\n limitUnits?: number;\n /** Number of bars rendered (one per bar-unit of capacity). */\n barCount: number;\n /** Utilisation percentage of capacity; 0 when unbounded. */\n pct: number;\n /** Per-bar fill fractions, left to right. */\n bars: ProgressBarFill[];\n}\n\nconst clamp01 = (n: number): number => Math.min(1, Math.max(0, n));\n\n// deriveProgressBars turns the latest usage/limit readings into a per-bar model: one\n// bar per unit of capacity (ceil of the limit), filled left to right by usage so\n// the bar straddling the usage boundary is partially filled and the remainder\n// stays empty. When the limit is missing or 0 the bar count falls back to the\n// ceiling of the usage so an unbounded reading still renders sensibly. `perBar`\n// is the raw value that fills one bar (default 1000 = millicores per core); pass\n// e.g. 1 GiB to render one bar per gigabyte of memory.\nexport function deriveProgressBars(\n usage: number | undefined,\n limit: number | undefined,\n perBar = 1000,\n): ProgressBarsModel {\n const hasUsage = usage !== undefined;\n const usageUnits = (usage ?? 0) / perBar;\n const hasLimit = limit !== undefined && limit > 0;\n const limitUnits = hasLimit ? (limit as number) / perBar : undefined;\n\n const barCount = Math.max(\n 1,\n Math.ceil(hasLimit ? (limitUnits as number) : usageUnits),\n );\n const pct = hasLimit\n ? Math.min(100, Math.round((usageUnits / (limitUnits as number)) * 100))\n : 0;\n\n const bars: ProgressBarFill[] = Array.from({ length: barCount }, (_, i) => ({\n fill: clamp01(usageUnits - i),\n }));\n\n return {\n hasUsage,\n usageUnits,\n barCount,\n pct,\n bars,\n ...(limitUnits !== undefined && { limitUnits }),\n };\n}\n"],"names":[],"mappings":";;AAoBA,MAAM,UAAU,CAAC,MAAsB,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,CAAC,CAAC;AAS1D,SAAS,mBACd,OACA,OACA,SAAS,KACU;AACnB,QAAM,WAAW,UAAU;AAC3B,QAAM,cAAc,SAAS,KAAK;AAClC,QAAM,WAAW,UAAU,UAAa,QAAQ;AAChD,QAAM,aAAa,WAAY,QAAmB,SAAS;AAE3D,QAAM,WAAW,KAAK;AAAA,IACpB;AAAA,IACA,KAAK,KAAK,WAAY,aAAwB,UAAU;AAAA,EAAA;AAE1D,QAAM,MAAM,WACR,KAAK,IAAI,KAAK,KAAK,MAAO,aAAc,aAAyB,GAAG,CAAC,IACrE;AAEJ,QAAM,OAA0B,MAAM,KAAK,EAAE,QAAQ,SAAA,GAAY,CAAC,GAAG,OAAO;AAAA,IAC1E,MAAM,QAAQ,aAAa,CAAC;AAAA,EAAA,EAC5B;AAEF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,eAAe,UAAa,EAAE,WAAA;AAAA,EAAW;AAEjD;;"}
@@ -0,0 +1,20 @@
1
+ export interface ProgressBarFill {
2
+ /** Fraction of this bar that is in use, in [0, 1]. */
3
+ fill: number;
4
+ }
5
+ export interface ProgressBarsModel {
6
+ /** Whether a usage reading was available. */
7
+ hasUsage: boolean;
8
+ /** Usage expressed in bar-units (raw / perBar). */
9
+ usageUnits: number;
10
+ /** Capacity in bar-units; undefined when no limit was resolved. */
11
+ limitUnits?: number;
12
+ /** Number of bars rendered (one per bar-unit of capacity). */
13
+ barCount: number;
14
+ /** Utilisation percentage of capacity; 0 when unbounded. */
15
+ pct: number;
16
+ /** Per-bar fill fractions, left to right. */
17
+ bars: ProgressBarFill[];
18
+ }
19
+ export declare function deriveProgressBars(usage: number | undefined, limit: number | undefined, perBar?: number): ProgressBarsModel;
20
+ //# sourceMappingURL=ProgressBars.model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProgressBars.model.d.ts","sourceRoot":"","sources":["../../src/data/ProgressBars.model.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,6CAA6C;IAC7C,QAAQ,EAAE,OAAO,CAAC;IAClB,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,6CAA6C;IAC7C,IAAI,EAAE,eAAe,EAAE,CAAC;CACzB;AAWD,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,SAAO,GACZ,iBAAiB,CA0BnB"}
@@ -0,0 +1,27 @@
1
+ const clamp01 = (n) => Math.min(1, Math.max(0, n));
2
+ function deriveProgressBars(usage, limit, perBar = 1e3) {
3
+ const hasUsage = usage !== void 0;
4
+ const usageUnits = (usage ?? 0) / perBar;
5
+ const hasLimit = limit !== void 0 && limit > 0;
6
+ const limitUnits = hasLimit ? limit / perBar : void 0;
7
+ const barCount = Math.max(
8
+ 1,
9
+ Math.ceil(hasLimit ? limitUnits : usageUnits)
10
+ );
11
+ const pct = hasLimit ? Math.min(100, Math.round(usageUnits / limitUnits * 100)) : 0;
12
+ const bars = Array.from({ length: barCount }, (_, i) => ({
13
+ fill: clamp01(usageUnits - i)
14
+ }));
15
+ return {
16
+ hasUsage,
17
+ usageUnits,
18
+ barCount,
19
+ pct,
20
+ bars,
21
+ ...limitUnits !== void 0 && { limitUnits }
22
+ };
23
+ }
24
+ export {
25
+ deriveProgressBars
26
+ };
27
+ //# sourceMappingURL=ProgressBars.model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProgressBars.model.js","sources":["../../src/data/ProgressBars.model.ts"],"sourcesContent":["export interface ProgressBarFill {\n /** Fraction of this bar that is in use, in [0, 1]. */\n fill: number;\n}\n\nexport interface ProgressBarsModel {\n /** Whether a usage reading was available. */\n hasUsage: boolean;\n /** Usage expressed in bar-units (raw / perBar). */\n usageUnits: number;\n /** Capacity in bar-units; undefined when no limit was resolved. */\n limitUnits?: number;\n /** Number of bars rendered (one per bar-unit of capacity). */\n barCount: number;\n /** Utilisation percentage of capacity; 0 when unbounded. */\n pct: number;\n /** Per-bar fill fractions, left to right. */\n bars: ProgressBarFill[];\n}\n\nconst clamp01 = (n: number): number => Math.min(1, Math.max(0, n));\n\n// deriveProgressBars turns the latest usage/limit readings into a per-bar model: one\n// bar per unit of capacity (ceil of the limit), filled left to right by usage so\n// the bar straddling the usage boundary is partially filled and the remainder\n// stays empty. When the limit is missing or 0 the bar count falls back to the\n// ceiling of the usage so an unbounded reading still renders sensibly. `perBar`\n// is the raw value that fills one bar (default 1000 = millicores per core); pass\n// e.g. 1 GiB to render one bar per gigabyte of memory.\nexport function deriveProgressBars(\n usage: number | undefined,\n limit: number | undefined,\n perBar = 1000,\n): ProgressBarsModel {\n const hasUsage = usage !== undefined;\n const usageUnits = (usage ?? 0) / perBar;\n const hasLimit = limit !== undefined && limit > 0;\n const limitUnits = hasLimit ? (limit as number) / perBar : undefined;\n\n const barCount = Math.max(\n 1,\n Math.ceil(hasLimit ? (limitUnits as number) : usageUnits),\n );\n const pct = hasLimit\n ? Math.min(100, Math.round((usageUnits / (limitUnits as number)) * 100))\n : 0;\n\n const bars: ProgressBarFill[] = Array.from({ length: barCount }, (_, i) => ({\n fill: clamp01(usageUnits - i),\n }));\n\n return {\n hasUsage,\n usageUnits,\n barCount,\n pct,\n bars,\n ...(limitUnits !== undefined && { limitUnits }),\n };\n}\n"],"names":[],"mappings":"AAoBA,MAAM,UAAU,CAAC,MAAsB,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,CAAC,CAAC;AAS1D,SAAS,mBACd,OACA,OACA,SAAS,KACU;AACnB,QAAM,WAAW,UAAU;AAC3B,QAAM,cAAc,SAAS,KAAK;AAClC,QAAM,WAAW,UAAU,UAAa,QAAQ;AAChD,QAAM,aAAa,WAAY,QAAmB,SAAS;AAE3D,QAAM,WAAW,KAAK;AAAA,IACpB;AAAA,IACA,KAAK,KAAK,WAAY,aAAwB,UAAU;AAAA,EAAA;AAE1D,QAAM,MAAM,WACR,KAAK,IAAI,KAAK,KAAK,MAAO,aAAc,aAAyB,GAAG,CAAC,IACrE;AAEJ,QAAM,OAA0B,MAAM,KAAK,EAAE,QAAQ,SAAA,GAAY,CAAC,GAAG,OAAO;AAAA,IAC1E,MAAM,QAAQ,aAAa,CAAC;AAAA,EAAA,EAC5B;AAEF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAI,eAAe,UAAa,EAAE,WAAA;AAAA,EAAW;AAEjD;"}
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const react = require("react");
5
+ const button = require("../components/button.cjs");
6
+ const Icon = require("./Icon.cjs");
7
+ const utils = require("../lib/utils.cjs");
8
+ const usePrompts = require("../hooks/use-prompts.cjs");
9
+ const PromptDialog = require("./PromptDialog.cjs");
10
+ const PromptBanner_model = require("./PromptBanner.model.cjs");
11
+ const UiQuestion = require("../icons/components/UiQuestion.cjs");
12
+ const UiRobotAi = require("../icons/components/UiRobotAi.cjs");
13
+ const UiClose = require("../icons/components/UiClose.cjs");
14
+ const UiCheck = require("../icons/components/UiCheck.cjs");
15
+ const UiHourglass = require("../icons/components/UiHourglass.cjs");
16
+ const UiComment = require("../icons/components/UiComment.cjs");
17
+ function PromptBanner({
18
+ basePath = "/api/todos",
19
+ enabled,
20
+ title,
21
+ className,
22
+ inlineActions = "auto",
23
+ showSummary = true,
24
+ historyLimit = 5,
25
+ state: _ignoredState,
26
+ ...filter
27
+ }) {
28
+ const { prompts, pending } = usePrompts.usePrompts({
29
+ ...filter,
30
+ basePath,
31
+ ...enabled !== void 0 ? { enabled } : {},
32
+ ...showSummary ? {} : { state: "pending" }
33
+ });
34
+ const [active, setActive] = react.useState(null);
35
+ const [comment, setComment] = react.useState("");
36
+ const [resolving, setResolving] = react.useState(null);
37
+ const [error, setError] = react.useState(null);
38
+ const next = pending[0];
39
+ const activePrompt = active ?? next;
40
+ const inlineSpec = react.useMemo(
41
+ () => inlineActions === "auto" && activePrompt ? PromptBanner_model.analyzePromptInlineActions(activePrompt) : null,
42
+ [activePrompt, inlineActions]
43
+ );
44
+ const summary = react.useMemo(() => PromptBanner_model.summarizePrompts(prompts), [prompts]);
45
+ const recent = prompts.slice(0, Math.max(0, historyLimit));
46
+ react.useEffect(() => {
47
+ setComment(activePrompt ? PromptBanner_model.initialPromptComment(activePrompt, inlineSpec) : "");
48
+ setError(null);
49
+ setResolving(null);
50
+ }, [activePrompt == null ? void 0 : activePrompt.id, inlineSpec]);
51
+ if (showSummary ? prompts.length === 0 : !next) return null;
52
+ const headline = title ?? (pending.length > 1 ? `${pending.length} questions need your input` : (next == null ? void 0 : next.title) ?? "No pending prompts");
53
+ const resolveInline = async (decision) => {
54
+ if (!activePrompt || !inlineSpec) return;
55
+ setResolving(decision);
56
+ setError(null);
57
+ try {
58
+ await usePrompts.answerPrompt(basePath, activePrompt.id, {
59
+ values: PromptBanner_model.buildPromptInlineValues(activePrompt, inlineSpec, decision, comment)
60
+ });
61
+ setActive(null);
62
+ } catch (e) {
63
+ setError(e instanceof Error ? e.message : String(e));
64
+ } finally {
65
+ setResolving(null);
66
+ }
67
+ };
68
+ const commentRequiredButEmpty = Boolean((inlineSpec == null ? void 0 : inlineSpec.commentRequired) && comment.trim().length === 0);
69
+ return /* @__PURE__ */ jsxRuntime.jsxs(
70
+ "section",
71
+ {
72
+ "aria-label": "Prompt requests",
73
+ className: utils.cn(
74
+ "rounded-md border border-border bg-card px-3 py-2 text-card-foreground",
75
+ className
76
+ ),
77
+ children: [
78
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
79
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [
80
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-start gap-2", children: [
81
+ /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: next ? UiQuestion.UiQuestion : UiRobotAi.UiRobotAi, className: "mt-0.5 size-4 shrink-0" }),
82
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
83
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-sm font-medium", children: headline }),
84
+ (next == null ? void 0 : next.description) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 line-clamp-2 text-xs opacity-80", children: next.description }) : null
85
+ ] })
86
+ ] }),
87
+ showSummary ? /* @__PURE__ */ jsxRuntime.jsx(PromptSummaryCounts, { summary }) : null
88
+ ] }),
89
+ next ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: inlineSpec ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
90
+ inlineSpec.commentField ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex min-w-0 flex-col gap-1 text-xs", children: [
91
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: inlineSpec.commentLabel ?? "Comment" }),
92
+ /* @__PURE__ */ jsxRuntime.jsx(
93
+ "textarea",
94
+ {
95
+ className: "min-h-16 w-full resize-y rounded-md border border-input bg-background px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
96
+ placeholder: "Add a short note",
97
+ value: comment,
98
+ onChange: (event) => setComment(event.target.value)
99
+ }
100
+ )
101
+ ] }) : null,
102
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [
103
+ error ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mr-auto text-xs text-red-600 dark:text-red-300", children: error }) : null,
104
+ /* @__PURE__ */ jsxRuntime.jsxs(
105
+ button.Button,
106
+ {
107
+ size: "sm",
108
+ variant: "outline",
109
+ type: "button",
110
+ loading: resolving === "reject",
111
+ disabled: resolving !== null || commentRequiredButEmpty,
112
+ onClick: () => void resolveInline("reject"),
113
+ className: "gap-1.5",
114
+ children: [
115
+ /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiClose.UiClose, className: "size-3.5" }),
116
+ "Reject"
117
+ ]
118
+ }
119
+ ),
120
+ /* @__PURE__ */ jsxRuntime.jsxs(
121
+ button.Button,
122
+ {
123
+ size: "sm",
124
+ type: "button",
125
+ loading: resolving === "approve",
126
+ disabled: resolving !== null || commentRequiredButEmpty,
127
+ onClick: () => void resolveInline("approve"),
128
+ className: "gap-1.5",
129
+ children: [
130
+ /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiCheck.UiCheck, className: "size-3.5" }),
131
+ "Approve"
132
+ ]
133
+ }
134
+ ),
135
+ /* @__PURE__ */ jsxRuntime.jsx(button.Button, { size: "sm", variant: "ghost", type: "button", onClick: () => setActive(next), children: "More" })
136
+ ] })
137
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(button.Button, { size: "sm", type: "button", onClick: () => setActive(next), children: "Answer" }) }) }) : null,
138
+ showSummary && recent.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(PromptHistory, { prompts: recent, activeId: next == null ? void 0 : next.id }) : null
139
+ ] }),
140
+ active ? /* @__PURE__ */ jsxRuntime.jsx(
141
+ PromptDialog.PromptDialog,
142
+ {
143
+ prompt: active,
144
+ basePath,
145
+ open: true,
146
+ onClose: () => setActive(null),
147
+ onResolved: () => setActive(null)
148
+ }
149
+ ) : null
150
+ ]
151
+ }
152
+ );
153
+ }
154
+ function PromptSummaryCounts({ summary }) {
155
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-x-3 gap-y-1 text-xs opacity-85", children: [
156
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 font-medium", children: [
157
+ /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiHourglass.UiHourglass, className: "size-3.5" }),
158
+ summary.pending,
159
+ " pending"
160
+ ] }),
161
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
162
+ summary.answered,
163
+ " answered"
164
+ ] }),
165
+ (summary.cancelled > 0 || summary.expired > 0) && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
166
+ summary.cancelled + summary.expired,
167
+ " closed"
168
+ ] })
169
+ ] });
170
+ }
171
+ function PromptHistory({ prompts, activeId }) {
172
+ return /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "divide-y divide-border overflow-hidden rounded-md border border-border bg-background text-xs", children: prompts.map((prompt) => /* @__PURE__ */ jsxRuntime.jsxs(
173
+ "li",
174
+ {
175
+ className: utils.cn("flex min-w-0 items-start gap-2 px-2 py-1.5", prompt.id === activeId && "bg-muted/60"),
176
+ children: [
177
+ /* @__PURE__ */ jsxRuntime.jsx(PromptStateIcon, { prompt }),
178
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
179
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
180
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-medium text-foreground", children: prompt.title }),
181
+ prompt.kind ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-muted-foreground", children: prompt.kind }) : null
182
+ ] }),
183
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 truncate text-muted-foreground", children: PromptBanner_model.summarizePromptAnswer(prompt) })
184
+ ] })
185
+ ]
186
+ },
187
+ prompt.id
188
+ )) });
189
+ }
190
+ function PromptStateIcon({ prompt }) {
191
+ if (prompt.state === "answered") return /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiCheck.UiCheck, className: "mt-0.5 size-3.5 shrink-0 text-emerald-600" });
192
+ if (prompt.state === "cancelled" || prompt.state === "expired") {
193
+ return /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiClose.UiClose, className: "mt-0.5 size-3.5 shrink-0 text-rose-600" });
194
+ }
195
+ if (prompt.kind === "comment") return /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiComment.UiComment, className: "mt-0.5 size-3.5 shrink-0" });
196
+ return /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiHourglass.UiHourglass, className: "mt-0.5 size-3.5 shrink-0" });
197
+ }
198
+ exports.PromptBanner = PromptBanner;
199
+ //# sourceMappingURL=PromptBanner.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PromptBanner.cjs","sources":["../../src/data/PromptBanner.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from \"react\";\nimport { Button } from \"../components/button\";\nimport { Icon } from \"./Icon\";\nimport { UiCheck, UiClose, UiComment, UiHourglass, UiQuestion, UiRobotAi } from \"../icons\";\nimport { cn } from \"../lib/utils\";\nimport { answerPrompt, usePrompts, type PromptFilter, type PromptSnapshot } from \"../hooks/use-prompts\";\nimport { PromptDialog } from \"./PromptDialog\";\nimport {\n analyzePromptInlineActions,\n buildPromptInlineValues,\n initialPromptComment,\n summarizePromptAnswer,\n summarizePrompts,\n type PromptInlineDecision,\n} from \"./PromptBanner.model\";\n\nexport interface PromptBannerProps extends PromptFilter {\n /** Base path the prompt API is mounted under, e.g. \"/api/todos\". */\n basePath?: string | undefined;\n enabled?: boolean | undefined;\n /** Override the headline; defaults to a count-aware message. */\n title?: string | undefined;\n className?: string | undefined;\n /** Render approve/reject quick actions when the prompt schema supports them. */\n inlineActions?: \"auto\" | false | undefined;\n /** Show compact counts and recent prompt outcomes, not only the current pending prompt. */\n showSummary?: boolean | undefined;\n /** Maximum recent prompts shown in the summary list. */\n historyLimit?: number | undefined;\n}\n\n// PromptBanner subscribes to pending prompts for a scope (owner / session label)\n// and surfaces them as an inline affordance, opening a PromptDialog to answer.\n// Mount it scoped to a todo (owner) at the top of a detail page, or to a session\n// (labels.session) inside a session view using the same component and filters.\nexport function PromptBanner({\n basePath = \"/api/todos\",\n enabled,\n title,\n className,\n inlineActions = \"auto\",\n showSummary = true,\n historyLimit = 5,\n state: _ignoredState,\n ...filter\n}: PromptBannerProps) {\n const { prompts, pending } = usePrompts({\n ...filter,\n basePath,\n ...(enabled !== undefined ? { enabled } : {}),\n ...(showSummary ? {} : { state: \"pending\" }),\n });\n const [active, setActive] = useState<PromptSnapshot | null>(null);\n const [comment, setComment] = useState(\"\");\n const [resolving, setResolving] = useState<PromptInlineDecision | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const next = pending[0];\n const activePrompt = active ?? next;\n const inlineSpec = useMemo(\n () => (inlineActions === \"auto\" && activePrompt ? analyzePromptInlineActions(activePrompt) : null),\n [activePrompt, inlineActions],\n );\n const summary = useMemo(() => summarizePrompts(prompts), [prompts]);\n const recent = prompts.slice(0, Math.max(0, historyLimit));\n\n useEffect(() => {\n setComment(activePrompt ? initialPromptComment(activePrompt, inlineSpec) : \"\");\n setError(null);\n setResolving(null);\n }, [activePrompt?.id, inlineSpec]);\n\n if (showSummary ? prompts.length === 0 : !next) return null;\n const headline =\n title ??\n (pending.length > 1\n ? `${pending.length} questions need your input`\n : next?.title ?? \"No pending prompts\");\n\n const resolveInline = async (decision: PromptInlineDecision) => {\n if (!activePrompt || !inlineSpec) return;\n setResolving(decision);\n setError(null);\n try {\n await answerPrompt(basePath, activePrompt.id, {\n values: buildPromptInlineValues(activePrompt, inlineSpec, decision, comment),\n });\n setActive(null);\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e));\n } finally {\n setResolving(null);\n }\n };\n\n const commentRequiredButEmpty = Boolean(inlineSpec?.commentRequired && comment.trim().length === 0);\n\n return (\n <section\n aria-label=\"Prompt requests\"\n className={cn(\n \"rounded-md border border-border bg-card px-3 py-2 text-card-foreground\",\n className,\n )}\n >\n <div className=\"flex flex-col gap-3\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"flex min-w-0 flex-1 items-start gap-2\">\n <Icon icon={next ? UiQuestion : UiRobotAi} className=\"mt-0.5 size-4 shrink-0\" />\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium\">{headline}</div>\n {next?.description ? (\n <div className=\"mt-0.5 line-clamp-2 text-xs opacity-80\">{next.description}</div>\n ) : null}\n </div>\n </div>\n {showSummary ? <PromptSummaryCounts summary={summary} /> : null}\n </div>\n\n {next ? (\n <div className=\"flex flex-col gap-2\">\n {inlineSpec ? (\n <div className=\"flex flex-col gap-2\">\n {inlineSpec.commentField ? (\n <label className=\"flex min-w-0 flex-col gap-1 text-xs\">\n <span className=\"font-medium\">{inlineSpec.commentLabel ?? \"Comment\"}</span>\n <textarea\n className=\"min-h-16 w-full resize-y rounded-md border border-input bg-background px-2 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n placeholder=\"Add a short note\"\n value={comment}\n onChange={(event) => setComment(event.target.value)}\n />\n </label>\n ) : null}\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n {error ? <span className=\"mr-auto text-xs text-red-600 dark:text-red-300\">{error}</span> : null}\n <Button\n size=\"sm\"\n variant=\"outline\"\n type=\"button\"\n loading={resolving === \"reject\"}\n disabled={resolving !== null || commentRequiredButEmpty}\n onClick={() => void resolveInline(\"reject\")}\n className=\"gap-1.5\"\n >\n <Icon icon={UiClose} className=\"size-3.5\" />\n Reject\n </Button>\n <Button\n size=\"sm\"\n type=\"button\"\n loading={resolving === \"approve\"}\n disabled={resolving !== null || commentRequiredButEmpty}\n onClick={() => void resolveInline(\"approve\")}\n className=\"gap-1.5\"\n >\n <Icon icon={UiCheck} className=\"size-3.5\" />\n Approve\n </Button>\n <Button size=\"sm\" variant=\"ghost\" type=\"button\" onClick={() => setActive(next)}>\n More\n </Button>\n </div>\n </div>\n ) : (\n <div className=\"flex items-center justify-end\">\n <Button size=\"sm\" type=\"button\" onClick={() => setActive(next)}>\n Answer\n </Button>\n </div>\n )}\n </div>\n ) : null}\n\n {showSummary && recent.length > 0 ? <PromptHistory prompts={recent} activeId={next?.id} /> : null}\n </div>\n {active ? (\n <PromptDialog\n prompt={active}\n basePath={basePath}\n open={true}\n onClose={() => setActive(null)}\n onResolved={() => setActive(null)}\n />\n ) : null}\n </section>\n );\n}\n\nfunction PromptSummaryCounts({ summary }: { summary: ReturnType<typeof summarizePrompts> }) {\n return (\n <div className=\"flex shrink-0 flex-wrap items-center gap-x-3 gap-y-1 text-xs opacity-85\">\n <span className=\"inline-flex items-center gap-1 font-medium\">\n <Icon icon={UiHourglass} className=\"size-3.5\" />\n {summary.pending} pending\n </span>\n <span>{summary.answered} answered</span>\n {(summary.cancelled > 0 || summary.expired > 0) && (\n <span>{summary.cancelled + summary.expired} closed</span>\n )}\n </div>\n );\n}\n\nfunction PromptHistory({ prompts, activeId }: { prompts: PromptSnapshot[]; activeId?: string | undefined }) {\n return (\n <ol className=\"divide-y divide-border overflow-hidden rounded-md border border-border bg-background text-xs\">\n {prompts.map((prompt) => (\n <li\n key={prompt.id}\n className={cn(\"flex min-w-0 items-start gap-2 px-2 py-1.5\", prompt.id === activeId && \"bg-muted/60\")}\n >\n <PromptStateIcon prompt={prompt} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <span className=\"truncate font-medium text-foreground\">{prompt.title}</span>\n {prompt.kind ? <span className=\"shrink-0 text-muted-foreground\">{prompt.kind}</span> : null}\n </div>\n <div className=\"mt-0.5 truncate text-muted-foreground\">{summarizePromptAnswer(prompt)}</div>\n </div>\n </li>\n ))}\n </ol>\n );\n}\n\nfunction PromptStateIcon({ prompt }: { prompt: PromptSnapshot }) {\n if (prompt.state === \"answered\") return <Icon icon={UiCheck} className=\"mt-0.5 size-3.5 shrink-0 text-emerald-600\" />;\n if (prompt.state === \"cancelled\" || prompt.state === \"expired\") {\n return <Icon icon={UiClose} className=\"mt-0.5 size-3.5 shrink-0 text-rose-600\" />;\n }\n if (prompt.kind === \"comment\") return <Icon icon={UiComment} className=\"mt-0.5 size-3.5 shrink-0\" />;\n return <Icon icon={UiHourglass} className=\"mt-0.5 size-3.5 shrink-0\" />;\n}\n"],"names":["usePrompts","useState","useMemo","analyzePromptInlineActions","summarizePrompts","useEffect","initialPromptComment","answerPrompt","buildPromptInlineValues","jsxs","cn","jsx","Icon","UiQuestion","UiRobotAi","Button","UiClose","UiCheck","PromptDialog","UiHourglass","summarizePromptAnswer","UiComment"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { PromptFilter } from '../hooks/use-prompts';
2
+ export interface PromptBannerProps extends PromptFilter {
3
+ /** Base path the prompt API is mounted under, e.g. "/api/todos". */
4
+ basePath?: string | undefined;
5
+ enabled?: boolean | undefined;
6
+ /** Override the headline; defaults to a count-aware message. */
7
+ title?: string | undefined;
8
+ className?: string | undefined;
9
+ /** Render approve/reject quick actions when the prompt schema supports them. */
10
+ inlineActions?: "auto" | false | undefined;
11
+ /** Show compact counts and recent prompt outcomes, not only the current pending prompt. */
12
+ showSummary?: boolean | undefined;
13
+ /** Maximum recent prompts shown in the summary list. */
14
+ historyLimit?: number | undefined;
15
+ }
16
+ export declare function PromptBanner({ basePath, enabled, title, className, inlineActions, showSummary, historyLimit, state: _ignoredState, ...filter }: PromptBannerProps): import("react/jsx-runtime").JSX.Element | null;
17
+ //# sourceMappingURL=PromptBanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PromptBanner.d.ts","sourceRoot":"","sources":["../../src/data/PromptBanner.tsx"],"names":[],"mappings":"AAKA,OAAO,EAA4B,KAAK,YAAY,EAAuB,MAAM,sBAAsB,CAAC;AAWxG,MAAM,WAAW,iBAAkB,SAAQ,YAAY;IACrD,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,gFAAgF;IAChF,aAAa,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;IAC3C,2FAA2F;IAC3F,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAClC,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC;AAMD,wBAAgB,YAAY,CAAC,EAC3B,QAAuB,EACvB,OAAO,EACP,KAAK,EACL,SAAS,EACT,aAAsB,EACtB,WAAkB,EAClB,YAAgB,EAChB,KAAK,EAAE,aAAa,EACpB,GAAG,MAAM,EACV,EAAE,iBAAiB,kDA8InB"}