@flanksource/clicky-ui 0.2.21 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/dist/components/Combobox.cjs +5 -2
  2. package/dist/components/Combobox.cjs.map +1 -1
  3. package/dist/components/Combobox.d.ts.map +1 -1
  4. package/dist/components/Combobox.js +5 -2
  5. package/dist/components/Combobox.js.map +1 -1
  6. package/dist/components/FilterBar.cjs +280 -253
  7. package/dist/components/FilterBar.cjs.map +1 -1
  8. package/dist/components/FilterBar.d.ts.map +1 -1
  9. package/dist/components/FilterBar.js +280 -253
  10. package/dist/components/FilterBar.js.map +1 -1
  11. package/dist/components/JsonSchemaForm.cjs +3 -1
  12. package/dist/components/JsonSchemaForm.cjs.map +1 -1
  13. package/dist/components/JsonSchemaForm.d.ts.map +1 -1
  14. package/dist/components/JsonSchemaForm.js +4 -2
  15. package/dist/components/JsonSchemaForm.js.map +1 -1
  16. package/dist/components/MultiSelect.cjs +29 -32
  17. package/dist/components/MultiSelect.cjs.map +1 -1
  18. package/dist/components/MultiSelect.d.ts.map +1 -1
  19. package/dist/components/MultiSelect.js +30 -33
  20. package/dist/components/MultiSelect.js.map +1 -1
  21. package/dist/components/TimeRange.cjs +29 -37
  22. package/dist/components/TimeRange.cjs.map +1 -1
  23. package/dist/components/TimeRange.d.ts.map +1 -1
  24. package/dist/components/TimeRange.js +30 -38
  25. package/dist/components/TimeRange.js.map +1 -1
  26. package/dist/components/TreePickerField.cjs +6 -3
  27. package/dist/components/TreePickerField.cjs.map +1 -1
  28. package/dist/components/TreePickerField.d.ts.map +1 -1
  29. package/dist/components/TreePickerField.js +6 -3
  30. package/dist/components/TreePickerField.js.map +1 -1
  31. package/dist/components/icon-menu-picker.cjs +9 -3
  32. package/dist/components/icon-menu-picker.cjs.map +1 -1
  33. package/dist/components/icon-menu-picker.d.ts.map +1 -1
  34. package/dist/components/icon-menu-picker.js +9 -3
  35. package/dist/components/icon-menu-picker.js.map +1 -1
  36. package/dist/components/json-schema-form-fields.cjs +80 -2
  37. package/dist/components/json-schema-form-fields.cjs.map +1 -1
  38. package/dist/components/json-schema-form-fields.d.ts +15 -0
  39. package/dist/components/json-schema-form-fields.d.ts.map +1 -1
  40. package/dist/components/json-schema-form-fields.js +81 -3
  41. package/dist/components/json-schema-form-fields.js.map +1 -1
  42. package/dist/components/json-schema-form-refs.cjs +60 -0
  43. package/dist/components/json-schema-form-refs.cjs.map +1 -0
  44. package/dist/components/json-schema-form-refs.d.ts +3 -0
  45. package/dist/components/json-schema-form-refs.d.ts.map +1 -0
  46. package/dist/components/json-schema-form-refs.js +60 -0
  47. package/dist/components/json-schema-form-refs.js.map +1 -0
  48. package/dist/components/json-schema-form-render.cjs +14 -2
  49. package/dist/components/json-schema-form-render.cjs.map +1 -1
  50. package/dist/components/json-schema-form-render.d.ts.map +1 -1
  51. package/dist/components/json-schema-form-render.js +15 -3
  52. package/dist/components/json-schema-form-render.js.map +1 -1
  53. package/dist/components/json-schema-form-resolve.cjs +5 -0
  54. package/dist/components/json-schema-form-resolve.cjs.map +1 -1
  55. package/dist/components/json-schema-form-resolve.d.ts.map +1 -1
  56. package/dist/components/json-schema-form-resolve.js +5 -0
  57. package/dist/components/json-schema-form-resolve.js.map +1 -1
  58. package/dist/components/json-schema-form-template-menu.cjs +83 -0
  59. package/dist/components/json-schema-form-template-menu.cjs.map +1 -0
  60. package/dist/components/json-schema-form-template-menu.d.ts +15 -0
  61. package/dist/components/json-schema-form-template-menu.d.ts.map +1 -0
  62. package/dist/components/json-schema-form-template-menu.js +83 -0
  63. package/dist/components/json-schema-form-template-menu.js.map +1 -0
  64. package/dist/components/json-schema-form-template.cjs +18 -0
  65. package/dist/components/json-schema-form-template.cjs.map +1 -0
  66. package/dist/components/json-schema-form-template.d.ts +13 -0
  67. package/dist/components/json-schema-form-template.d.ts.map +1 -0
  68. package/dist/components/json-schema-form-template.js +18 -0
  69. package/dist/components/json-schema-form-template.js.map +1 -0
  70. package/dist/components/json-schema-form-types.d.ts +5 -1
  71. package/dist/components/json-schema-form-types.d.ts.map +1 -1
  72. package/dist/components.cjs +4 -0
  73. package/dist/components.cjs.map +1 -1
  74. package/dist/components.d.ts +3 -0
  75. package/dist/components.d.ts.map +1 -1
  76. package/dist/components.js +4 -0
  77. package/dist/components.js.map +1 -1
  78. package/dist/data/Clicky.cjs +6 -9
  79. package/dist/data/Clicky.cjs.map +1 -1
  80. package/dist/data/Clicky.d.ts.map +1 -1
  81. package/dist/data/Clicky.js +6 -9
  82. package/dist/data/Clicky.js.map +1 -1
  83. package/dist/data/DataTable.cjs +23 -17
  84. package/dist/data/DataTable.cjs.map +1 -1
  85. package/dist/data/DataTable.d.ts +7 -1
  86. package/dist/data/DataTable.d.ts.map +1 -1
  87. package/dist/data/DataTable.js +24 -18
  88. package/dist/data/DataTable.js.map +1 -1
  89. package/dist/data/Gauge.cjs +37 -4
  90. package/dist/data/Gauge.cjs.map +1 -1
  91. package/dist/data/Gauge.d.ts +6 -1
  92. package/dist/data/Gauge.d.ts.map +1 -1
  93. package/dist/data/Gauge.js +37 -4
  94. package/dist/data/Gauge.js.map +1 -1
  95. package/dist/data/TimeseriesCoreBars.cjs +127 -29
  96. package/dist/data/TimeseriesCoreBars.cjs.map +1 -1
  97. package/dist/data/TimeseriesCoreBars.d.ts +11 -1
  98. package/dist/data/TimeseriesCoreBars.d.ts.map +1 -1
  99. package/dist/data/TimeseriesCoreBars.js +128 -30
  100. package/dist/data/TimeseriesCoreBars.js.map +1 -1
  101. package/dist/data/TimeseriesGauge.cjs +105 -47
  102. package/dist/data/TimeseriesGauge.cjs.map +1 -1
  103. package/dist/data/TimeseriesGauge.d.ts +6 -1
  104. package/dist/data/TimeseriesGauge.d.ts.map +1 -1
  105. package/dist/data/TimeseriesGauge.js +105 -47
  106. package/dist/data/TimeseriesGauge.js.map +1 -1
  107. package/dist/data/WorkloadCard.cjs +371 -0
  108. package/dist/data/WorkloadCard.cjs.map +1 -0
  109. package/dist/data/WorkloadCard.d.ts +24 -0
  110. package/dist/data/WorkloadCard.d.ts.map +1 -0
  111. package/dist/data/WorkloadCard.js +371 -0
  112. package/dist/data/WorkloadCard.js.map +1 -0
  113. package/dist/data/WorkloadCard.model.cjs +51 -0
  114. package/dist/data/WorkloadCard.model.cjs.map +1 -0
  115. package/dist/data/WorkloadCard.model.d.ts +53 -0
  116. package/dist/data/WorkloadCard.model.d.ts.map +1 -0
  117. package/dist/data/WorkloadCard.model.js +51 -0
  118. package/dist/data/WorkloadCard.model.js.map +1 -0
  119. package/dist/data/ai/ChatFab.cjs +3 -1
  120. package/dist/data/ai/ChatFab.cjs.map +1 -1
  121. package/dist/data/ai/ChatFab.d.ts.map +1 -1
  122. package/dist/data/ai/ChatFab.js +3 -1
  123. package/dist/data/ai/ChatFab.js.map +1 -1
  124. package/dist/data/ai/ChatWindow.cjs +25 -7
  125. package/dist/data/ai/ChatWindow.cjs.map +1 -1
  126. package/dist/data/ai/ChatWindow.d.ts +11 -3
  127. package/dist/data/ai/ChatWindow.d.ts.map +1 -1
  128. package/dist/data/ai/ChatWindow.js +26 -8
  129. package/dist/data/ai/ChatWindow.js.map +1 -1
  130. package/dist/data/ai/chat-window-context.cjs +2 -1
  131. package/dist/data/ai/chat-window-context.cjs.map +1 -1
  132. package/dist/data/ai/chat-window-context.d.ts +1 -1
  133. package/dist/data/ai/chat-window-context.d.ts.map +1 -1
  134. package/dist/data/ai/chat-window-context.js +2 -1
  135. package/dist/data/ai/chat-window-context.js.map +1 -1
  136. package/dist/data/ai/context.cjs.map +1 -1
  137. package/dist/data/ai/context.d.ts +3 -0
  138. package/dist/data/ai/context.d.ts.map +1 -1
  139. package/dist/data/ai/context.js.map +1 -1
  140. package/dist/data/chat/Chat.cjs +9 -1
  141. package/dist/data/chat/Chat.cjs.map +1 -1
  142. package/dist/data/chat/Chat.d.ts +4 -2
  143. package/dist/data/chat/Chat.d.ts.map +1 -1
  144. package/dist/data/chat/Chat.js +10 -2
  145. package/dist/data/chat/Chat.js.map +1 -1
  146. package/dist/data/chat/Message.cjs +13 -4
  147. package/dist/data/chat/Message.cjs.map +1 -1
  148. package/dist/data/chat/Message.d.ts +4 -2
  149. package/dist/data/chat/Message.d.ts.map +1 -1
  150. package/dist/data/chat/Message.js +13 -4
  151. package/dist/data/chat/Message.js.map +1 -1
  152. package/dist/data/chat/ToolCall.cjs +9 -4
  153. package/dist/data/chat/ToolCall.cjs.map +1 -1
  154. package/dist/data/chat/ToolCall.d.ts +3 -2
  155. package/dist/data/chat/ToolCall.d.ts.map +1 -1
  156. package/dist/data/chat/ToolCall.js +9 -4
  157. package/dist/data/chat/ToolCall.js.map +1 -1
  158. package/dist/data/chat/index.d.ts +1 -1
  159. package/dist/data/chat/index.d.ts.map +1 -1
  160. package/dist/data/chat/types.cjs.map +1 -1
  161. package/dist/data/chat/types.d.ts +7 -0
  162. package/dist/data/chat/types.d.ts.map +1 -1
  163. package/dist/data/chat/types.js.map +1 -1
  164. package/dist/data/version-info.cjs +2 -2
  165. package/dist/data/version-info.js +2 -2
  166. package/dist/data.cjs +7 -0
  167. package/dist/data.cjs.map +1 -1
  168. package/dist/data.d.ts +5 -3
  169. package/dist/data.d.ts.map +1 -1
  170. package/dist/data.js +8 -1
  171. package/dist/data.js.map +1 -1
  172. package/dist/hooks/use-sort.cjs +19 -10
  173. package/dist/hooks/use-sort.cjs.map +1 -1
  174. package/dist/hooks/use-sort.d.ts +3 -0
  175. package/dist/hooks/use-sort.d.ts.map +1 -1
  176. package/dist/hooks/use-sort.js +19 -10
  177. package/dist/hooks/use-sort.js.map +1 -1
  178. package/dist/index.cjs +11 -0
  179. package/dist/index.cjs.map +1 -1
  180. package/dist/index.js +11 -0
  181. package/dist/index.js.map +1 -1
  182. package/dist/layout/AppShell.cjs +6 -10
  183. package/dist/layout/AppShell.cjs.map +1 -1
  184. package/dist/layout/AppShell.d.ts +2 -0
  185. package/dist/layout/AppShell.d.ts.map +1 -1
  186. package/dist/layout/AppShell.js +6 -10
  187. package/dist/layout/AppShell.js.map +1 -1
  188. package/dist/overlay/DropdownMenu.cjs +50 -38
  189. package/dist/overlay/DropdownMenu.cjs.map +1 -1
  190. package/dist/overlay/DropdownMenu.d.ts +5 -1
  191. package/dist/overlay/DropdownMenu.d.ts.map +1 -1
  192. package/dist/overlay/DropdownMenu.js +50 -38
  193. package/dist/overlay/DropdownMenu.js.map +1 -1
  194. package/dist/overlay/HoverCard.cjs +7 -2
  195. package/dist/overlay/HoverCard.cjs.map +1 -1
  196. package/dist/overlay/HoverCard.d.ts.map +1 -1
  197. package/dist/overlay/HoverCard.js +7 -2
  198. package/dist/overlay/HoverCard.js.map +1 -1
  199. package/dist/overlay/Modal.cjs +20 -17
  200. package/dist/overlay/Modal.cjs.map +1 -1
  201. package/dist/overlay/Modal.d.ts.map +1 -1
  202. package/dist/overlay/Modal.js +21 -18
  203. package/dist/overlay/Modal.js.map +1 -1
  204. package/dist/overlay/Toast.cjs +9 -1
  205. package/dist/overlay/Toast.cjs.map +1 -1
  206. package/dist/overlay/Toast.d.ts.map +1 -1
  207. package/dist/overlay/Toast.js +9 -1
  208. package/dist/overlay/Toast.js.map +1 -1
  209. package/dist/overlay/modalStack.cjs +53 -1
  210. package/dist/overlay/modalStack.cjs.map +1 -1
  211. package/dist/overlay/modalStack.d.ts +12 -0
  212. package/dist/overlay/modalStack.d.ts.map +1 -1
  213. package/dist/overlay/modalStack.js +54 -2
  214. package/dist/overlay/modalStack.js.map +1 -1
  215. package/dist/overlay/zIndex.cjs +20 -0
  216. package/dist/overlay/zIndex.cjs.map +1 -0
  217. package/dist/overlay/zIndex.d.ts +39 -0
  218. package/dist/overlay/zIndex.d.ts.map +1 -0
  219. package/dist/overlay/zIndex.js +20 -0
  220. package/dist/overlay/zIndex.js.map +1 -0
  221. package/dist/rpc/AcceptPicker.cjs +28 -26
  222. package/dist/rpc/AcceptPicker.cjs.map +1 -1
  223. package/dist/rpc/AcceptPicker.d.ts.map +1 -1
  224. package/dist/rpc/AcceptPicker.js +29 -27
  225. package/dist/rpc/AcceptPicker.js.map +1 -1
  226. package/dist/styles.css +1 -1
  227. package/package.json +1 -1
