@tollerud/ui 1.1.4 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (361) hide show
  1. package/AGENTS.md +34 -11
  2. package/CHANGELOG.md +376 -0
  3. package/COMPONENTS.md +951 -0
  4. package/GETTING_STARTED.md +159 -0
  5. package/README.md +51 -43
  6. package/SKILL.md +55 -19
  7. package/components.json +18 -0
  8. package/dist/accordion.d.ts +20 -0
  9. package/dist/accordion.js +5 -0
  10. package/dist/accordion.js.map +1 -0
  11. package/dist/action-diff.d.ts +26 -0
  12. package/dist/action-diff.js +5 -0
  13. package/dist/action-diff.js.map +1 -0
  14. package/dist/action-row.d.ts +36 -0
  15. package/dist/action-row.js +6 -0
  16. package/dist/action-row.js.map +1 -0
  17. package/dist/alert-inbox.d.ts +23 -0
  18. package/dist/alert-inbox.js +6 -0
  19. package/dist/alert-inbox.js.map +1 -0
  20. package/dist/alert.d.ts +33 -0
  21. package/dist/alert.js +5 -0
  22. package/dist/alert.js.map +1 -0
  23. package/dist/approval-card.d.ts +27 -0
  24. package/dist/approval-card.js +5 -0
  25. package/dist/approval-card.js.map +1 -0
  26. package/dist/area-chart.d.ts +10 -0
  27. package/dist/area-chart.js +5 -0
  28. package/dist/area-chart.js.map +1 -0
  29. package/dist/avatar.d.ts +27 -0
  30. package/dist/avatar.js +5 -0
  31. package/dist/avatar.js.map +1 -0
  32. package/dist/backup-status-panel.d.ts +25 -0
  33. package/dist/backup-status-panel.js +6 -0
  34. package/dist/backup-status-panel.js.map +1 -0
  35. package/dist/badge.d.ts +17 -0
  36. package/dist/badge.js +5 -0
  37. package/dist/badge.js.map +1 -0
  38. package/dist/bar-chart.d.ts +15 -0
  39. package/dist/bar-chart.js +5 -0
  40. package/dist/bar-chart.js.map +1 -0
  41. package/dist/bento-dashboard.d.ts +30 -0
  42. package/dist/bento-dashboard.js +5 -0
  43. package/dist/bento-dashboard.js.map +1 -0
  44. package/dist/breadcrumb.d.ts +16 -0
  45. package/dist/breadcrumb.js +5 -0
  46. package/dist/breadcrumb.js.map +1 -0
  47. package/dist/button.d.ts +29 -0
  48. package/dist/button.js +5 -0
  49. package/dist/button.js.map +1 -0
  50. package/dist/card.d.ts +10 -0
  51. package/dist/card.js +5 -0
  52. package/dist/card.js.map +1 -0
  53. package/dist/checkbox.d.ts +9 -0
  54. package/dist/checkbox.js +5 -0
  55. package/dist/checkbox.js.map +1 -0
  56. package/dist/chunk-2QWKOCWF.js +79 -0
  57. package/dist/chunk-2QWKOCWF.js.map +1 -0
  58. package/dist/chunk-3TGMGBKM.js +393 -0
  59. package/dist/chunk-3TGMGBKM.js.map +1 -0
  60. package/dist/chunk-3XTZPDNV.js +94 -0
  61. package/dist/chunk-3XTZPDNV.js.map +1 -0
  62. package/dist/chunk-435JHF7G.js +65 -0
  63. package/dist/chunk-435JHF7G.js.map +1 -0
  64. package/dist/chunk-4PA2ACNF.js +52 -0
  65. package/dist/chunk-4PA2ACNF.js.map +1 -0
  66. package/dist/chunk-5GWHUJ5D.js +29 -0
  67. package/dist/chunk-5GWHUJ5D.js.map +1 -0
  68. package/dist/chunk-6FUKJD3W.js +123 -0
  69. package/dist/chunk-6FUKJD3W.js.map +1 -0
  70. package/dist/chunk-6IS2AYYG.js +106 -0
  71. package/dist/chunk-6IS2AYYG.js.map +1 -0
  72. package/dist/chunk-6PZKU6ZL.js +78 -0
  73. package/dist/chunk-6PZKU6ZL.js.map +1 -0
  74. package/dist/chunk-6SKTH45H.js +75 -0
  75. package/dist/chunk-6SKTH45H.js.map +1 -0
  76. package/dist/chunk-6UXW5YUC.js +77 -0
  77. package/dist/chunk-6UXW5YUC.js.map +1 -0
  78. package/dist/chunk-7EP2T3OW.js +52 -0
  79. package/dist/chunk-7EP2T3OW.js.map +1 -0
  80. package/dist/chunk-7J5QXUQN.js +38 -0
  81. package/dist/chunk-7J5QXUQN.js.map +1 -0
  82. package/dist/chunk-7TOT5ME3.js +53 -0
  83. package/dist/chunk-7TOT5ME3.js.map +1 -0
  84. package/dist/chunk-A6L5C3IJ.js +47 -0
  85. package/dist/chunk-A6L5C3IJ.js.map +1 -0
  86. package/dist/chunk-ANW6J6PV.js +42 -0
  87. package/dist/chunk-ANW6J6PV.js.map +1 -0
  88. package/dist/chunk-APFFKNPS.js +80 -0
  89. package/dist/chunk-APFFKNPS.js.map +1 -0
  90. package/dist/chunk-AZADSX4Z.js +85 -0
  91. package/dist/chunk-AZADSX4Z.js.map +1 -0
  92. package/dist/chunk-BPCH5LJ3.js +36 -0
  93. package/dist/chunk-BPCH5LJ3.js.map +1 -0
  94. package/dist/chunk-CBQ63EBL.js +85 -0
  95. package/dist/chunk-CBQ63EBL.js.map +1 -0
  96. package/dist/chunk-CDI7353B.js +40 -0
  97. package/dist/chunk-CDI7353B.js.map +1 -0
  98. package/dist/chunk-CKNWXYMA.js +53 -0
  99. package/dist/chunk-CKNWXYMA.js.map +1 -0
  100. package/dist/chunk-DFM7UUKB.js +79 -0
  101. package/dist/chunk-DFM7UUKB.js.map +1 -0
  102. package/dist/chunk-DGCRHVWW.js +84 -0
  103. package/dist/chunk-DGCRHVWW.js.map +1 -0
  104. package/dist/chunk-DNJI65VQ.js +22 -0
  105. package/dist/chunk-DNJI65VQ.js.map +1 -0
  106. package/dist/chunk-DOUDJU4P.js +63 -0
  107. package/dist/chunk-DOUDJU4P.js.map +1 -0
  108. package/dist/chunk-DRCMGIQ6.js +64 -0
  109. package/dist/chunk-DRCMGIQ6.js.map +1 -0
  110. package/dist/chunk-DZOBXK2S.js +28 -0
  111. package/dist/chunk-DZOBXK2S.js.map +1 -0
  112. package/dist/chunk-EN4OJCEF.js +54 -0
  113. package/dist/chunk-EN4OJCEF.js.map +1 -0
  114. package/dist/chunk-EVHZFYWX.js +33 -0
  115. package/dist/chunk-EVHZFYWX.js.map +1 -0
  116. package/dist/chunk-FGXOV2QH.js +23 -0
  117. package/dist/chunk-FGXOV2QH.js.map +1 -0
  118. package/dist/chunk-G2VKWNZA.js +53 -0
  119. package/dist/chunk-G2VKWNZA.js.map +1 -0
  120. package/dist/chunk-GTM2DE4C.js +156 -0
  121. package/dist/chunk-GTM2DE4C.js.map +1 -0
  122. package/dist/chunk-H3ZVGTJM.js +165 -0
  123. package/dist/chunk-H3ZVGTJM.js.map +1 -0
  124. package/dist/chunk-HWAWUEHC.js +28 -0
  125. package/dist/chunk-HWAWUEHC.js.map +1 -0
  126. package/dist/chunk-HWJVRTWO.js +36 -0
  127. package/dist/chunk-HWJVRTWO.js.map +1 -0
  128. package/dist/chunk-HYQGOC2E.js +79 -0
  129. package/dist/chunk-HYQGOC2E.js.map +1 -0
  130. package/dist/chunk-ILADNTUB.js +77 -0
  131. package/dist/chunk-ILADNTUB.js.map +1 -0
  132. package/dist/chunk-ISHZ6ZPJ.js +31 -0
  133. package/dist/chunk-ISHZ6ZPJ.js.map +1 -0
  134. package/dist/chunk-JRFSUVSO.js +66 -0
  135. package/dist/chunk-JRFSUVSO.js.map +1 -0
  136. package/dist/chunk-KI6OTVID.js +91 -0
  137. package/dist/chunk-KI6OTVID.js.map +1 -0
  138. package/dist/chunk-LUM2YJBH.js +73 -0
  139. package/dist/chunk-LUM2YJBH.js.map +1 -0
  140. package/dist/chunk-NHPISZWS.js +71 -0
  141. package/dist/chunk-NHPISZWS.js.map +1 -0
  142. package/dist/chunk-NOLWJJHT.js +52 -0
  143. package/dist/chunk-NOLWJJHT.js.map +1 -0
  144. package/dist/chunk-NPVINX3Q.js +20 -0
  145. package/dist/chunk-NPVINX3Q.js.map +1 -0
  146. package/dist/chunk-NSMU66ZX.js +47 -0
  147. package/dist/chunk-NSMU66ZX.js.map +1 -0
  148. package/dist/chunk-O57QMLNI.js +68 -0
  149. package/dist/chunk-O57QMLNI.js.map +1 -0
  150. package/dist/chunk-O5SWPHUQ.js +79 -0
  151. package/dist/chunk-O5SWPHUQ.js.map +1 -0
  152. package/dist/chunk-OGVSZ7NV.js +53 -0
  153. package/dist/chunk-OGVSZ7NV.js.map +1 -0
  154. package/dist/chunk-OONIUDST.js +48 -0
  155. package/dist/chunk-OONIUDST.js.map +1 -0
  156. package/dist/chunk-PLF3BBQI.js +139 -0
  157. package/dist/chunk-PLF3BBQI.js.map +1 -0
  158. package/dist/chunk-Q74VRQEX.js +26 -0
  159. package/dist/chunk-Q74VRQEX.js.map +1 -0
  160. package/dist/chunk-QEHTPQHL.js +35 -0
  161. package/dist/chunk-QEHTPQHL.js.map +1 -0
  162. package/dist/chunk-RJTDQOT2.js +73 -0
  163. package/dist/chunk-RJTDQOT2.js.map +1 -0
  164. package/dist/chunk-RQ3RXKAZ.js +203 -0
  165. package/dist/chunk-RQ3RXKAZ.js.map +1 -0
  166. package/dist/chunk-RWJELAS6.js +46 -0
  167. package/dist/chunk-RWJELAS6.js.map +1 -0
  168. package/dist/chunk-RZK2S2OO.js +126 -0
  169. package/dist/chunk-RZK2S2OO.js.map +1 -0
  170. package/dist/chunk-SAP7JSSO.js +106 -0
  171. package/dist/chunk-SAP7JSSO.js.map +1 -0
  172. package/dist/chunk-T3TQPOVM.js +79 -0
  173. package/dist/chunk-T3TQPOVM.js.map +1 -0
  174. package/dist/chunk-T56TTOI6.js +53 -0
  175. package/dist/chunk-T56TTOI6.js.map +1 -0
  176. package/dist/chunk-T7EFDE2L.js +36 -0
  177. package/dist/chunk-T7EFDE2L.js.map +1 -0
  178. package/dist/chunk-V3P5QLLX.js +154 -0
  179. package/dist/chunk-V3P5QLLX.js.map +1 -0
  180. package/dist/chunk-VTRUUT5K.js +31 -0
  181. package/dist/chunk-VTRUUT5K.js.map +1 -0
  182. package/dist/chunk-WDANALHD.js +95 -0
  183. package/dist/chunk-WDANALHD.js.map +1 -0
  184. package/dist/chunk-WSQNPRGN.js +12 -0
  185. package/dist/chunk-WSQNPRGN.js.map +1 -0
  186. package/dist/chunk-XR5QBVEV.js +56 -0
  187. package/dist/chunk-XR5QBVEV.js.map +1 -0
  188. package/dist/chunk-YYWODLER.js +111 -0
  189. package/dist/chunk-YYWODLER.js.map +1 -0
  190. package/dist/chunk-ZOXO3G3I.js +50 -0
  191. package/dist/chunk-ZOXO3G3I.js.map +1 -0
  192. package/dist/code-block.d.ts +14 -0
  193. package/dist/code-block.js +5 -0
  194. package/dist/code-block.js.map +1 -0
  195. package/dist/combobox.d.ts +26 -0
  196. package/dist/combobox.js +5 -0
  197. package/dist/combobox.js.map +1 -0
  198. package/dist/command-menu.d.ts +52 -0
  199. package/dist/command-menu.js +7 -0
  200. package/dist/command-menu.js.map +1 -0
  201. package/dist/container.d.ts +9 -0
  202. package/dist/container.js +5 -0
  203. package/dist/container.js.map +1 -0
  204. package/dist/cta-band.d.ts +12 -0
  205. package/dist/cta-band.js +5 -0
  206. package/dist/cta-band.js.map +1 -0
  207. package/dist/data-table.d.ts +58 -0
  208. package/dist/data-table.js +12 -0
  209. package/dist/data-table.js.map +1 -0
  210. package/dist/date-picker.d.ts +20 -0
  211. package/dist/date-picker.js +5 -0
  212. package/dist/date-picker.js.map +1 -0
  213. package/dist/dialog.d.ts +21 -0
  214. package/dist/dialog.js +5 -0
  215. package/dist/dialog.js.map +1 -0
  216. package/dist/divider.d.ts +12 -0
  217. package/dist/divider.js +5 -0
  218. package/dist/divider.js.map +1 -0
  219. package/dist/docker-stack-card.d.ts +21 -0
  220. package/dist/docker-stack-card.js +6 -0
  221. package/dist/docker-stack-card.js.map +1 -0
  222. package/dist/donut.d.ts +15 -0
  223. package/dist/donut.js +5 -0
  224. package/dist/donut.js.map +1 -0
  225. package/dist/dropdown-menu.d.ts +15 -0
  226. package/dist/dropdown-menu.js +5 -0
  227. package/dist/dropdown-menu.js.map +1 -0
  228. package/dist/empty.d.ts +12 -0
  229. package/dist/empty.js +5 -0
  230. package/dist/empty.js.map +1 -0
  231. package/dist/feature-card.d.ts +11 -0
  232. package/dist/feature-card.js +6 -0
  233. package/dist/feature-card.js.map +1 -0
  234. package/dist/file-upload.d.ts +20 -0
  235. package/dist/file-upload.js +5 -0
  236. package/dist/file-upload.js.map +1 -0
  237. package/dist/footer.d.ts +35 -0
  238. package/dist/footer.js +5 -0
  239. package/dist/footer.js.map +1 -0
  240. package/dist/form-row.d.ts +15 -0
  241. package/dist/form-row.js +5 -0
  242. package/dist/form-row.js.map +1 -0
  243. package/dist/glow-card.d.ts +14 -0
  244. package/dist/glow-card.js +5 -0
  245. package/dist/glow-card.js.map +1 -0
  246. package/dist/hero-block.d.ts +16 -0
  247. package/dist/hero-block.js +7 -0
  248. package/dist/hero-block.js.map +1 -0
  249. package/dist/host-card.d.ts +27 -0
  250. package/dist/host-card.js +6 -0
  251. package/dist/host-card.js.map +1 -0
  252. package/dist/incident-card.d.ts +23 -0
  253. package/dist/incident-card.js +5 -0
  254. package/dist/incident-card.js.map +1 -0
  255. package/dist/index.d.ts +76 -960
  256. package/dist/index.js +68 -3812
  257. package/dist/index.js.map +1 -1
  258. package/dist/input.d.ts +10 -0
  259. package/dist/input.js +5 -0
  260. package/dist/input.js.map +1 -0
  261. package/dist/kbd.d.ts +24 -0
  262. package/dist/kbd.js +5 -0
  263. package/dist/kbd.js.map +1 -0
  264. package/dist/log-viewer.d.ts +35 -0
  265. package/dist/log-viewer.js +5 -0
  266. package/dist/log-viewer.js.map +1 -0
  267. package/dist/meter.d.ts +23 -0
  268. package/dist/meter.js +5 -0
  269. package/dist/meter.js.map +1 -0
  270. package/dist/noir-glow-background.d.ts +50 -0
  271. package/dist/noir-glow-background.js +4 -0
  272. package/dist/noir-glow-background.js.map +1 -0
  273. package/dist/pagination.d.ts +16 -0
  274. package/dist/pagination.js +5 -0
  275. package/dist/pagination.js.map +1 -0
  276. package/dist/panel.d.ts +12 -0
  277. package/dist/panel.js +5 -0
  278. package/dist/panel.js.map +1 -0
  279. package/dist/password-input.d.ts +10 -0
  280. package/dist/password-input.js +5 -0
  281. package/dist/password-input.js.map +1 -0
  282. package/dist/pill.d.ts +14 -0
  283. package/dist/pill.js +5 -0
  284. package/dist/pill.js.map +1 -0
  285. package/dist/pricing-card.d.ts +20 -0
  286. package/dist/pricing-card.js +6 -0
  287. package/dist/pricing-card.js.map +1 -0
  288. package/dist/progress.d.ts +6 -0
  289. package/dist/progress.js +5 -0
  290. package/dist/progress.js.map +1 -0
  291. package/dist/radio-group.d.ts +18 -0
  292. package/dist/radio-group.js +5 -0
  293. package/dist/radio-group.js.map +1 -0
  294. package/dist/rollback-plan.d.ts +23 -0
  295. package/dist/rollback-plan.js +5 -0
  296. package/dist/rollback-plan.js.map +1 -0
  297. package/dist/segmented.d.ts +17 -0
  298. package/dist/segmented.js +5 -0
  299. package/dist/segmented.js.map +1 -0
  300. package/dist/select.d.ts +18 -0
  301. package/dist/select.js +5 -0
  302. package/dist/select.js.map +1 -0
  303. package/dist/service-health-card.d.ts +21 -0
  304. package/dist/service-health-card.js +6 -0
  305. package/dist/service-health-card.js.map +1 -0
  306. package/dist/sheet.d.ts +25 -0
  307. package/dist/sheet.js +5 -0
  308. package/dist/sheet.js.map +1 -0
  309. package/dist/skeleton.d.ts +5 -0
  310. package/dist/skeleton.js +5 -0
  311. package/dist/skeleton.js.map +1 -0
  312. package/dist/slider.d.ts +12 -0
  313. package/dist/slider.js +5 -0
  314. package/dist/slider.js.map +1 -0
  315. package/dist/sparkline.d.ts +16 -0
  316. package/dist/sparkline.js +5 -0
  317. package/dist/sparkline.js.map +1 -0
  318. package/dist/stat-card.d.ts +15 -0
  319. package/dist/stat-card.js +5 -0
  320. package/dist/stat-card.js.map +1 -0
  321. package/dist/status-dot.d.ts +13 -0
  322. package/dist/status-dot.js +5 -0
  323. package/dist/status-dot.js.map +1 -0
  324. package/dist/stepper.d.ts +16 -0
  325. package/dist/stepper.js +5 -0
  326. package/dist/stepper.js.map +1 -0
  327. package/dist/switch.d.ts +9 -0
  328. package/dist/switch.js +5 -0
  329. package/dist/switch.js.map +1 -0
  330. package/dist/tabs.d.ts +9 -0
  331. package/dist/tabs.js +5 -0
  332. package/dist/tabs.js.map +1 -0
  333. package/dist/tag-input.d.ts +20 -0
  334. package/dist/tag-input.js +5 -0
  335. package/dist/tag-input.js.map +1 -0
  336. package/dist/textarea.d.ts +10 -0
  337. package/dist/textarea.js +5 -0
  338. package/dist/textarea.js.map +1 -0
  339. package/dist/timeline.d.ts +30 -0
  340. package/dist/timeline.js +5 -0
  341. package/dist/timeline.js.map +1 -0
  342. package/dist/toaster.d.ts +10 -0
  343. package/dist/toaster.js +4 -0
  344. package/dist/toaster.js.map +1 -0
  345. package/dist/tooltip.d.ts +12 -0
  346. package/dist/tooltip.js +5 -0
  347. package/dist/tooltip.js.map +1 -0
  348. package/dist/utils.d.ts +5 -0
  349. package/dist/utils.js +4 -0
  350. package/dist/utils.js.map +1 -0
  351. package/globals-layers.css +935 -0
  352. package/globals-v3.css +17 -0
  353. package/globals-v4.css +2 -0
  354. package/globals.css +12 -939
  355. package/package.json +82 -17
  356. package/registry.json +920 -0
  357. package/tailwind.css +9 -0
  358. package/tollerud-preset.js +55 -50
  359. package/dist/index.cjs +0 -3938
  360. package/dist/index.cjs.map +0 -1
  361. package/dist/index.d.cts +0 -960
@@ -0,0 +1,54 @@
1
+ 'use client';
2
+ import { cn } from './chunk-WSQNPRGN.js';
3
+ import { forwardRef } from 'react';
4
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
+
6
+ var severityStyles = {
7
+ critical: { border: "border-tollerud-error/50", dot: "bg-tollerud-error shadow-[0_0_8px_rgba(239,68,68,0.6)]", label: "text-tollerud-error" },
8
+ high: { border: "border-tollerud-yellow/50", dot: "bg-tollerud-yellow shadow-[0_0_8px_rgba(232,213,0,0.5)]", label: "text-tollerud-yellow" },
9
+ medium: { border: "border-tollerud-amber/40", dot: "bg-tollerud-amber", label: "text-tollerud-amber" },
10
+ low: { border: "border-tollerud-noir-500", dot: "bg-tollerud-noir-400", label: "text-tollerud-text-muted" },
11
+ info: { border: "border-tollerud-info/30", dot: "bg-tollerud-info", label: "text-tollerud-info" }
12
+ };
13
+ var IncidentCard = forwardRef(
14
+ ({ className, title, severity, timestamp, description, service, acknowledged, loading, ...props }, ref) => {
15
+ const style = severityStyles[severity];
16
+ return /* @__PURE__ */ jsx(
17
+ "div",
18
+ {
19
+ ref,
20
+ className: cn(
21
+ "rounded-lg border bg-tollerud-surface-raised p-4",
22
+ "transition-[border-color] duration-[150ms]",
23
+ style.border,
24
+ acknowledged && "opacity-50",
25
+ loading && "animate-pulse",
26
+ className
27
+ ),
28
+ ...props,
29
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
30
+ /* @__PURE__ */ jsx("span", { className: cn("w-2 h-2 rounded-full mt-1.5 flex-shrink-0", style.dot) }),
31
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
32
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
33
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-tollerud-foreground truncate", children: title }),
34
+ /* @__PURE__ */ jsx("span", { className: cn("text-[11px] font-medium uppercase whitespace-nowrap", style.label), children: severity })
35
+ ] }),
36
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-0.5 text-xs text-tollerud-text-muted", children: [
37
+ /* @__PURE__ */ jsx("span", { children: timestamp }),
38
+ service && /* @__PURE__ */ jsxs(Fragment, { children: [
39
+ /* @__PURE__ */ jsx("span", { className: "text-tollerud-noir-500", children: "\xB7" }),
40
+ /* @__PURE__ */ jsx("span", { children: service })
41
+ ] })
42
+ ] }),
43
+ description && /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-xs text-tollerud-text-secondary leading-relaxed", children: description })
44
+ ] })
45
+ ] })
46
+ }
47
+ );
48
+ }
49
+ );
50
+ IncidentCard.displayName = "IncidentCard";
51
+
52
+ export { IncidentCard };
53
+ //# sourceMappingURL=chunk-EN4OJCEF.js.map
54
+ //# sourceMappingURL=chunk-EN4OJCEF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../components/IncidentCard.tsx"],"names":[],"mappings":";;;;AAsBA,IAAM,cAAA,GAA2F;AAAA,EAC/F,UAAU,EAAE,MAAA,EAAQ,4BAA4B,GAAA,EAAK,wDAAA,EAA0D,OAAO,qBAAA,EAAsB;AAAA,EAC5I,MAAU,EAAE,MAAA,EAAQ,6BAA6B,GAAA,EAAK,yDAAA,EAA2D,OAAO,sBAAA,EAAuB;AAAA,EAC/I,QAAU,EAAE,MAAA,EAAQ,4BAA4B,GAAA,EAAK,mBAAA,EAAqB,OAAO,qBAAA,EAAsB;AAAA,EACvG,KAAU,EAAE,MAAA,EAAQ,4BAA4B,GAAA,EAAK,sBAAA,EAAwB,OAAO,0BAAA,EAA2B;AAAA,EAC/G,MAAU,EAAE,MAAA,EAAQ,2BAA2B,GAAA,EAAK,kBAAA,EAAoB,OAAO,oBAAA;AACjF,CAAA;AAEA,IAAM,YAAA,GAAe,UAAA;AAAA,EACnB,CAAC,EAAE,SAAA,EAAW,KAAA,EAAO,QAAA,EAAU,SAAA,EAAW,WAAA,EAAa,OAAA,EAAS,YAAA,EAAc,OAAA,EAAS,GAAG,KAAA,IAAS,GAAA,KAAQ;AACzG,IAAA,MAAM,KAAA,GAAQ,eAAe,QAAQ,CAAA;AAErC,IAAA,uBACE,GAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAW,EAAA;AAAA,UACT,kDAAA;AAAA,UACA,4CAAA;AAAA,UACA,KAAA,CAAM,MAAA;AAAA,UACN,YAAA,IAAgB,YAAA;AAAA,UAChB,OAAA,IAAW,eAAA;AAAA,UACX;AAAA,SACF;AAAA,QACC,GAAG,KAAA;AAAA,QAEJ,QAAA,kBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wBAAA,EACb,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,UAAK,SAAA,EAAW,EAAA,CAAG,2CAAA,EAA6C,KAAA,CAAM,GAAG,CAAA,EAAG,CAAA;AAAA,0BAC7E,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,gBAAA,EACb,QAAA,EAAA;AAAA,4BAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,yCAAA,EACb,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,yDAAA,EAA2D,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,8BACjF,GAAA,CAAC,UAAK,SAAA,EAAW,EAAA,CAAG,uDAAuD,KAAA,CAAM,KAAK,GACnF,QAAA,EAAA,QAAA,EACH;AAAA,aAAA,EACF,CAAA;AAAA,4BACA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,iEAAA,EACb,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,UAAM,QAAA,EAAA,SAAA,EAAU,CAAA;AAAA,cAChB,2BAAW,IAAA,CAAA,QAAA,EAAA,EAAE,QAAA,EAAA;AAAA,gCAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,wBAAA,EAAyB,QAAA,EAAA,MAAA,EAAC,CAAA;AAAA,gCAAO,GAAA,CAAC,UAAM,QAAA,EAAA,OAAA,EAAQ;AAAA,eAAA,EAAO;AAAA,aAAA,EACvF,CAAA;AAAA,YACC,WAAA,oBACC,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,+DAA+D,QAAA,EAAA,WAAA,EAAY;AAAA,WAAA,EAE5F;AAAA,SAAA,EACF;AAAA;AAAA,KACF;AAAA,EAEJ;AACF;AACA,YAAA,CAAa,WAAA,GAAc,cAAA","file":"chunk-EN4OJCEF.js","sourcesContent":["import { type HTMLAttributes, forwardRef } from 'react'\nimport { cn } from '@/lib/utils'\n\nexport type IncidentSeverity = 'critical' | 'high' | 'medium' | 'low' | 'info'\n\nexport interface IncidentCardProps extends HTMLAttributes<HTMLDivElement> {\n /** Incident title */\n title: string\n /** Severity level */\n severity: IncidentSeverity\n /** Timestamp string */\n timestamp: string\n /** Description / detail */\n description?: string\n /** Service name affected */\n service?: string\n /** Whether acknowledged */\n acknowledged?: boolean\n /** Whether the card is loading */\n loading?: boolean\n}\n\nconst severityStyles: Record<IncidentSeverity, { border: string; dot: string; label: string }> = {\n critical: { border: 'border-tollerud-error/50', dot: 'bg-tollerud-error shadow-[0_0_8px_rgba(239,68,68,0.6)]', label: 'text-tollerud-error' },\n high: { border: 'border-tollerud-yellow/50', dot: 'bg-tollerud-yellow shadow-[0_0_8px_rgba(232,213,0,0.5)]', label: 'text-tollerud-yellow' },\n medium: { border: 'border-tollerud-amber/40', dot: 'bg-tollerud-amber', label: 'text-tollerud-amber' },\n low: { border: 'border-tollerud-noir-500', dot: 'bg-tollerud-noir-400', label: 'text-tollerud-text-muted' },\n info: { border: 'border-tollerud-info/30', dot: 'bg-tollerud-info', label: 'text-tollerud-info' },\n}\n\nconst IncidentCard = forwardRef<HTMLDivElement, IncidentCardProps>(\n ({ className, title, severity, timestamp, description, service, acknowledged, loading, ...props }, ref) => {\n const style = severityStyles[severity]\n\n return (\n <div\n ref={ref}\n className={cn(\n 'rounded-lg border bg-tollerud-surface-raised p-4',\n 'transition-[border-color] duration-[150ms]',\n style.border,\n acknowledged && 'opacity-50',\n loading && 'animate-pulse',\n className\n )}\n {...props}\n >\n <div className=\"flex items-start gap-3\">\n <span className={cn('w-2 h-2 rounded-full mt-1.5 flex-shrink-0', style.dot)} />\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"text-sm font-semibold text-tollerud-foreground truncate\">{title}</span>\n <span className={cn('text-[11px] font-medium uppercase whitespace-nowrap', style.label)}>\n {severity}\n </span>\n </div>\n <div className=\"flex items-center gap-2 mt-0.5 text-xs text-tollerud-text-muted\">\n <span>{timestamp}</span>\n {service && <><span className=\"text-tollerud-noir-500\">·</span><span>{service}</span></>}\n </div>\n {description && (\n <p className=\"mt-1.5 text-xs text-tollerud-text-secondary leading-relaxed\">{description}</p>\n )}\n </div>\n </div>\n </div>\n )\n }\n)\nIncidentCard.displayName = 'IncidentCard'\n\nexport { IncidentCard }\n"]}
@@ -0,0 +1,33 @@
1
+ 'use client';
2
+ import { cn } from './chunk-WSQNPRGN.js';
3
+ import { forwardRef } from 'react';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ var Sparkline = forwardRef(
7
+ ({ className, data, width, height, w, h, color = "#E8D500", ...props }, ref) => {
8
+ const resolvedWidth = width ?? w ?? 120;
9
+ const resolvedHeight = height ?? h ?? 34;
10
+ const max = Math.max(...data);
11
+ const min = Math.min(...data);
12
+ const span = Math.max(data.length - 1, 1);
13
+ const pts = data.map(
14
+ (v, i) => `${i / span * resolvedWidth},${resolvedHeight - 2 - (v - min) / (max - min || 1) * (resolvedHeight - 4)}`
15
+ ).join(" ");
16
+ return /* @__PURE__ */ jsx("div", { ref, className: cn("inline-block", className), ...props, children: /* @__PURE__ */ jsx("svg", { width: resolvedWidth, height: resolvedHeight, className: "block", role: "img", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
17
+ "polyline",
18
+ {
19
+ points: pts,
20
+ fill: "none",
21
+ stroke: color,
22
+ strokeWidth: "1.6",
23
+ strokeLinecap: "round",
24
+ strokeLinejoin: "round"
25
+ }
26
+ ) }) });
27
+ }
28
+ );
29
+ Sparkline.displayName = "Sparkline";
30
+
31
+ export { Sparkline };
32
+ //# sourceMappingURL=chunk-EVHZFYWX.js.map
33
+ //# sourceMappingURL=chunk-EVHZFYWX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../components/Sparkline.tsx"],"names":[],"mappings":";;;;AAcA,IAAM,SAAA,GAAY,UAAA;AAAA,EAChB,CAAC,EAAE,SAAA,EAAW,IAAA,EAAM,KAAA,EAAO,MAAA,EAAQ,CAAA,EAAG,CAAA,EAAG,KAAA,GAAQ,SAAA,EAAW,GAAG,KAAA,IAAS,GAAA,KAAQ;AAC9E,IAAA,MAAM,aAAA,GAAgB,SAAS,CAAA,IAAK,GAAA;AACpC,IAAA,MAAM,cAAA,GAAiB,UAAU,CAAA,IAAK,EAAA;AACtC,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,GAAG,IAAI,CAAA;AAC5B,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,GAAG,IAAI,CAAA;AAC5B,IAAA,MAAM,OAAO,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,MAAA,GAAS,GAAG,CAAC,CAAA;AACxC,IAAA,MAAM,MAAM,IAAA,CACT,GAAA;AAAA,MACC,CAAC,CAAA,EAAG,CAAA,KACF,CAAA,EAAI,CAAA,GAAI,OAAQ,aAAa,CAAA,CAAA,EAAI,cAAA,GAAiB,CAAA,GAAA,CAAM,IAAI,GAAA,KAAQ,GAAA,GAAM,GAAA,IAAO,CAAA,CAAA,IAAO,iBAAiB,CAAA,CAAE,CAAA;AAAA,KAC/G,CACC,KAAK,GAAG,CAAA;AAEX,IAAA,uBACE,GAAA,CAAC,SAAI,GAAA,EAAU,SAAA,EAAW,GAAG,cAAA,EAAgB,SAAS,GAAI,GAAG,KAAA,EAC3D,8BAAC,KAAA,EAAA,EAAI,KAAA,EAAO,eAAe,MAAA,EAAQ,cAAA,EAAgB,WAAU,OAAA,EAAQ,IAAA,EAAK,KAAA,EAAM,aAAA,EAAY,MAAA,EAC1F,QAAA,kBAAA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,MAAA,EAAQ,GAAA;AAAA,QACR,IAAA,EAAK,MAAA;AAAA,QACL,MAAA,EAAQ,KAAA;AAAA,QACR,WAAA,EAAY,KAAA;AAAA,QACZ,aAAA,EAAc,OAAA;AAAA,QACd,cAAA,EAAe;AAAA;AAAA,OAEnB,CAAA,EACF,CAAA;AAAA,EAEJ;AACF;AACA,SAAA,CAAU,WAAA,GAAc,WAAA","file":"chunk-EVHZFYWX.js","sourcesContent":["import { type HTMLAttributes, forwardRef } from 'react'\nimport { cn } from '@/lib/utils'\n\nexport interface SparklineProps extends HTMLAttributes<HTMLDivElement> {\n data: number[]\n width?: number\n height?: number\n /** @deprecated Use `width` */\n w?: number\n /** @deprecated Use `height` */\n h?: number\n color?: string\n}\n\nconst Sparkline = forwardRef<HTMLDivElement, SparklineProps>(\n ({ className, data, width, height, w, h, color = '#E8D500', ...props }, ref) => {\n const resolvedWidth = width ?? w ?? 120\n const resolvedHeight = height ?? h ?? 34\n const max = Math.max(...data)\n const min = Math.min(...data)\n const span = Math.max(data.length - 1, 1)\n const pts = data\n .map(\n (v, i) =>\n `${(i / span) * resolvedWidth},${resolvedHeight - 2 - ((v - min) / (max - min || 1)) * (resolvedHeight - 4)}`\n )\n .join(' ')\n\n return (\n <div ref={ref} className={cn('inline-block', className)} {...props}>\n <svg width={resolvedWidth} height={resolvedHeight} className=\"block\" role=\"img\" aria-hidden=\"true\">\n <polyline\n points={pts}\n fill=\"none\"\n stroke={color}\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </div>\n )\n }\n)\nSparkline.displayName = 'Sparkline'\n\nexport { Sparkline }\n"]}
@@ -0,0 +1,23 @@
1
+ 'use client';
2
+ import { cn } from './chunk-WSQNPRGN.js';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ function Skeleton({
6
+ className,
7
+ ...props
8
+ }) {
9
+ return /* @__PURE__ */ jsx(
10
+ "div",
11
+ {
12
+ className: cn(
13
+ "animate-pulse rounded-md bg-tollerud-noir-800",
14
+ className
15
+ ),
16
+ ...props
17
+ }
18
+ );
19
+ }
20
+
21
+ export { Skeleton };
22
+ //# sourceMappingURL=chunk-FGXOV2QH.js.map
23
+ //# sourceMappingURL=chunk-FGXOV2QH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../components/Skeleton.tsx"],"names":[],"mappings":";;;AAEA,SAAS,QAAA,CAAS;AAAA,EAChB,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAyC;AACvC,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAA;AAAA,QACT,+CAAA;AAAA,QACA;AAAA,OACF;AAAA,MACC,GAAG;AAAA;AAAA,GACN;AAEJ","file":"chunk-FGXOV2QH.js","sourcesContent":["import { cn } from '@/lib/utils'\n\nfunction Skeleton({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n className={cn(\n 'animate-pulse rounded-md bg-tollerud-noir-800',\n className\n )}\n {...props}\n />\n )\n}\n\nexport { Skeleton }"]}
@@ -0,0 +1,53 @@
1
+ 'use client';
2
+ import { StatusDot } from './chunk-NHPISZWS.js';
3
+ import { cn } from './chunk-WSQNPRGN.js';
4
+ import { forwardRef } from 'react';
5
+ import { jsxs, jsx } from 'react/jsx-runtime';
6
+
7
+ var DockerStackCard = forwardRef(
8
+ ({ className, name, services, composePath, loading, ...props }, ref) => {
9
+ const onlineCount = services.filter((s) => s.status === "online").length;
10
+ const degraded = services.some((s) => s.status === "offline" || s.status === "warning");
11
+ const status = services.every((s) => s.status === "online") ? "online" : degraded ? "warning" : "offline";
12
+ return /* @__PURE__ */ jsxs(
13
+ "div",
14
+ {
15
+ ref,
16
+ className: cn(
17
+ "rounded-lg border bg-tollerud-surface-raised p-4",
18
+ "transition-[border-color] duration-[150ms]",
19
+ status === "offline" && "border-tollerud-error/40",
20
+ status === "warning" && "border-tollerud-yellow/30",
21
+ status === "online" && "border-tollerud-border hover:border-tollerud-noir-500",
22
+ loading && "animate-pulse",
23
+ className
24
+ ),
25
+ ...props,
26
+ children: [
27
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
28
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
29
+ /* @__PURE__ */ jsx(StatusDot, { status }),
30
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-sm text-tollerud-foreground truncate", children: name })
31
+ ] }),
32
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-tollerud-text-muted whitespace-nowrap ml-2", children: [
33
+ onlineCount,
34
+ "/",
35
+ services.length,
36
+ " healthy"
37
+ ] })
38
+ ] }),
39
+ composePath && /* @__PURE__ */ jsx("div", { className: "text-[11px] text-tollerud-text-muted font-mono mb-2 truncate", children: composePath }),
40
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children: services.map((svc) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs", children: [
41
+ /* @__PURE__ */ jsx("span", { className: "text-tollerud-text-secondary truncate", children: svc.name }),
42
+ /* @__PURE__ */ jsx(StatusDot, { status: svc.status })
43
+ ] }, svc.name)) })
44
+ ]
45
+ }
46
+ );
47
+ }
48
+ );
49
+ DockerStackCard.displayName = "DockerStackCard";
50
+
51
+ export { DockerStackCard };
52
+ //# sourceMappingURL=chunk-G2VKWNZA.js.map
53
+ //# sourceMappingURL=chunk-G2VKWNZA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../components/DockerStackCard.tsx"],"names":[],"mappings":";;;;;AAoBA,IAAM,eAAA,GAAkB,UAAA;AAAA,EACtB,CAAC,EAAE,SAAA,EAAW,IAAA,EAAM,QAAA,EAAU,aAAa,OAAA,EAAS,GAAG,KAAA,EAAM,EAAG,GAAA,KAAQ;AACtE,IAAA,MAAM,WAAA,GAAc,SAAS,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,MAAA,KAAW,QAAQ,CAAA,CAAE,MAAA;AAClE,IAAA,MAAM,QAAA,GAAW,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,EAAE,MAAA,KAAW,SAAA,IAAa,CAAA,CAAE,MAAA,KAAW,SAAS,CAAA;AACtF,IAAA,MAAM,MAAA,GAAiB,QAAA,CAAS,KAAA,CAAM,CAAC,CAAA,KAAM,CAAA,CAAE,MAAA,KAAW,QAAQ,CAAA,GAAI,QAAA,GAAW,QAAA,GAAW,SAAA,GAAY,SAAA;AAExG,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAW,EAAA;AAAA,UACT,kDAAA;AAAA,UACA,4CAAA;AAAA,UACA,WAAW,SAAA,IAAa,0BAAA;AAAA,UACxB,WAAW,SAAA,IAAa,2BAAA;AAAA,UACxB,WAAW,QAAA,IAAY,uDAAA;AAAA,UACvB,OAAA,IAAW,eAAA;AAAA,UACX;AAAA,SACF;AAAA,QACC,GAAG,KAAA;AAAA,QAEJ,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,wCAAA,EACb,QAAA,EAAA;AAAA,4BAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,iCAAA,EACb,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,aAAU,MAAA,EAAgB,CAAA;AAAA,8BAC3B,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,yDAAA,EACb,QAAA,EAAA,IAAA,EACH;AAAA,aAAA,EACF,CAAA;AAAA,4BACA,IAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,yDAAA,EACb,QAAA,EAAA;AAAA,cAAA,WAAA;AAAA,cAAY,GAAA;AAAA,cAAE,QAAA,CAAS,MAAA;AAAA,cAAO;AAAA,aAAA,EACjC;AAAA,WAAA,EACF,CAAA;AAAA,UACC,WAAA,oBACC,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,gEACZ,QAAA,EAAA,WAAA,EACH,CAAA;AAAA,0BAEF,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qBAAA,EACZ,QAAA,EAAA,QAAA,CAAS,GAAA,CAAI,CAAC,GAAA,qBACb,IAAA,CAAC,KAAA,EAAA,EAAmB,SAAA,EAAU,2CAAA,EAC5B,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,uCAAA,EAAyC,QAAA,EAAA,GAAA,CAAI,IAAA,EAAK,CAAA;AAAA,4BAClE,GAAA,CAAC,SAAA,EAAA,EAAU,MAAA,EAAQ,GAAA,CAAI,MAAA,EAAQ;AAAA,WAAA,EAAA,EAFvB,GAAA,CAAI,IAGd,CACD,CAAA,EACH;AAAA;AAAA;AAAA,KACF;AAAA,EAEJ;AACF;AACA,eAAA,CAAgB,WAAA,GAAc,iBAAA","file":"chunk-G2VKWNZA.js","sourcesContent":["import { type HTMLAttributes, forwardRef } from 'react'\nimport { cn } from '@/lib/utils'\nimport { StatusDot, type Status } from './StatusDot'\n\nexport interface StackService {\n name: string\n status: Status\n}\n\nexport interface DockerStackCardProps extends HTMLAttributes<HTMLDivElement> {\n /** Stack name */\n name: string\n /** Services in the stack */\n services: StackService[]\n /** Path to compose file */\n composePath?: string\n /** Whether the card is loading */\n loading?: boolean\n}\n\nconst DockerStackCard = forwardRef<HTMLDivElement, DockerStackCardProps>(\n ({ className, name, services, composePath, loading, ...props }, ref) => {\n const onlineCount = services.filter((s) => s.status === 'online').length\n const degraded = services.some((s) => s.status === 'offline' || s.status === 'warning')\n const status: Status = services.every((s) => s.status === 'online') ? 'online' : degraded ? 'warning' : 'offline'\n\n return (\n <div\n ref={ref}\n className={cn(\n 'rounded-lg border bg-tollerud-surface-raised p-4',\n 'transition-[border-color] duration-[150ms]',\n status === 'offline' && 'border-tollerud-error/40',\n status === 'warning' && 'border-tollerud-yellow/30',\n status === 'online' && 'border-tollerud-border hover:border-tollerud-noir-500',\n loading && 'animate-pulse',\n className\n )}\n {...props}\n >\n <div className=\"flex items-center justify-between mb-3\">\n <div className=\"flex items-center gap-2 min-w-0\">\n <StatusDot status={status} />\n <span className=\"font-semibold text-sm text-tollerud-foreground truncate\">\n {name}\n </span>\n </div>\n <span className=\"text-xs text-tollerud-text-muted whitespace-nowrap ml-2\">\n {onlineCount}/{services.length} healthy\n </span>\n </div>\n {composePath && (\n <div className=\"text-[11px] text-tollerud-text-muted font-mono mb-2 truncate\">\n {composePath}\n </div>\n )}\n <div className=\"flex flex-col gap-1\">\n {services.map((svc) => (\n <div key={svc.name} className=\"flex items-center justify-between text-xs\">\n <span className=\"text-tollerud-text-secondary truncate\">{svc.name}</span>\n <StatusDot status={svc.status} />\n </div>\n ))}\n </div>\n </div>\n )\n }\n)\nDockerStackCard.displayName = 'DockerStackCard'\n\nexport { DockerStackCard }\n"]}
@@ -0,0 +1,156 @@
1
+ 'use client';
2
+ import { cn } from './chunk-WSQNPRGN.js';
3
+ import { forwardRef, useState, useRef, useEffect, useCallback } from 'react';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+
6
+ var Select = forwardRef(
7
+ ({ className, label, error, placeholder, options = [], value, onChange, ...props }, ref) => {
8
+ const [open, setOpen] = useState(false);
9
+ const [highlightedIdx, setHighlightedIdx] = useState(0);
10
+ const containerRef = useRef(null);
11
+ const listRef = useRef(null);
12
+ const selectedOption = options.find((o) => o.value === value);
13
+ useEffect(() => {
14
+ if (!open) return;
15
+ const handleClick = (e) => {
16
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
17
+ setOpen(false);
18
+ }
19
+ };
20
+ document.addEventListener("mousedown", handleClick);
21
+ return () => document.removeEventListener("mousedown", handleClick);
22
+ }, [open]);
23
+ useEffect(() => {
24
+ if (open) {
25
+ const idx = value ? options.findIndex((o) => o.value === value) : -1;
26
+ setHighlightedIdx(idx >= 0 ? idx : 0);
27
+ }
28
+ }, [open, options, value]);
29
+ useEffect(() => {
30
+ if (open && listRef.current) {
31
+ const item = listRef.current.children[highlightedIdx];
32
+ item?.scrollIntoView({ block: "nearest" });
33
+ }
34
+ }, [open, highlightedIdx]);
35
+ const selectOption = useCallback(
36
+ (opt) => {
37
+ onChange?.(opt.value);
38
+ setOpen(false);
39
+ },
40
+ [onChange]
41
+ );
42
+ const handleKeyDown = (e) => {
43
+ if (!open) {
44
+ if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
45
+ e.preventDefault();
46
+ setOpen(true);
47
+ }
48
+ return;
49
+ }
50
+ switch (e.key) {
51
+ case "Escape":
52
+ e.preventDefault();
53
+ setOpen(false);
54
+ break;
55
+ case "ArrowDown":
56
+ e.preventDefault();
57
+ setHighlightedIdx((prev) => Math.min(prev + 1, options.length - 1));
58
+ break;
59
+ case "ArrowUp":
60
+ e.preventDefault();
61
+ setHighlightedIdx((prev) => Math.max(prev - 1, 0));
62
+ break;
63
+ case "Enter":
64
+ e.preventDefault();
65
+ if (options[highlightedIdx]) {
66
+ selectOption(options[highlightedIdx]);
67
+ }
68
+ break;
69
+ }
70
+ };
71
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", ref, ...props, children: [
72
+ label && /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-tollerud-text-muted", children: label }),
73
+ /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative", children: [
74
+ /* @__PURE__ */ jsxs(
75
+ "button",
76
+ {
77
+ type: "button",
78
+ onClick: () => setOpen(!open),
79
+ onKeyDown: handleKeyDown,
80
+ "aria-haspopup": "listbox",
81
+ "aria-expanded": open,
82
+ className: cn(
83
+ "font-sans text-sm w-full flex items-center justify-between px-3 py-2.5 rounded-lg",
84
+ "bg-tollerud-surface-raised",
85
+ "text-tollerud-text-primary text-left",
86
+ "transition-all duration-150 ease-out cursor-pointer",
87
+ error ? "border-tollerud-error/70 focus:border-tollerud-error focus:shadow-[0_0_0_1px_#EF4444]" : "border-tollerud-border focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_#E8D500]",
88
+ "border hover:border-tollerud-noir-400",
89
+ "focus:outline-none",
90
+ className
91
+ ),
92
+ children: [
93
+ /* @__PURE__ */ jsx("span", { className: cn(!selectedOption && "text-tollerud-text-muted"), children: selectedOption ? selectedOption.label : placeholder || "Select\u2026" }),
94
+ /* @__PURE__ */ jsx(
95
+ "svg",
96
+ {
97
+ className: cn(
98
+ "h-4 w-4 text-tollerud-text-muted transition-transform duration-150 flex-shrink-0",
99
+ open && "rotate-180"
100
+ ),
101
+ fill: "none",
102
+ viewBox: "0 0 24 24",
103
+ stroke: "currentColor",
104
+ strokeWidth: 2,
105
+ "aria-hidden": "true",
106
+ children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 9l6 6 6-6" })
107
+ }
108
+ )
109
+ ]
110
+ }
111
+ ),
112
+ open && /* @__PURE__ */ jsxs(
113
+ "div",
114
+ {
115
+ ref: listRef,
116
+ role: "listbox",
117
+ className: cn(
118
+ "absolute z-10 left-0 right-0 mt-1 py-1",
119
+ "rounded-lg border border-tollerud-border bg-tollerud-surface-overlay",
120
+ "shadow-[0_8px_24px_rgba(0,0,0,0.4)]",
121
+ "max-h-60 overflow-y-auto"
122
+ ),
123
+ children: [
124
+ options.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-xs text-tollerud-text-muted text-center", children: "No options" }),
125
+ options.map((opt, idx) => /* @__PURE__ */ jsx(
126
+ "button",
127
+ {
128
+ type: "button",
129
+ role: "option",
130
+ "aria-selected": opt.value === value,
131
+ onClick: () => selectOption(opt),
132
+ onMouseEnter: () => setHighlightedIdx(idx),
133
+ className: cn(
134
+ "w-full text-sm text-left px-3 py-2 transition-colors duration-75",
135
+ "cursor-pointer",
136
+ opt.value === value ? "text-tollerud-yellow" : "text-tollerud-text-primary",
137
+ idx === highlightedIdx && !(opt.value === value) ? "bg-tollerud-noir-700" : "hover:bg-tollerud-noir-700/60",
138
+ opt.value === value && highlightedIdx === idx && "bg-tollerud-noir-700"
139
+ ),
140
+ children: opt.label
141
+ },
142
+ opt.value
143
+ ))
144
+ ]
145
+ }
146
+ )
147
+ ] }),
148
+ error && /* @__PURE__ */ jsx("p", { className: "text-xs text-tollerud-error mt-0.5", children: error })
149
+ ] });
150
+ }
151
+ );
152
+ Select.displayName = "Select";
153
+
154
+ export { Select };
155
+ //# sourceMappingURL=chunk-GTM2DE4C.js.map
156
+ //# sourceMappingURL=chunk-GTM2DE4C.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../components/Select.tsx"],"names":[],"mappings":";;;;AAmBA,IAAM,MAAA,GAAS,UAAA;AAAA,EACb,CAAC,EAAE,SAAA,EAAW,KAAA,EAAO,OAAO,WAAA,EAAa,OAAA,GAAU,EAAC,EAAG,KAAA,EAAO,QAAA,EAAU,GAAG,KAAA,IAAS,GAAA,KAAQ;AAC1F,IAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAS,KAAK,CAAA;AACtC,IAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAI,SAAS,CAAC,CAAA;AACtD,IAAA,MAAM,YAAA,GAAe,OAAuB,IAAI,CAAA;AAChD,IAAA,MAAM,OAAA,GAAU,OAAuB,IAAI,CAAA;AAE3C,IAAA,MAAM,iBAAiB,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,UAAU,KAAK,CAAA;AAG5D,IAAA,SAAA,CAAU,MAAM;AACd,MAAA,IAAI,CAAC,IAAA,EAAM;AACX,MAAA,MAAM,WAAA,GAAc,CAAC,CAAA,KAAkB;AACrC,QAAA,IAAI,YAAA,CAAa,WAAW,CAAC,YAAA,CAAa,QAAQ,QAAA,CAAS,CAAA,CAAE,MAAc,CAAA,EAAG;AAC5E,UAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,QACf;AAAA,MACF,CAAA;AACA,MAAA,QAAA,CAAS,gBAAA,CAAiB,aAAa,WAAW,CAAA;AAClD,MAAA,OAAO,MAAM,QAAA,CAAS,mBAAA,CAAoB,WAAA,EAAa,WAAW,CAAA;AAAA,IACpE,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAGT,IAAA,SAAA,CAAU,MAAM;AACd,MAAA,IAAI,IAAA,EAAM;AACR,QAAA,MAAM,GAAA,GAAM,QAAQ,OAAA,CAAQ,SAAA,CAAU,CAAC,CAAA,KAAM,CAAA,CAAE,KAAA,KAAU,KAAK,CAAA,GAAI,EAAA;AAClE,QAAA,iBAAA,CAAkB,GAAA,IAAO,CAAA,GAAI,GAAA,GAAM,CAAC,CAAA;AAAA,MACtC;AAAA,IACF,CAAA,EAAG,CAAC,IAAA,EAAM,OAAA,EAAS,KAAK,CAAC,CAAA;AAGzB,IAAA,SAAA,CAAU,MAAM;AACd,MAAA,IAAI,IAAA,IAAQ,QAAQ,OAAA,EAAS;AAC3B,QAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,OAAA,CAAQ,QAAA,CAAS,cAAc,CAAA;AACpD,QAAA,IAAA,EAAM,cAAA,CAAe,EAAE,KAAA,EAAO,SAAA,EAAW,CAAA;AAAA,MAC3C;AAAA,IACF,CAAA,EAAG,CAAC,IAAA,EAAM,cAAc,CAAC,CAAA;AAEzB,IAAA,MAAM,YAAA,GAAe,WAAA;AAAA,MACnB,CAAC,GAAA,KAAsB;AACrB,QAAA,QAAA,GAAW,IAAI,KAAK,CAAA;AACpB,QAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,MACf,CAAA;AAAA,MACA,CAAC,QAAQ;AAAA,KACX;AAEA,IAAA,MAAM,aAAA,GAAgB,CAAC,CAAA,KAA2B;AAChD,MAAA,IAAI,CAAC,IAAA,EAAM;AACT,QAAA,IAAI,CAAA,CAAE,QAAQ,OAAA,IAAW,CAAA,CAAE,QAAQ,GAAA,IAAO,CAAA,CAAE,QAAQ,WAAA,EAAa;AAC/D,UAAA,CAAA,CAAE,cAAA,EAAe;AACjB,UAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,QACd;AACA,QAAA;AAAA,MACF;AAEA,MAAA,QAAQ,EAAE,GAAA;AAAK,QACb,KAAK,QAAA;AACH,UAAA,CAAA,CAAE,cAAA,EAAe;AACjB,UAAA,OAAA,CAAQ,KAAK,CAAA;AACb,UAAA;AAAA,QACF,KAAK,WAAA;AACH,UAAA,CAAA,CAAE,cAAA,EAAe;AACjB,UAAA,iBAAA,CAAkB,CAAC,SAAS,IAAA,CAAK,GAAA,CAAI,OAAO,CAAA,EAAG,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAC,CAAA;AAClE,UAAA;AAAA,QACF,KAAK,SAAA;AACH,UAAA,CAAA,CAAE,cAAA,EAAe;AACjB,UAAA,iBAAA,CAAkB,CAAC,IAAA,KAAS,IAAA,CAAK,IAAI,IAAA,GAAO,CAAA,EAAG,CAAC,CAAC,CAAA;AACjD,UAAA;AAAA,QACF,KAAK,OAAA;AACH,UAAA,CAAA,CAAE,cAAA,EAAe;AACjB,UAAA,IAAI,OAAA,CAAQ,cAAc,CAAA,EAAG;AAC3B,YAAA,YAAA,CAAa,OAAA,CAAQ,cAAc,CAAC,CAAA;AAAA,UACtC;AACA,UAAA;AAAA;AACJ,IACF,CAAA;AAEA,IAAA,4BACG,KAAA,EAAA,EAAI,SAAA,EAAU,uBAAA,EAAwB,GAAA,EAAW,GAAG,KAAA,EAClD,QAAA,EAAA;AAAA,MAAA,KAAA,oBACC,GAAA,CAAC,OAAA,EAAA,EAAM,SAAA,EAAU,8CAAA,EACd,QAAA,EAAA,KAAA,EACH,CAAA;AAAA,sBAEF,IAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,YAAA,EAAc,WAAU,UAAA,EAEhC,QAAA,EAAA;AAAA,wBAAA,IAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAK,QAAA;AAAA,YACL,OAAA,EAAS,MAAM,OAAA,CAAQ,CAAC,IAAI,CAAA;AAAA,YAC5B,SAAA,EAAW,aAAA;AAAA,YACX,eAAA,EAAc,SAAA;AAAA,YACd,eAAA,EAAe,IAAA;AAAA,YACf,SAAA,EAAW,EAAA;AAAA,cACT,mFAAA;AAAA,cACA,4BAAA;AAAA,cACA,sCAAA;AAAA,cACA,qDAAA;AAAA,cACA,QACI,uFAAA,GACA,sFAAA;AAAA,cACJ,uCAAA;AAAA,cACA,oBAAA;AAAA,cACA;AAAA,aACF;AAAA,YAEA,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAW,EAAA,CAAG,CAAC,cAAA,IAAkB,0BAA0B,CAAA,EAC9D,QAAA,EAAA,cAAA,GAAiB,cAAA,CAAe,KAAA,GAAQ,WAAA,IAAe,cAAA,EAC1D,CAAA;AAAA,8BACA,GAAA;AAAA,gBAAC,KAAA;AAAA,gBAAA;AAAA,kBACC,SAAA,EAAW,EAAA;AAAA,oBACT,kFAAA;AAAA,oBACA,IAAA,IAAQ;AAAA,mBACV;AAAA,kBACA,IAAA,EAAK,MAAA;AAAA,kBACL,OAAA,EAAQ,WAAA;AAAA,kBACR,MAAA,EAAO,cAAA;AAAA,kBACP,WAAA,EAAa,CAAA;AAAA,kBACb,aAAA,EAAY,MAAA;AAAA,kBAEZ,8BAAC,MAAA,EAAA,EAAK,aAAA,EAAc,SAAQ,cAAA,EAAe,OAAA,EAAQ,GAAE,cAAA,EAAe;AAAA;AAAA;AACtE;AAAA;AAAA,SACF;AAAA,QAGC,IAAA,oBACC,IAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,GAAA,EAAK,OAAA;AAAA,YACL,IAAA,EAAK,SAAA;AAAA,YACL,SAAA,EAAW,EAAA;AAAA,cACT,wCAAA;AAAA,cACA,sEAAA;AAAA,cACA,qCAAA;AAAA,cACA;AAAA,aACF;AAAA,YAEC,QAAA,EAAA;AAAA,cAAA,OAAA,CAAQ,WAAW,CAAA,oBAClB,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,0DAAyD,QAAA,EAAA,YAAA,EAExE,CAAA;AAAA,cAED,OAAA,CAAQ,GAAA,CAAI,CAAC,GAAA,EAAK,GAAA,qBACjB,GAAA;AAAA,gBAAC,QAAA;AAAA,gBAAA;AAAA,kBAEC,IAAA,EAAK,QAAA;AAAA,kBACL,IAAA,EAAK,QAAA;AAAA,kBACL,eAAA,EAAe,IAAI,KAAA,KAAU,KAAA;AAAA,kBAC7B,OAAA,EAAS,MAAM,YAAA,CAAa,GAAG,CAAA;AAAA,kBAC/B,YAAA,EAAc,MAAM,iBAAA,CAAkB,GAAG,CAAA;AAAA,kBACzC,SAAA,EAAW,EAAA;AAAA,oBACT,kEAAA;AAAA,oBACA,gBAAA;AAAA,oBACA,GAAA,CAAI,KAAA,KAAU,KAAA,GACV,sBAAA,GACA,4BAAA;AAAA,oBACJ,QAAQ,cAAA,IAAkB,EAAE,GAAA,CAAI,KAAA,KAAU,SACtC,sBAAA,GACA,+BAAA;AAAA,oBACJ,GAAA,CAAI,KAAA,KAAU,KAAA,IAAS,cAAA,KAAmB,GAAA,IAAO;AAAA,mBACnD;AAAA,kBAEC,QAAA,EAAA,GAAA,CAAI;AAAA,iBAAA;AAAA,gBAlBA,GAAA,CAAI;AAAA,eAoBZ;AAAA;AAAA;AAAA;AACH,OAAA,EAEJ,CAAA;AAAA,MACC,KAAA,oBACC,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,sCAAsC,QAAA,EAAA,KAAA,EAAM;AAAA,KAAA,EAE7D,CAAA;AAAA,EAEJ;AACF;AACA,MAAA,CAAO,WAAA,GAAc,QAAA","file":"chunk-GTM2DE4C.js","sourcesContent":["'use client'\n\nimport { type HTMLAttributes, forwardRef, useState, useRef, useEffect, useCallback } from 'react'\nimport { cn } from '@/lib/utils'\n\nexport interface SelectOption {\n value: string\n label: string\n}\n\nexport interface SelectProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {\n label?: string\n error?: string\n placeholder?: string\n options?: SelectOption[]\n value?: string\n onChange?: (value: string) => void\n}\n\nconst Select = forwardRef<HTMLDivElement, SelectProps>(\n ({ className, label, error, placeholder, options = [], value, onChange, ...props }, ref) => {\n const [open, setOpen] = useState(false)\n const [highlightedIdx, setHighlightedIdx] = useState(0)\n const containerRef = useRef<HTMLDivElement>(null)\n const listRef = useRef<HTMLDivElement>(null)\n\n const selectedOption = options.find((o) => o.value === value)\n\n // Close on click outside\n useEffect(() => {\n if (!open) return\n const handleClick = (e: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(e.target as Node)) {\n setOpen(false)\n }\n }\n document.addEventListener('mousedown', handleClick)\n return () => document.removeEventListener('mousedown', handleClick)\n }, [open])\n\n // Reset highlight when opening\n useEffect(() => {\n if (open) {\n const idx = value ? options.findIndex((o) => o.value === value) : -1\n setHighlightedIdx(idx >= 0 ? idx : 0)\n }\n }, [open, options, value])\n\n // Scroll highlighted option into view\n useEffect(() => {\n if (open && listRef.current) {\n const item = listRef.current.children[highlightedIdx] as HTMLElement | undefined\n item?.scrollIntoView({ block: 'nearest' })\n }\n }, [open, highlightedIdx])\n\n const selectOption = useCallback(\n (opt: SelectOption) => {\n onChange?.(opt.value)\n setOpen(false)\n },\n [onChange]\n )\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (!open) {\n if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {\n e.preventDefault()\n setOpen(true)\n }\n return\n }\n\n switch (e.key) {\n case 'Escape':\n e.preventDefault()\n setOpen(false)\n break\n case 'ArrowDown':\n e.preventDefault()\n setHighlightedIdx((prev) => Math.min(prev + 1, options.length - 1))\n break\n case 'ArrowUp':\n e.preventDefault()\n setHighlightedIdx((prev) => Math.max(prev - 1, 0))\n break\n case 'Enter':\n e.preventDefault()\n if (options[highlightedIdx]) {\n selectOption(options[highlightedIdx])\n }\n break\n }\n }\n\n return (\n <div className=\"flex flex-col gap-1.5\" ref={ref} {...props}>\n {label && (\n <label className=\"text-xs font-medium text-tollerud-text-muted\">\n {label}\n </label>\n )}\n <div ref={containerRef} className=\"relative\">\n {/* Trigger */}\n <button\n type=\"button\"\n onClick={() => setOpen(!open)}\n onKeyDown={handleKeyDown}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n className={cn(\n 'font-sans text-sm w-full flex items-center justify-between px-3 py-2.5 rounded-lg',\n 'bg-tollerud-surface-raised',\n 'text-tollerud-text-primary text-left',\n 'transition-all duration-150 ease-out cursor-pointer',\n error\n ? 'border-tollerud-error/70 focus:border-tollerud-error focus:shadow-[0_0_0_1px_#EF4444]'\n : 'border-tollerud-border focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_#E8D500]',\n 'border hover:border-tollerud-noir-400',\n 'focus:outline-none',\n className\n )}\n >\n <span className={cn(!selectedOption && 'text-tollerud-text-muted')}>\n {selectedOption ? selectedOption.label : placeholder || 'Select…'}\n </span>\n <svg\n className={cn(\n 'h-4 w-4 text-tollerud-text-muted transition-transform duration-150 flex-shrink-0',\n open && 'rotate-180'\n )}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={2}\n aria-hidden=\"true\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M6 9l6 6 6-6\" />\n </svg>\n </button>\n\n {/* Dropdown */}\n {open && (\n <div\n ref={listRef}\n role=\"listbox\"\n className={cn(\n 'absolute z-10 left-0 right-0 mt-1 py-1',\n 'rounded-lg border border-tollerud-border bg-tollerud-surface-overlay',\n 'shadow-[0_8px_24px_rgba(0,0,0,0.4)]',\n 'max-h-60 overflow-y-auto'\n )}\n >\n {options.length === 0 && (\n <div className=\"px-3 py-2 text-xs text-tollerud-text-muted text-center\">\n No options\n </div>\n )}\n {options.map((opt, idx) => (\n <button\n key={opt.value}\n type=\"button\"\n role=\"option\"\n aria-selected={opt.value === value}\n onClick={() => selectOption(opt)}\n onMouseEnter={() => setHighlightedIdx(idx)}\n className={cn(\n 'w-full text-sm text-left px-3 py-2 transition-colors duration-75',\n 'cursor-pointer',\n opt.value === value\n ? 'text-tollerud-yellow'\n : 'text-tollerud-text-primary',\n idx === highlightedIdx && !(opt.value === value)\n ? 'bg-tollerud-noir-700'\n : 'hover:bg-tollerud-noir-700/60',\n opt.value === value && highlightedIdx === idx && 'bg-tollerud-noir-700'\n )}\n >\n {opt.label}\n </button>\n ))}\n </div>\n )}\n </div>\n {error && (\n <p className=\"text-xs text-tollerud-error mt-0.5\">{error}</p>\n )}\n </div>\n )\n }\n)\nSelect.displayName = 'Select'\n\nexport { Select }"]}
@@ -0,0 +1,165 @@
1
+ 'use client';
2
+ import { cn } from './chunk-WSQNPRGN.js';
3
+ import { useId, useRef, useState, useMemo, useEffect } from 'react';
4
+ import { ChevronDown, Check } from 'lucide-react';
5
+ import { jsxs, jsx } from 'react/jsx-runtime';
6
+
7
+ var defaultFilter = (option, query) => option.label.toLowerCase().includes(query.toLowerCase());
8
+ function Combobox({
9
+ options,
10
+ value: valueProp,
11
+ defaultValue,
12
+ onChange,
13
+ placeholder = "Search\u2026",
14
+ label,
15
+ error,
16
+ filter = defaultFilter,
17
+ className,
18
+ disabled
19
+ }) {
20
+ const id = useId();
21
+ const rootRef = useRef(null);
22
+ const isControlled = valueProp !== void 0;
23
+ const [internalValue, setInternalValue] = useState(defaultValue);
24
+ const value = isControlled ? valueProp : internalValue;
25
+ const [open, setOpen] = useState(false);
26
+ const [query, setQuery] = useState("");
27
+ const [activeIndex, setActiveIndex] = useState(0);
28
+ const selected = options.find((o) => o.value === value);
29
+ const filtered = useMemo(() => {
30
+ if (!query) return options;
31
+ return options.filter((o) => filter(o, query));
32
+ }, [options, query, filter]);
33
+ useEffect(() => {
34
+ if (!open) return;
35
+ function onClickOutside(e) {
36
+ if (rootRef.current && !rootRef.current.contains(e.target)) {
37
+ setOpen(false);
38
+ setQuery("");
39
+ }
40
+ }
41
+ function onResize() {
42
+ setOpen(false);
43
+ setQuery("");
44
+ }
45
+ document.addEventListener("mousedown", onClickOutside);
46
+ window.addEventListener("resize", onResize);
47
+ return () => {
48
+ document.removeEventListener("mousedown", onClickOutside);
49
+ window.removeEventListener("resize", onResize);
50
+ };
51
+ }, [open]);
52
+ const commit = (option) => {
53
+ if (option.disabled) return;
54
+ if (!isControlled) setInternalValue(option.value);
55
+ onChange?.(option.value);
56
+ setOpen(false);
57
+ setQuery("");
58
+ };
59
+ const onKeyDown = (e) => {
60
+ if (e.key === "ArrowDown") {
61
+ e.preventDefault();
62
+ setOpen(true);
63
+ setActiveIndex((i) => Math.min(i + 1, filtered.length - 1));
64
+ } else if (e.key === "ArrowUp") {
65
+ e.preventDefault();
66
+ setActiveIndex((i) => Math.max(i - 1, 0));
67
+ } else if (e.key === "Enter") {
68
+ e.preventDefault();
69
+ const opt = filtered[activeIndex];
70
+ if (opt) commit(opt);
71
+ } else if (e.key === "Escape") {
72
+ setOpen(false);
73
+ setQuery("");
74
+ }
75
+ };
76
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: cn("relative flex flex-col gap-1", className), children: [
77
+ label && /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-xs font-medium text-tollerud-text-muted", children: label }),
78
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
79
+ /* @__PURE__ */ jsx(
80
+ "input",
81
+ {
82
+ id,
83
+ role: "combobox",
84
+ "aria-expanded": open,
85
+ "aria-autocomplete": "list",
86
+ "aria-controls": `${id}-listbox`,
87
+ disabled,
88
+ value: open ? query : selected?.label ?? "",
89
+ placeholder: selected ? selected.label : placeholder,
90
+ onFocus: () => {
91
+ setOpen(true);
92
+ setActiveIndex(0);
93
+ },
94
+ onChange: (e) => {
95
+ setQuery(e.target.value);
96
+ setActiveIndex(0);
97
+ if (!open) setOpen(true);
98
+ },
99
+ onKeyDown,
100
+ className: cn(
101
+ "w-full font-sans text-base px-3 py-2 pr-9 rounded",
102
+ "bg-tollerud-surface-raised border",
103
+ "text-tollerud-text-primary placeholder:text-tollerud-text-muted",
104
+ "transition-[border-color] duration-[150ms]",
105
+ "focus:outline-none focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_#E8D500]",
106
+ error ? "border-tollerud-error" : "border-tollerud-border",
107
+ disabled && "opacity-50 pointer-events-none"
108
+ )
109
+ }
110
+ ),
111
+ /* @__PURE__ */ jsx(
112
+ ChevronDown,
113
+ {
114
+ size: 15,
115
+ className: cn(
116
+ "pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-tollerud-text-muted transition-transform duration-[150ms]",
117
+ open && "rotate-180"
118
+ )
119
+ }
120
+ )
121
+ ] }),
122
+ open && /* @__PURE__ */ jsxs(
123
+ "ul",
124
+ {
125
+ id: `${id}-listbox`,
126
+ role: "listbox",
127
+ className: "absolute top-full z-20 mt-1 max-h-64 w-full overflow-auto rounded-lg border border-tollerud-border bg-tollerud-surface-overlay py-1 shadow-lg",
128
+ children: [
129
+ filtered.length === 0 && /* @__PURE__ */ jsx("li", { className: "px-3 py-2 text-sm text-tollerud-text-muted", children: "No results" }),
130
+ filtered.map((option, i) => {
131
+ const isSelected = option.value === value;
132
+ return /* @__PURE__ */ jsxs(
133
+ "li",
134
+ {
135
+ role: "option",
136
+ "aria-selected": isSelected,
137
+ onMouseDown: (e) => {
138
+ e.preventDefault();
139
+ commit(option);
140
+ },
141
+ onMouseEnter: () => setActiveIndex(i),
142
+ className: cn(
143
+ "flex items-center justify-between gap-2 px-3 py-2 text-sm cursor-pointer",
144
+ i === activeIndex ? "bg-tollerud-surface-hover text-tollerud-text-primary" : "text-tollerud-text-secondary",
145
+ option.disabled && "opacity-40 pointer-events-none"
146
+ ),
147
+ children: [
148
+ option.label,
149
+ isSelected && /* @__PURE__ */ jsx(Check, { size: 14, className: "text-tollerud-yellow" })
150
+ ]
151
+ },
152
+ option.value
153
+ );
154
+ })
155
+ ]
156
+ }
157
+ ),
158
+ error && /* @__PURE__ */ jsx("p", { className: "text-xs text-tollerud-error mt-0.5", children: error })
159
+ ] });
160
+ }
161
+ Combobox.displayName = "Combobox";
162
+
163
+ export { Combobox };
164
+ //# sourceMappingURL=chunk-H3ZVGTJM.js.map
165
+ //# sourceMappingURL=chunk-H3ZVGTJM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../components/Combobox.tsx"],"names":[],"mappings":"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","file":"chunk-H3ZVGTJM.js","sourcesContent":["'use client'\n\nimport { useEffect, useId, useMemo, useRef, useState } from 'react'\nimport { Check, ChevronDown } from 'lucide-react'\nimport { cn } from '@/lib/utils'\n\nexport interface ComboboxOption {\n value: string\n label: string\n disabled?: boolean\n}\n\nexport interface ComboboxProps {\n options: ComboboxOption[]\n value?: string\n defaultValue?: string\n onChange?: (value: string) => void\n placeholder?: string\n label?: string\n error?: string\n /** Filter predicate, defaults to a case-insensitive substring match on the label */\n filter?: (option: ComboboxOption, query: string) => boolean\n className?: string\n disabled?: boolean\n}\n\nconst defaultFilter = (option: ComboboxOption, query: string) =>\n option.label.toLowerCase().includes(query.toLowerCase())\n\nfunction Combobox({\n options,\n value: valueProp,\n defaultValue,\n onChange,\n placeholder = 'Search…',\n label,\n error,\n filter = defaultFilter,\n className,\n disabled,\n}: ComboboxProps) {\n const id = useId()\n const rootRef = useRef<HTMLDivElement>(null)\n const isControlled = valueProp !== undefined\n const [internalValue, setInternalValue] = useState(defaultValue)\n const value = isControlled ? valueProp : internalValue\n\n const [open, setOpen] = useState(false)\n const [query, setQuery] = useState('')\n const [activeIndex, setActiveIndex] = useState(0)\n\n const selected = options.find((o) => o.value === value)\n\n const filtered = useMemo(() => {\n if (!query) return options\n return options.filter((o) => filter(o, query))\n }, [options, query, filter])\n\n useEffect(() => {\n if (!open) return\n function onClickOutside(e: MouseEvent) {\n if (rootRef.current && !rootRef.current.contains(e.target as Node)) {\n setOpen(false)\n setQuery('')\n }\n }\n function onResize() {\n setOpen(false)\n setQuery('')\n }\n document.addEventListener('mousedown', onClickOutside)\n window.addEventListener('resize', onResize)\n return () => {\n document.removeEventListener('mousedown', onClickOutside)\n window.removeEventListener('resize', onResize)\n }\n }, [open])\n\n const commit = (option: ComboboxOption) => {\n if (option.disabled) return\n if (!isControlled) setInternalValue(option.value)\n onChange?.(option.value)\n setOpen(false)\n setQuery('')\n }\n\n const onKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n setOpen(true)\n setActiveIndex((i) => Math.min(i + 1, filtered.length - 1))\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n setActiveIndex((i) => Math.max(i - 1, 0))\n } else if (e.key === 'Enter') {\n e.preventDefault()\n const opt = filtered[activeIndex]\n if (opt) commit(opt)\n } else if (e.key === 'Escape') {\n setOpen(false)\n setQuery('')\n }\n }\n\n return (\n <div ref={rootRef} className={cn('relative flex flex-col gap-1', className)}>\n {label && (\n <label htmlFor={id} className=\"text-xs font-medium text-tollerud-text-muted\">\n {label}\n </label>\n )}\n <div className=\"relative\">\n <input\n id={id}\n role=\"combobox\"\n aria-expanded={open}\n aria-autocomplete=\"list\"\n aria-controls={`${id}-listbox`}\n disabled={disabled}\n value={open ? query : selected?.label ?? ''}\n placeholder={selected ? selected.label : placeholder}\n onFocus={() => {\n setOpen(true)\n setActiveIndex(0)\n }}\n onChange={(e) => {\n setQuery(e.target.value)\n setActiveIndex(0)\n if (!open) setOpen(true)\n }}\n onKeyDown={onKeyDown}\n className={cn(\n 'w-full font-sans text-base px-3 py-2 pr-9 rounded',\n 'bg-tollerud-surface-raised border',\n 'text-tollerud-text-primary placeholder:text-tollerud-text-muted',\n 'transition-[border-color] duration-[150ms]',\n 'focus:outline-none focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_#E8D500]',\n error ? 'border-tollerud-error' : 'border-tollerud-border',\n disabled && 'opacity-50 pointer-events-none'\n )}\n />\n <ChevronDown\n size={15}\n className={cn(\n 'pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-tollerud-text-muted transition-transform duration-[150ms]',\n open && 'rotate-180'\n )}\n />\n </div>\n\n {open && (\n <ul\n id={`${id}-listbox`}\n role=\"listbox\"\n className=\"absolute top-full z-20 mt-1 max-h-64 w-full overflow-auto rounded-lg border border-tollerud-border bg-tollerud-surface-overlay py-1 shadow-lg\"\n >\n {filtered.length === 0 && (\n <li className=\"px-3 py-2 text-sm text-tollerud-text-muted\">No results</li>\n )}\n {filtered.map((option, i) => {\n const isSelected = option.value === value\n return (\n <li\n key={option.value}\n role=\"option\"\n aria-selected={isSelected}\n onMouseDown={(e) => {\n e.preventDefault()\n commit(option)\n }}\n onMouseEnter={() => setActiveIndex(i)}\n className={cn(\n 'flex items-center justify-between gap-2 px-3 py-2 text-sm cursor-pointer',\n i === activeIndex ? 'bg-tollerud-surface-hover text-tollerud-text-primary' : 'text-tollerud-text-secondary',\n option.disabled && 'opacity-40 pointer-events-none'\n )}\n >\n {option.label}\n {isSelected && <Check size={14} className=\"text-tollerud-yellow\" />}\n </li>\n )\n })}\n </ul>\n )}\n\n {error && <p className=\"text-xs text-tollerud-error mt-0.5\">{error}</p>}\n </div>\n )\n}\nCombobox.displayName = 'Combobox'\n\nexport { Combobox }\n"]}
@@ -0,0 +1,28 @@
1
+ 'use client';
2
+ import { cn } from './chunk-WSQNPRGN.js';
3
+ import { forwardRef } from 'react';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ var Kbd = forwardRef(
7
+ ({ className, keys, size = "md", ...props }, ref) => {
8
+ const keyArray = typeof keys === "string" ? [keys] : keys;
9
+ return /* @__PURE__ */ jsx(
10
+ "span",
11
+ {
12
+ ref,
13
+ className: cn(
14
+ "tollerud-kbd",
15
+ size === "sm" && "tollerud-kbd--sm",
16
+ className
17
+ ),
18
+ ...props,
19
+ children: keyArray.map((key, i) => /* @__PURE__ */ jsx("span", { className: "tollerud-kbd__key", children: key }, i))
20
+ }
21
+ );
22
+ }
23
+ );
24
+ Kbd.displayName = "Kbd";
25
+
26
+ export { Kbd };
27
+ //# sourceMappingURL=chunk-HWAWUEHC.js.map
28
+ //# sourceMappingURL=chunk-HWAWUEHC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../components/Kbd.tsx"],"names":[],"mappings":";;;;AAwBA,IAAM,GAAA,GAAM,UAAA;AAAA,EACV,CAAC,EAAE,SAAA,EAAW,IAAA,EAAM,OAAO,IAAA,EAAM,GAAG,KAAA,EAAM,EAAG,GAAA,KAAQ;AACnD,IAAA,MAAM,WAAW,OAAO,IAAA,KAAS,QAAA,GAAW,CAAC,IAAI,CAAA,GAAI,IAAA;AAErD,IAAA,uBACE,GAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAW,EAAA;AAAA,UACT,cAAA;AAAA,UACA,SAAS,IAAA,IAAQ,kBAAA;AAAA,UACjB;AAAA,SACF;AAAA,QACC,GAAG,KAAA;AAAA,QAEH,QAAA,EAAA,QAAA,CAAS,GAAA,CAAI,CAAC,GAAA,EAAK,CAAA,qBAClB,GAAA,CAAC,MAAA,EAAA,EAAa,SAAA,EAAU,mBAAA,EACrB,QAAA,EAAA,GAAA,EAAA,EADQ,CAEX,CACD;AAAA;AAAA,KACH;AAAA,EAEJ;AACF;AACA,GAAA,CAAI,WAAA,GAAc,KAAA","file":"chunk-HWAWUEHC.js","sourcesContent":["\"use client\"\n\nimport { type HTMLAttributes, forwardRef } from 'react'\nimport { cn } from '@/lib/utils'\n\nexport interface KbdProps extends HTMLAttributes<HTMLSpanElement> {\n /**\n * The keys to display. Separate by + for chords.\n * @example \"⌘K\", \"⌘⇧S\", \"⌘K\", \"Esc\"\n */\n keys: string | string[]\n /** Small variant for inline use */\n size?: 'sm' | 'md'\n}\n\n/**\n * Keyboard shortcut chip — inspired by Raycast shortcut badges.\n *\n * ```tsx\n * <Kbd keys=\"⌘K\" />\n * <Kbd keys={[\"⌘\", \"⇧\", \"S\"]} />\n * <Kbd keys=\"Esc\" size=\"sm\" />\n * ```\n */\nconst Kbd = forwardRef<HTMLSpanElement, KbdProps>(\n ({ className, keys, size = 'md', ...props }, ref) => {\n const keyArray = typeof keys === 'string' ? [keys] : keys\n\n return (\n <span\n ref={ref}\n className={cn(\n 'tollerud-kbd',\n size === 'sm' && 'tollerud-kbd--sm',\n className\n )}\n {...props}\n >\n {keyArray.map((key, i) => (\n <span key={i} className=\"tollerud-kbd__key\">\n {key}\n </span>\n ))}\n </span>\n )\n }\n)\nKbd.displayName = 'Kbd'\n\nexport { Kbd }"]}