@@ -44,6 +44,8 @@ function TimeseriesGauge({
44
44
  expandable = true,
45
45
  thresholds = [75, 90],
46
46
  centerDisplay = "value",
47
+ variant = "default",
48
+ showLabel = true,
47
49
  fetcher = defaultFetcher,
48
50
  className
49
51
  }) {
@@ -83,60 +85,116 @@ function TimeseriesGauge({
83
85
  const bounded = hasValue && limit !== void 0 && limit > 0;
84
86
  const pct = bounded ? Math.min(100, Math.round(usage / limit * 100)) : 0;
85
87
  const tone = toneClass(pct, thresholds);
88
+ const readout = centerDisplay === "percent" ? bounded ? `${pct}%` : hasValue ? "n/a" : "—" : hasValue ? formatUnit(usage, unit) : "—";
89
+ const titleText = `${title}: ${readout}`;
90
+ const canExpand = expandable && variant === "default";
91
+ const readoutTone = bounded ? tone : "text-foreground";
86
92
  const chartSeries = useMemo(() => {
87
93
  const s = [{ id: value.id, label: "value", ...value.transform ? { transform: value.transform } : {} }];
88
94
  if (maxSeries) s.push({ id: maxSeries.id, label: "max", ...maxSeries.transform ? { transform: maxSeries.transform } : {} });
89
95
  return s;
90
96
  }, [value.id, value.transform, maxSeries]);
91
- return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col items-center gap-1", className), children: [
92
- /* @__PURE__ */ jsxs("div", { className: "relative h-10 w-20", children: [
93
- /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 100 50", className: "h-full w-full overflow-visible", children: [
94
- /* @__PURE__ */ jsx("path", { d: GAUGE_PATH, fill: "none", stroke: "currentColor", strokeWidth: 9, strokeLinecap: "round", className: "text-muted" }),
95
- /* @__PURE__ */ jsx(
96
- "path",
97
- {
98
- d: GAUGE_PATH,
99
- fill: "none",
100
- stroke: "currentColor",
101
- strokeWidth: 9,
102
- strokeLinecap: "round",
103
- className: tone,
104
- strokeDasharray: GAUGE_ARC,
105
- strokeDashoffset: GAUGE_ARC * (1 - pct / 100)
106
- }
107
- )
108
- ] }),
109
- /* @__PURE__ */ jsx("span", { className: "absolute inset-x-0 bottom-0 text-center text-sm font-semibold text-foreground", children: centerDisplay === "percent" ? bounded ? `${pct}%` : hasValue ? "n/a" : "—" : hasValue ? formatUnit(usage, unit) : "—" }),
110
- expandable && /* @__PURE__ */ jsx(
111
- "button",
112
- {
113
- type: "button",
114
- "aria-label": "Expand chart",
115
- onClick: () => setExpanded(true),
116
- className: "absolute right-0 top-0 text-muted-foreground hover:text-foreground",
117
- children: /* @__PURE__ */ jsx(Icon, { icon: UiFullscreen, width: 12, height: 12 })
118
- }
119
- )
120
- ] }),
121
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
122
- icon ? /* @__PURE__ */ jsx(GaugeIcon, { icon }) : null,
123
- /* @__PURE__ */ jsx("span", { children: title })
124
- ] }),
125
- expandable && /* @__PURE__ */ jsx(Modal, { open: expanded, onClose: () => setExpanded(false), title, size: "xl", children: /* @__PURE__ */ jsx(
126
- TimeseriesPanel,
97
+ if (variant === "cell") {
98
+ return /* @__PURE__ */ jsxs(
99
+ "span",
127
100
  {
128
- title,
129
- ...icon ? { icon } : {},
130
- ...unit ? { unit } : {},
131
- baseUrl,
132
- series: chartSeries,
133
- range,
134
- refreshMs,
135
- expandable: false,
136
- fetcher
101
+ className: cn(
102
+ "inline-flex min-w-0 items-center gap-1.5 whitespace-nowrap align-middle text-xs",
103
+ className
104
+ ),
105
+ title: titleText,
106
+ "aria-label": !showLabel ? titleText : void 0,
107
+ children: [
108
+ icon ? /* @__PURE__ */ jsx(GaugeIcon, { icon }) : null,
109
+ showLabel ? /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate text-muted-foreground", children: title }) : null,
110
+ /* @__PURE__ */ jsx("span", { className: "relative h-4 w-8 shrink-0", "aria-hidden": "true", children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 100 50", className: "h-full w-full overflow-visible", children: [
111
+ /* @__PURE__ */ jsx(
112
+ "path",
113
+ {
114
+ d: GAUGE_PATH,
115
+ fill: "none",
116
+ stroke: "currentColor",
117
+ strokeWidth: 10,
118
+ strokeLinecap: "round",
119
+ className: "text-muted"
120
+ }
121
+ ),
122
+ /* @__PURE__ */ jsx(
123
+ "path",
124
+ {
125
+ d: GAUGE_PATH,
126
+ fill: "none",
127
+ stroke: "currentColor",
128
+ strokeWidth: 10,
129
+ strokeLinecap: "round",
130
+ className: tone,
131
+ strokeDasharray: GAUGE_ARC,
132
+ strokeDashoffset: GAUGE_ARC * (1 - pct / 100)
133
+ }
134
+ )
135
+ ] }) }),
136
+ /* @__PURE__ */ jsx("span", { className: cn("shrink-0 font-semibold tabular-nums", readoutTone), children: readout })
137
+ ]
137
138
  }
138
- ) })
139
- ] });
139
+ );
140
+ }
141
+ return /* @__PURE__ */ jsxs(
142
+ "div",
143
+ {
144
+ className: cn("flex flex-col items-center gap-1", className),
145
+ title: !showLabel ? titleText : void 0,
146
+ "aria-label": !showLabel ? titleText : void 0,
147
+ children: [
148
+ /* @__PURE__ */ jsxs("div", { className: "relative h-10 w-20", children: [
149
+ /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 100 50", className: "h-full w-full overflow-visible", children: [
150
+ /* @__PURE__ */ jsx("path", { d: GAUGE_PATH, fill: "none", stroke: "currentColor", strokeWidth: 9, strokeLinecap: "round", className: "text-muted" }),
151
+ /* @__PURE__ */ jsx(
152
+ "path",
153
+ {
154
+ d: GAUGE_PATH,
155
+ fill: "none",
156
+ stroke: "currentColor",
157
+ strokeWidth: 9,
158
+ strokeLinecap: "round",
159
+ className: tone,
160
+ strokeDasharray: GAUGE_ARC,
161
+ strokeDashoffset: GAUGE_ARC * (1 - pct / 100)
162
+ }
163
+ )
164
+ ] }),
165
+ /* @__PURE__ */ jsx("span", { className: "absolute inset-x-0 bottom-0 text-center text-sm font-semibold text-foreground", children: readout }),
166
+ canExpand && /* @__PURE__ */ jsx(
167
+ "button",
168
+ {
169
+ type: "button",
170
+ "aria-label": "Expand chart",
171
+ onClick: () => setExpanded(true),
172
+ className: "absolute right-0 top-0 text-muted-foreground hover:text-foreground",
173
+ children: /* @__PURE__ */ jsx(Icon, { icon: UiFullscreen, width: 12, height: 12 })
174
+ }
175
+ )
176
+ ] }),
177
+ icon || showLabel ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
178
+ icon ? /* @__PURE__ */ jsx(GaugeIcon, { icon }) : null,
179
+ showLabel ? /* @__PURE__ */ jsx("span", { children: title }) : null
180
+ ] }) : null,
181
+ canExpand && /* @__PURE__ */ jsx(Modal, { open: expanded, onClose: () => setExpanded(false), title, size: "xl", children: /* @__PURE__ */ jsx(
182
+ TimeseriesPanel,
183
+ {
184
+ title,
185
+ ...icon ? { icon } : {},
186
+ ...unit ? { unit } : {},
187
+ baseUrl,
188
+ series: chartSeries,
189
+ range,
190
+ refreshMs,
191
+ expandable: false,
192
+ fetcher
193
+ }
194
+ ) })
195
+ ]
196
+ }
197
+ );
140
198
  }
141
199
  export {
142
200
  TimeseriesGauge
@@ -1 +1 @@
1
- {"version":3,"file":"TimeseriesGauge.js","sources":["../../src/data/TimeseriesGauge.tsx"],"sourcesContent":["import { useQueries } from \"@tanstack/react-query\";\nimport { useMemo, useState } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { formatUnit } from \"../lib/format\";\nimport { Modal } from \"../overlay/Modal\";\nimport { UiFullscreen } from \"../icons\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\nimport {\n TimeseriesPanel,\n type TimeseriesResponse,\n type TimeseriesSeries,\n} from \"./TimeseriesPanel\";\n\n/** A metric series whose latest value drives the gauge fill or its maximum. */\nexport interface GaugeSeries {\n /** Metric id appended to the gauge's baseUrl, e.g. \"k8s.statefulset.cycle.cpu.usage\". */\n id: string;\n /** Maps the latest value before display (e.g. unit scaling). Identity when omitted. */\n transform?: (value: number) => number;\n}\n\nexport interface TimeseriesGaugeProps {\n /** Common prefix; the value/max requests are `baseUrl + id`. */\n baseUrl?: string;\n /** The metric whose latest value fills the gauge. */\n value: GaugeSeries;\n /**\n * The gauge maximum. Either a metric series (its latest value, e.g. a `.limit`\n * metric) or a fixed number. When omitted, or when the resolved max is 0, the\n * gauge shows the raw value with no fill (an unbounded reading).\n */\n max?: GaugeSeries | number;\n title: string;\n /** Iconify name or static icon component shown beside the label. */\n icon?: string | StaticIconComponent;\n /** Grafana-style unit key for the readout (e.g. \"bytes\", \"percent\", \"short\", \"ms\"). */\n unit?: string;\n /** Look-back window for the expand chart, passed as ?since=; defaults to \"1h\". */\n range?: string;\n /** Poll interval in ms; defaults to 5000. Pass 0 to disable polling. */\n refreshMs?: number;\n /** Show an expand button that opens the value/max time-series chart in a modal. Default true. */\n expandable?: boolean;\n /**\n * Utilisation thresholds (percent of max) at which the arc turns amber then\n * red. Defaults to [75, 90].\n */\n thresholds?: [warning: number, danger: number];\n /**\n * What to print in the centre: the formatted value (default) or the\n * utilisation percentage of max (e.g. CPU usage out of its millicore limit).\n */\n centerDisplay?: \"value\" | \"percent\";\n /** Override the default fetch (e.g. to route through an app's API client). */\n fetcher?: (url: string) => Promise<TimeseriesResponse>;\n className?: string;\n}\n\nconst defaultFetcher = async (url: string): Promise<TimeseriesResponse> => {\n const res = await fetch(url);\n if (!res.ok) throw new Error(`metrics request failed: ${res.status}`);\n return res.json();\n};\n\n// Half-gauge SVG geometry: a 100×50 viewBox semicircle (radius 40, centre 50,50)\n// swept 180°. The arc length is π·r; stroke-dashoffset reveals value/max of it.\nconst GAUGE_RADIUS = 40;\nconst GAUGE_ARC = Math.PI * GAUGE_RADIUS;\nconst GAUGE_PATH = `M 10 50 A ${GAUGE_RADIUS} ${GAUGE_RADIUS} 0 0 1 90 50`;\n\nfunction toneClass(pct: number, [warn, danger]: [number, number]): string {\n if (pct >= danger) return \"text-red-500\";\n if (pct >= warn) return \"text-amber-500\";\n return \"text-emerald-500\";\n}\n\nfunction latestValue(resp: TimeseriesResponse | undefined): number | undefined {\n const points = resp?.points;\n if (!points || points.length === 0) return undefined;\n return points[points.length - 1]?.value;\n}\n\nfunction GaugeIcon({ icon }: { icon: string | StaticIconComponent }) {\n if (typeof icon === \"string\") {\n return <Icon name={icon} width={14} height={14} className=\"text-muted-foreground\" />;\n }\n return <Icon icon={icon} width={14} height={14} className=\"text-muted-foreground\" />;\n}\n\n/**\n * TimeseriesGauge renders a half (180°) radial gauge whose fill is the latest\n * value of a metric over its maximum (a `.limit`-style metric or a fixed number),\n * both read live from the timeseries store. The centre shows the utilisation\n * percentage, with the value/max caption below; the arc colour crosses warning\n * and danger thresholds. An expand button opens the full value/max time-series\n * chart in a modal (a `TimeseriesPanel`), so the gauge gives the at-a-glance\n * reading and the chart the trend.\n */\nexport function TimeseriesGauge({\n baseUrl = \"\",\n value,\n max,\n title,\n icon,\n unit,\n range = \"1h\",\n refreshMs = 5000,\n expandable = true,\n thresholds = [75, 90],\n centerDisplay = \"value\",\n fetcher = defaultFetcher,\n className,\n}: TimeseriesGaugeProps) {\n const [expanded, setExpanded] = useState(false);\n\n const maxIsSeries = typeof max === \"object\";\n const maxSeries = maxIsSeries ? max : undefined;\n const ids = useMemo(() => {\n const list = [value.id];\n if (maxSeries) list.push(maxSeries.id);\n return list;\n }, [value.id, maxSeries]);\n\n const results = useQueries({\n queries: ids.map((id) => {\n const u = new URL(baseUrl + id, window.location.origin);\n if (range) u.searchParams.set(\"since\", range);\n const requestUrl = u.pathname + u.search;\n return {\n queryKey: [\"timeseries\", requestUrl],\n queryFn: () => fetcher(requestUrl),\n refetchInterval: refreshMs > 0 ? refreshMs : false,\n staleTime: 0,\n retry: 0,\n };\n }),\n });\n\n const rawValue = latestValue(results[0]?.data);\n const hasValue = rawValue !== undefined;\n const usage = value.transform ? value.transform(rawValue ?? 0) : (rawValue ?? 0);\n\n let limit: number | undefined;\n if (typeof max === \"number\") {\n limit = max;\n } else if (maxSeries) {\n const rawMax = latestValue(results[1]?.data);\n limit = rawMax === undefined ? undefined : maxSeries.transform ? maxSeries.transform(rawMax) : rawMax;\n }\n\n const bounded = hasValue && limit !== undefined && limit > 0;\n const pct = bounded ? Math.min(100, Math.round((usage / (limit as number)) * 100)) : 0;\n const tone = toneClass(pct, thresholds);\n\n const chartSeries: TimeseriesSeries[] = useMemo(() => {\n const s: TimeseriesSeries[] = [{ id: value.id, label: \"value\", ...(value.transform ? { transform: value.transform } : {}) }];\n if (maxSeries) s.push({ id: maxSeries.id, label: \"max\", ...(maxSeries.transform ? { transform: maxSeries.transform } : {}) });\n return s;\n }, [value.id, value.transform, maxSeries]);\n\n return (\n <div className={cn(\"flex flex-col items-center gap-1\", className)}>\n <div className=\"relative h-10 w-20\">\n <svg viewBox=\"0 0 100 50\" className=\"h-full w-full overflow-visible\">\n <path d={GAUGE_PATH} fill=\"none\" stroke=\"currentColor\" strokeWidth={9} strokeLinecap=\"round\" className=\"text-muted\" />\n <path\n d={GAUGE_PATH}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={9}\n strokeLinecap=\"round\"\n className={tone}\n strokeDasharray={GAUGE_ARC}\n strokeDashoffset={GAUGE_ARC * (1 - pct / 100)}\n />\n </svg>\n <span className=\"absolute inset-x-0 bottom-0 text-center text-sm font-semibold text-foreground\">\n {centerDisplay === \"percent\"\n ? bounded\n ? `${pct}%`\n : hasValue\n ? \"n/a\"\n : \"—\"\n : hasValue\n ? formatUnit(usage, unit)\n : \"—\"}\n </span>\n {expandable && (\n <button\n type=\"button\"\n aria-label=\"Expand chart\"\n onClick={() => setExpanded(true)}\n className=\"absolute right-0 top-0 text-muted-foreground hover:text-foreground\"\n >\n <Icon icon={UiFullscreen} width={12} height={12} />\n </button>\n )}\n </div>\n <div className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n {icon ? <GaugeIcon icon={icon} /> : null}\n <span>{title}</span>\n </div>\n\n {expandable && (\n <Modal open={expanded} onClose={() => setExpanded(false)} title={title} size=\"xl\">\n <TimeseriesPanel\n title={title}\n {...(icon ? { icon } : {})}\n {...(unit ? { unit } : {})}\n baseUrl={baseUrl}\n series={chartSeries}\n range={range}\n refreshMs={refreshMs}\n expandable={false}\n fetcher={fetcher}\n />\n </Modal>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TimeseriesGauge.js","sources":["../../src/data/TimeseriesGauge.tsx"],"sourcesContent":["import { useQueries } from \"@tanstack/react-query\";\nimport { useMemo, useState } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { formatUnit } from \"../lib/format\";\nimport { Modal } from \"../overlay/Modal\";\nimport { UiFullscreen } from \"../icons\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\nimport {\n TimeseriesPanel,\n type TimeseriesResponse,\n type TimeseriesSeries,\n} from \"./TimeseriesPanel\";\n\n/** A metric series whose latest value drives the gauge fill or its maximum. */\nexport interface GaugeSeries {\n /** Metric id appended to the gauge's baseUrl, e.g. \"k8s.statefulset.cycle.cpu.usage\". */\n id: string;\n /** Maps the latest value before display (e.g. unit scaling). Identity when omitted. */\n transform?: (value: number) => number;\n}\n\nexport type TimeseriesGaugeVariant = \"default\" | \"cell\";\n\nexport interface TimeseriesGaugeProps {\n /** Common prefix; the value/max requests are `baseUrl + id`. */\n baseUrl?: string;\n /** The metric whose latest value fills the gauge. */\n value: GaugeSeries;\n /**\n * The gauge maximum. Either a metric series (its latest value, e.g. a `.limit`\n * metric) or a fixed number. When omitted, or when the resolved max is 0, the\n * gauge shows the raw value with no fill (an unbounded reading).\n */\n max?: GaugeSeries | number;\n title: string;\n /** Iconify name or static icon component shown beside the label. */\n icon?: string | StaticIconComponent;\n /** Grafana-style unit key for the readout (e.g. \"bytes\", \"percent\", \"short\", \"ms\"). */\n unit?: string;\n /** Look-back window for the expand chart, passed as ?since=; defaults to \"1h\". */\n range?: string;\n /** Poll interval in ms; defaults to 5000. Pass 0 to disable polling. */\n refreshMs?: number;\n /** Show an expand button that opens the value/max time-series chart in a modal. Default true. */\n expandable?: boolean;\n /**\n * Utilisation thresholds (percent of max) at which the arc turns amber then\n * red. Defaults to [75, 90].\n */\n thresholds?: [warning: number, danger: number];\n /**\n * What to print in the centre: the formatted value (default) or the\n * utilisation percentage of max (e.g. CPU usage out of its millicore limit).\n */\n centerDisplay?: \"value\" | \"percent\";\n /** Visual density/layout. `cell` is a compact inline form for table/grid cells. */\n variant?: TimeseriesGaugeVariant;\n /** Show the title text. Icons and values remain visible when false. */\n showLabel?: boolean;\n /** Override the default fetch (e.g. to route through an app's API client). */\n fetcher?: (url: string) => Promise<TimeseriesResponse>;\n className?: string;\n}\n\nconst defaultFetcher = async (url: string): Promise<TimeseriesResponse> => {\n const res = await fetch(url);\n if (!res.ok) throw new Error(`metrics request failed: ${res.status}`);\n return res.json();\n};\n\n// Half-gauge SVG geometry: a 100×50 viewBox semicircle (radius 40, centre 50,50)\n// swept 180°. The arc length is π·r; stroke-dashoffset reveals value/max of it.\nconst GAUGE_RADIUS = 40;\nconst GAUGE_ARC = Math.PI * GAUGE_RADIUS;\nconst GAUGE_PATH = `M 10 50 A ${GAUGE_RADIUS} ${GAUGE_RADIUS} 0 0 1 90 50`;\n\nfunction toneClass(pct: number, [warn, danger]: [number, number]): string {\n if (pct >= danger) return \"text-red-500\";\n if (pct >= warn) return \"text-amber-500\";\n return \"text-emerald-500\";\n}\n\nfunction latestValue(resp: TimeseriesResponse | undefined): number | undefined {\n const points = resp?.points;\n if (!points || points.length === 0) return undefined;\n return points[points.length - 1]?.value;\n}\n\nfunction GaugeIcon({ icon }: { icon: string | StaticIconComponent }) {\n if (typeof icon === \"string\") {\n return <Icon name={icon} width={14} height={14} className=\"text-muted-foreground\" />;\n }\n return <Icon icon={icon} width={14} height={14} className=\"text-muted-foreground\" />;\n}\n\n/**\n * TimeseriesGauge renders a half (180°) radial gauge whose fill is the latest\n * value of a metric over its maximum (a `.limit`-style metric or a fixed number),\n * both read live from the timeseries store. The centre shows the utilisation\n * percentage, with the value/max caption below; the arc colour crosses warning\n * and danger thresholds. An expand button opens the full value/max time-series\n * chart in a modal (a `TimeseriesPanel`), so the gauge gives the at-a-glance\n * reading and the chart the trend.\n */\nexport function TimeseriesGauge({\n baseUrl = \"\",\n value,\n max,\n title,\n icon,\n unit,\n range = \"1h\",\n refreshMs = 5000,\n expandable = true,\n thresholds = [75, 90],\n centerDisplay = \"value\",\n variant = \"default\",\n showLabel = true,\n fetcher = defaultFetcher,\n className,\n}: TimeseriesGaugeProps) {\n const [expanded, setExpanded] = useState(false);\n\n const maxIsSeries = typeof max === \"object\";\n const maxSeries = maxIsSeries ? max : undefined;\n const ids = useMemo(() => {\n const list = [value.id];\n if (maxSeries) list.push(maxSeries.id);\n return list;\n }, [value.id, maxSeries]);\n\n const results = useQueries({\n queries: ids.map((id) => {\n const u = new URL(baseUrl + id, window.location.origin);\n if (range) u.searchParams.set(\"since\", range);\n const requestUrl = u.pathname + u.search;\n return {\n queryKey: [\"timeseries\", requestUrl],\n queryFn: () => fetcher(requestUrl),\n refetchInterval: refreshMs > 0 ? refreshMs : false,\n staleTime: 0,\n retry: 0,\n };\n }),\n });\n\n const rawValue = latestValue(results[0]?.data);\n const hasValue = rawValue !== undefined;\n const usage = value.transform ? value.transform(rawValue ?? 0) : (rawValue ?? 0);\n\n let limit: number | undefined;\n if (typeof max === \"number\") {\n limit = max;\n } else if (maxSeries) {\n const rawMax = latestValue(results[1]?.data);\n limit = rawMax === undefined ? undefined : maxSeries.transform ? maxSeries.transform(rawMax) : rawMax;\n }\n\n const bounded = hasValue && limit !== undefined && limit > 0;\n const pct = bounded ? Math.min(100, Math.round((usage / (limit as number)) * 100)) : 0;\n const tone = toneClass(pct, thresholds);\n const readout =\n centerDisplay === \"percent\"\n ? bounded\n ? `${pct}%`\n : hasValue\n ? \"n/a\"\n : \"—\"\n : hasValue\n ? formatUnit(usage, unit)\n : \"—\";\n const titleText = `${title}: ${readout}`;\n const canExpand = expandable && variant === \"default\";\n const readoutTone = bounded ? tone : \"text-foreground\";\n\n const chartSeries: TimeseriesSeries[] = useMemo(() => {\n const s: TimeseriesSeries[] = [{ id: value.id, label: \"value\", ...(value.transform ? { transform: value.transform } : {}) }];\n if (maxSeries) s.push({ id: maxSeries.id, label: \"max\", ...(maxSeries.transform ? { transform: maxSeries.transform } : {}) });\n return s;\n }, [value.id, value.transform, maxSeries]);\n\n if (variant === \"cell\") {\n return (\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 title={titleText}\n aria-label={!showLabel ? titleText : undefined}\n >\n {icon ? <GaugeIcon icon={icon} /> : null}\n {showLabel ? <span className=\"min-w-0 truncate text-muted-foreground\">{title}</span> : null}\n <span className=\"relative h-4 w-8 shrink-0\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 100 50\" className=\"h-full w-full overflow-visible\">\n <path\n d={GAUGE_PATH}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={10}\n strokeLinecap=\"round\"\n className=\"text-muted\"\n />\n <path\n d={GAUGE_PATH}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={10}\n strokeLinecap=\"round\"\n className={tone}\n strokeDasharray={GAUGE_ARC}\n strokeDashoffset={GAUGE_ARC * (1 - pct / 100)}\n />\n </svg>\n </span>\n <span className={cn(\"shrink-0 font-semibold tabular-nums\", readoutTone)}>\n {readout}\n </span>\n </span>\n );\n }\n\n return (\n <div\n className={cn(\"flex flex-col items-center gap-1\", className)}\n title={!showLabel ? titleText : undefined}\n aria-label={!showLabel ? titleText : undefined}\n >\n <div className=\"relative h-10 w-20\">\n <svg viewBox=\"0 0 100 50\" className=\"h-full w-full overflow-visible\">\n <path d={GAUGE_PATH} fill=\"none\" stroke=\"currentColor\" strokeWidth={9} strokeLinecap=\"round\" className=\"text-muted\" />\n <path\n d={GAUGE_PATH}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={9}\n strokeLinecap=\"round\"\n className={tone}\n strokeDasharray={GAUGE_ARC}\n strokeDashoffset={GAUGE_ARC * (1 - pct / 100)}\n />\n </svg>\n <span className=\"absolute inset-x-0 bottom-0 text-center text-sm font-semibold text-foreground\">\n {readout}\n </span>\n {canExpand && (\n <button\n type=\"button\"\n aria-label=\"Expand chart\"\n onClick={() => setExpanded(true)}\n className=\"absolute right-0 top-0 text-muted-foreground hover:text-foreground\"\n >\n <Icon icon={UiFullscreen} width={12} height={12} />\n </button>\n )}\n </div>\n {icon || showLabel ? (\n <div className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n {icon ? <GaugeIcon icon={icon} /> : null}\n {showLabel ? <span>{title}</span> : null}\n </div>\n ) : null}\n\n {canExpand && (\n <Modal open={expanded} onClose={() => setExpanded(false)} title={title} size=\"xl\">\n <TimeseriesPanel\n title={title}\n {...(icon ? { icon } : {})}\n {...(unit ? { unit } : {})}\n baseUrl={baseUrl}\n series={chartSeries}\n range={range}\n refreshMs={refreshMs}\n expandable={false}\n fetcher={fetcher}\n />\n </Modal>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,371 @@
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 mi = require("@flanksource/icons/mi");
6
+ const utils = require("../lib/utils.cjs");
7
+ const Modal = require("../overlay/Modal.cjs");
8
+ const Badge = require("./Badge.cjs");
9
+ const Timestamp = require("./cells/Timestamp.cjs");
10
+ const Icon = require("./Icon.cjs");
11
+ const TimeseriesCoreBars = require("./TimeseriesCoreBars.cjs");
12
+ const TimeseriesGauge = require("./TimeseriesGauge.cjs");
13
+ const TimeseriesPanel = require("./TimeseriesPanel.cjs");
14
+ const WorkloadCard_model = require("./WorkloadCard.model.cjs");
15
+ const UiChip = require("../icons/components/UiChip.cjs");
16
+ const UiMemoryStick = require("../icons/components/UiMemoryStick.cjs");
17
+ const UiHardDrive = require("../icons/components/UiHardDrive.cjs");
18
+ const UiFullscreen = require("../icons/components/UiFullscreen.cjs");
19
+ const KIND_ICONS = {
20
+ service: mi.K8SService,
21
+ ingress: mi.K8SIngress,
22
+ deployment: mi.K8SDeployment,
23
+ statefulset: mi.K8SStatefulset,
24
+ pod: mi.K8SPod
25
+ };
26
+ const SIZE_CLASSES = {
27
+ sm: {
28
+ root: "p-2",
29
+ header: "mb-2 gap-2",
30
+ titleGap: "gap-1",
31
+ icon: "h-3.5 w-3.5",
32
+ name: "text-xs",
33
+ meta: "mt-0.5 gap-x-1.5 gap-y-0.5 text-[11px] leading-4",
34
+ statusSize: "xxs",
35
+ action: "h-5 w-5",
36
+ actionIcon: 12,
37
+ metricGrid: "grid-cols-1 gap-1.5",
38
+ metricVariant: "cell",
39
+ empty: "px-2 py-3 text-[11px]"
40
+ },
41
+ md: {
42
+ root: "p-3",
43
+ header: "mb-3 gap-2",
44
+ titleGap: "gap-1.5",
45
+ icon: "h-4 w-4",
46
+ name: "text-sm",
47
+ meta: "mt-0.5 gap-x-2 gap-y-0.5 text-xs",
48
+ statusSize: "xs",
49
+ action: "h-6 w-6",
50
+ actionIcon: 14,
51
+ metricGrid: "gap-3",
52
+ metricVariant: "default",
53
+ empty: "px-3 py-4 text-xs"
54
+ },
55
+ lg: {
56
+ root: "p-4",
57
+ header: "mb-4 gap-3",
58
+ titleGap: "gap-2",
59
+ icon: "h-5 w-5",
60
+ name: "text-base",
61
+ meta: "mt-1 gap-x-2 gap-y-1 text-xs",
62
+ statusSize: "sm",
63
+ action: "h-7 w-7",
64
+ actionIcon: 16,
65
+ metricGrid: "gap-4",
66
+ metricVariant: "default",
67
+ empty: "px-4 py-6 text-sm"
68
+ }
69
+ };
70
+ const VARIANT_CLASSES = {
71
+ default: "border-border bg-card text-card-foreground",
72
+ outline: "border-border bg-background text-foreground",
73
+ subtle: "border-transparent bg-muted/35 text-foreground",
74
+ elevated: "border-border/70 bg-card text-card-foreground shadow-sm",
75
+ ghost: "border-transparent bg-transparent text-foreground"
76
+ };
77
+ function metricGridClass(count) {
78
+ if (count <= 1) return "grid-cols-1";
79
+ if (count === 2) return "grid-cols-2";
80
+ return "grid-cols-3";
81
+ }
82
+ function WorkloadIcon({
83
+ icon,
84
+ title,
85
+ className
86
+ }) {
87
+ if (typeof icon === "string") {
88
+ return /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { name: icon, title, ...className ? { className } : {} });
89
+ }
90
+ return /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon, title, ...className ? { className } : {} });
91
+ }
92
+ function metricSeries(metric) {
93
+ const series = [
94
+ {
95
+ id: metric.value.id,
96
+ label: metric.valueLabel ?? "usage",
97
+ ...metric.value.transform ? { transform: metric.value.transform } : {}
98
+ }
99
+ ];
100
+ if (typeof metric.max === "object") {
101
+ series.push({
102
+ id: metric.max.id,
103
+ label: metric.maxLabel ?? "limit",
104
+ ...metric.max.transform ? { transform: metric.max.transform } : {}
105
+ });
106
+ }
107
+ return series;
108
+ }
109
+ function hasMetric(metrics) {
110
+ return Boolean(metrics.cpu || metrics.memory || metrics.disk);
111
+ }
112
+ function HeaderMeta({ children }) {
113
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex min-w-0 items-center gap-1", children: [
114
+ /* @__PURE__ */ jsxRuntime.jsx(
115
+ "span",
116
+ {
117
+ className: "h-1 w-1 shrink-0 rounded-full bg-muted-foreground/40",
118
+ "aria-hidden": true
119
+ }
120
+ ),
121
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children })
122
+ ] });
123
+ }
124
+ function WorkloadCard({
125
+ workload,
126
+ metrics,
127
+ baseUrl,
128
+ range = "1h",
129
+ refreshMs = 5e3,
130
+ expandable = true,
131
+ size = "md",
132
+ variant = "default",
133
+ fetcher,
134
+ className
135
+ }) {
136
+ var _a;
137
+ const [expanded, setExpanded] = react.useState(false);
138
+ const kindLabel = WorkloadCard_model.workloadKindLabel(workload.kind);
139
+ const statusLabel = WorkloadCard_model.workloadStatusLabel(workload.status);
140
+ const replicaText = WorkloadCard_model.formatReplicaCounts(workload.replicas);
141
+ const workloadIcon = workload.icon ?? KIND_ICONS[workload.kind];
142
+ const sizeClasses = SIZE_CLASSES[size];
143
+ const panelMetrics = react.useMemo(() => {
144
+ const list = [];
145
+ if (metrics.cpu) {
146
+ list.push({
147
+ key: "cpu",
148
+ title: metrics.cpu.title ?? "CPU",
149
+ unit: metrics.cpu.unit ?? "short",
150
+ icon: metrics.cpu.icon ?? UiChip.UiChip,
151
+ metric: metrics.cpu
152
+ });
153
+ }
154
+ if (metrics.memory) {
155
+ list.push({
156
+ key: "memory",
157
+ title: metrics.memory.title ?? "Memory",
158
+ unit: metrics.memory.unit ?? "bytes",
159
+ icon: metrics.memory.icon ?? UiMemoryStick.UiMemoryStick,
160
+ metric: metrics.memory
161
+ });
162
+ }
163
+ if (metrics.disk) {
164
+ list.push({
165
+ key: "disk",
166
+ title: metrics.disk.title ?? "Disk",
167
+ unit: metrics.disk.unit ?? "bytes",
168
+ icon: metrics.disk.icon ?? UiHardDrive.UiHardDrive,
169
+ metric: metrics.disk
170
+ });
171
+ }
172
+ return list;
173
+ }, [metrics]);
174
+ const metricProps = {
175
+ ...baseUrl !== void 0 ? { baseUrl } : {},
176
+ range,
177
+ refreshMs,
178
+ ...fetcher !== void 0 ? { fetcher } : {}
179
+ };
180
+ const canExpand = expandable && hasMetric(metrics);
181
+ return /* @__PURE__ */ jsxRuntime.jsxs(
182
+ "div",
183
+ {
184
+ className: utils.cn(
185
+ "w-full rounded-md border",
186
+ VARIANT_CLASSES[variant],
187
+ sizeClasses.root,
188
+ className
189
+ ),
190
+ "data-size": size,
191
+ "data-variant": variant,
192
+ children: [
193
+ /* @__PURE__ */ jsxRuntime.jsxs(
194
+ "div",
195
+ {
196
+ className: utils.cn("flex items-start justify-between", sizeClasses.header),
197
+ children: [
198
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
199
+ /* @__PURE__ */ jsxRuntime.jsxs(
200
+ "div",
201
+ {
202
+ className: utils.cn("flex min-w-0 items-center", sizeClasses.titleGap),
203
+ children: [
204
+ /* @__PURE__ */ jsxRuntime.jsx(
205
+ WorkloadIcon,
206
+ {
207
+ icon: workloadIcon,
208
+ title: kindLabel,
209
+ className: utils.cn("shrink-0 text-muted-foreground", sizeClasses.icon)
210
+ }
211
+ ),
212
+ /* @__PURE__ */ jsxRuntime.jsx(
213
+ "span",
214
+ {
215
+ className: utils.cn("min-w-0 truncate font-medium", sizeClasses.name),
216
+ title: workload.name,
217
+ children: workload.name
218
+ }
219
+ )
220
+ ]
221
+ }
222
+ ),
223
+ /* @__PURE__ */ jsxRuntime.jsxs(
224
+ "div",
225
+ {
226
+ className: utils.cn(
227
+ "flex flex-wrap items-center text-muted-foreground",
228
+ sizeClasses.meta
229
+ ),
230
+ children: [
231
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: kindLabel }),
232
+ workload.namespace ? /* @__PURE__ */ jsxRuntime.jsx(HeaderMeta, { children: workload.namespace }) : null,
233
+ workload.role ? /* @__PURE__ */ jsxRuntime.jsx(HeaderMeta, { children: workload.role }) : null,
234
+ replicaText ? /* @__PURE__ */ jsxRuntime.jsx(HeaderMeta, { children: replicaText }) : null,
235
+ workload.createdAt !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs(HeaderMeta, { children: [
236
+ "age ",
237
+ /* @__PURE__ */ jsxRuntime.jsx(Timestamp.Timestamp, { value: workload.createdAt, format: "relative" })
238
+ ] }) : null
239
+ ]
240
+ }
241
+ )
242
+ ] }),
243
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
244
+ statusLabel ? /* @__PURE__ */ jsxRuntime.jsx("span", { title: (_a = workload.status) == null ? void 0 : _a.message, children: /* @__PURE__ */ jsxRuntime.jsx(
245
+ Badge.Badge,
246
+ {
247
+ tone: WorkloadCard_model.workloadStatusTone(workload.status),
248
+ variant: "soft",
249
+ size: sizeClasses.statusSize,
250
+ children: statusLabel
251
+ }
252
+ ) }) : null,
253
+ canExpand ? /* @__PURE__ */ jsxRuntime.jsx(
254
+ "button",
255
+ {
256
+ type: "button",
257
+ "aria-label": `Open ${workload.name} history`,
258
+ title: "History",
259
+ onClick: () => setExpanded(true),
260
+ className: utils.cn(
261
+ "inline-flex items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
262
+ sizeClasses.action
263
+ ),
264
+ children: /* @__PURE__ */ jsxRuntime.jsx(
265
+ Icon.Icon,
266
+ {
267
+ icon: UiFullscreen.UiFullscreen,
268
+ width: sizeClasses.actionIcon,
269
+ height: sizeClasses.actionIcon
270
+ }
271
+ )
272
+ }
273
+ ) : null
274
+ ] })
275
+ ]
276
+ }
277
+ ),
278
+ hasMetric(metrics) ? /* @__PURE__ */ jsxRuntime.jsxs(
279
+ "div",
280
+ {
281
+ className: utils.cn(
282
+ "grid items-start gap-3",
283
+ sizeClasses.metricVariant === "cell" ? sizeClasses.metricGrid : utils.cn(
284
+ metricGridClass(panelMetrics.length),
285
+ sizeClasses.metricGrid
286
+ )
287
+ ),
288
+ children: [
289
+ metrics.cpu ? /* @__PURE__ */ jsxRuntime.jsx(
290
+ TimeseriesCoreBars.TimeseriesCoreBars,
291
+ {
292
+ title: metrics.cpu.title ?? "CPU",
293
+ icon: metrics.cpu.icon ?? UiChip.UiChip,
294
+ value: metrics.cpu.value,
295
+ ...metrics.cpu.max !== void 0 ? { max: metrics.cpu.max } : {},
296
+ ...metrics.cpu.thresholds ? { thresholds: metrics.cpu.thresholds } : {},
297
+ ...metrics.cpu.orientation ? { orientation: metrics.cpu.orientation } : {},
298
+ ...metrics.cpu.showValue !== void 0 ? { showValue: metrics.cpu.showValue } : {},
299
+ variant: sizeClasses.metricVariant,
300
+ ...metricProps
301
+ }
302
+ ) : null,
303
+ metrics.memory ? /* @__PURE__ */ jsxRuntime.jsx(
304
+ TimeseriesGauge.TimeseriesGauge,
305
+ {
306
+ title: metrics.memory.title ?? "Memory",
307
+ icon: metrics.memory.icon ?? UiMemoryStick.UiMemoryStick,
308
+ value: metrics.memory.value,
309
+ unit: metrics.memory.unit ?? "bytes",
310
+ expandable: false,
311
+ centerDisplay: metrics.memory.centerDisplay ?? "value",
312
+ variant: sizeClasses.metricVariant,
313
+ ...metrics.memory.max !== void 0 ? { max: metrics.memory.max } : {},
314
+ ...metrics.memory.thresholds ? { thresholds: metrics.memory.thresholds } : {},
315
+ ...metricProps
316
+ }
317
+ ) : null,
318
+ metrics.disk ? /* @__PURE__ */ jsxRuntime.jsx(
319
+ TimeseriesGauge.TimeseriesGauge,
320
+ {
321
+ title: metrics.disk.title ?? "Disk",
322
+ icon: metrics.disk.icon ?? UiHardDrive.UiHardDrive,
323
+ value: metrics.disk.value,
324
+ unit: metrics.disk.unit ?? "bytes",
325
+ expandable: false,
326
+ centerDisplay: metrics.disk.centerDisplay ?? "value",
327
+ variant: sizeClasses.metricVariant,
328
+ ...metrics.disk.max !== void 0 ? { max: metrics.disk.max } : {},
329
+ ...metrics.disk.thresholds ? { thresholds: metrics.disk.thresholds } : {},
330
+ ...metricProps
331
+ }
332
+ ) : null
333
+ ]
334
+ }
335
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
336
+ "div",
337
+ {
338
+ className: utils.cn(
339
+ "rounded-md border border-dashed border-border text-center text-muted-foreground",
340
+ sizeClasses.empty
341
+ ),
342
+ children: "No metrics configured"
343
+ }
344
+ ),
345
+ /* @__PURE__ */ jsxRuntime.jsx(
346
+ Modal.Modal,
347
+ {
348
+ open: expanded,
349
+ onClose: () => setExpanded(false),
350
+ title: `${workload.name} history`,
351
+ size: "xl",
352
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-3", children: panelMetrics.map(({ key, title, unit, icon, metric }) => /* @__PURE__ */ jsxRuntime.jsx(
353
+ TimeseriesPanel.TimeseriesPanel,
354
+ {
355
+ title,
356
+ icon,
357
+ series: metricSeries(metric),
358
+ ...unit ? { unit } : {},
359
+ ...metricProps,
360
+ expandable: false
361
+ },
362
+ key
363
+ )) })
364
+ }
365
+ )
366
+ ]
367
+ }
368
+ );
369
+ }
370
+ exports.WorkloadCard = WorkloadCard;
371
+ //# sourceMappingURL=WorkloadCard.cjs.map