@tollerud/ui 1.1.5 → 3.1.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 (368) hide show
  1. package/AGENTS.md +36 -13
  2. package/CHANGELOG.md +411 -0
  3. package/COMPONENTS.md +951 -0
  4. package/GETTING_STARTED.md +159 -0
  5. package/README.md +51 -43
  6. package/SKILL.md +59 -24
  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 +28 -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 +10 -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-3LTW224O.js +53 -0
  59. package/dist/chunk-3LTW224O.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-A6L5C3IJ.js +47 -0
  83. package/dist/chunk-A6L5C3IJ.js.map +1 -0
  84. package/dist/chunk-ADE22JSR.js +54 -0
  85. package/dist/chunk-ADE22JSR.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-AQT3FZRQ.js +23 -0
  91. package/dist/chunk-AQT3FZRQ.js.map +1 -0
  92. package/dist/chunk-AZADSX4Z.js +85 -0
  93. package/dist/chunk-AZADSX4Z.js.map +1 -0
  94. package/dist/chunk-BPCH5LJ3.js +36 -0
  95. package/dist/chunk-BPCH5LJ3.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-DNJI65VQ.js +22 -0
  101. package/dist/chunk-DNJI65VQ.js.map +1 -0
  102. package/dist/chunk-DOUDJU4P.js +63 -0
  103. package/dist/chunk-DOUDJU4P.js.map +1 -0
  104. package/dist/chunk-DRCMGIQ6.js +64 -0
  105. package/dist/chunk-DRCMGIQ6.js.map +1 -0
  106. package/dist/chunk-DZOBXK2S.js +28 -0
  107. package/dist/chunk-DZOBXK2S.js.map +1 -0
  108. package/dist/chunk-EN4OJCEF.js +54 -0
  109. package/dist/chunk-EN4OJCEF.js.map +1 -0
  110. package/dist/chunk-EVHZFYWX.js +33 -0
  111. package/dist/chunk-EVHZFYWX.js.map +1 -0
  112. package/dist/chunk-G2VKWNZA.js +53 -0
  113. package/dist/chunk-G2VKWNZA.js.map +1 -0
  114. package/dist/chunk-GTM2DE4C.js +156 -0
  115. package/dist/chunk-GTM2DE4C.js.map +1 -0
  116. package/dist/chunk-H3ZVGTJM.js +165 -0
  117. package/dist/chunk-H3ZVGTJM.js.map +1 -0
  118. package/dist/chunk-HWAWUEHC.js +28 -0
  119. package/dist/chunk-HWAWUEHC.js.map +1 -0
  120. package/dist/chunk-HWJVRTWO.js +36 -0
  121. package/dist/chunk-HWJVRTWO.js.map +1 -0
  122. package/dist/chunk-ILADNTUB.js +77 -0
  123. package/dist/chunk-ILADNTUB.js.map +1 -0
  124. package/dist/chunk-IUPVQWO5.js +31 -0
  125. package/dist/chunk-IUPVQWO5.js.map +1 -0
  126. package/dist/chunk-JFOW2DI5.js +43 -0
  127. package/dist/chunk-JFOW2DI5.js.map +1 -0
  128. package/dist/chunk-JRFSUVSO.js +66 -0
  129. package/dist/chunk-JRFSUVSO.js.map +1 -0
  130. package/dist/chunk-KI6OTVID.js +91 -0
  131. package/dist/chunk-KI6OTVID.js.map +1 -0
  132. package/dist/chunk-LUM2YJBH.js +73 -0
  133. package/dist/chunk-LUM2YJBH.js.map +1 -0
  134. package/dist/chunk-NHPISZWS.js +71 -0
  135. package/dist/chunk-NHPISZWS.js.map +1 -0
  136. package/dist/chunk-NOLWJJHT.js +52 -0
  137. package/dist/chunk-NOLWJJHT.js.map +1 -0
  138. package/dist/chunk-NPVINX3Q.js +20 -0
  139. package/dist/chunk-NPVINX3Q.js.map +1 -0
  140. package/dist/chunk-NSMU66ZX.js +47 -0
  141. package/dist/chunk-NSMU66ZX.js.map +1 -0
  142. package/dist/chunk-O5SWPHUQ.js +79 -0
  143. package/dist/chunk-O5SWPHUQ.js.map +1 -0
  144. package/dist/chunk-OGVSZ7NV.js +53 -0
  145. package/dist/chunk-OGVSZ7NV.js.map +1 -0
  146. package/dist/chunk-OLHMMFQ7.js +43 -0
  147. package/dist/chunk-OLHMMFQ7.js.map +1 -0
  148. package/dist/chunk-ONMTHBZ4.js +54 -0
  149. package/dist/chunk-ONMTHBZ4.js.map +1 -0
  150. package/dist/chunk-OVSIOZHJ.js +56 -0
  151. package/dist/chunk-OVSIOZHJ.js.map +1 -0
  152. package/dist/chunk-Q54CVE3W.js +154 -0
  153. package/dist/chunk-Q54CVE3W.js.map +1 -0
  154. package/dist/chunk-QEHTPQHL.js +35 -0
  155. package/dist/chunk-QEHTPQHL.js.map +1 -0
  156. package/dist/chunk-QEIEWGHA.js +62 -0
  157. package/dist/chunk-QEIEWGHA.js.map +1 -0
  158. package/dist/chunk-QQHBEACI.js +88 -0
  159. package/dist/chunk-QQHBEACI.js.map +1 -0
  160. package/dist/chunk-RJTDQOT2.js +73 -0
  161. package/dist/chunk-RJTDQOT2.js.map +1 -0
  162. package/dist/chunk-RQ3RXKAZ.js +203 -0
  163. package/dist/chunk-RQ3RXKAZ.js.map +1 -0
  164. package/dist/chunk-RZK2S2OO.js +126 -0
  165. package/dist/chunk-RZK2S2OO.js.map +1 -0
  166. package/dist/chunk-SAP7JSSO.js +106 -0
  167. package/dist/chunk-SAP7JSSO.js.map +1 -0
  168. package/dist/chunk-T3TQPOVM.js +79 -0
  169. package/dist/chunk-T3TQPOVM.js.map +1 -0
  170. package/dist/chunk-T3UQ7G4T.js +58 -0
  171. package/dist/chunk-T3UQ7G4T.js.map +1 -0
  172. package/dist/chunk-T56TTOI6.js +53 -0
  173. package/dist/chunk-T56TTOI6.js.map +1 -0
  174. package/dist/chunk-T7EFDE2L.js +36 -0
  175. package/dist/chunk-T7EFDE2L.js.map +1 -0
  176. package/dist/chunk-VFS3V3VY.js +91 -0
  177. package/dist/chunk-VFS3V3VY.js.map +1 -0
  178. package/dist/chunk-VOARBYVQ.js +44 -0
  179. package/dist/chunk-VOARBYVQ.js.map +1 -0
  180. package/dist/chunk-WDANALHD.js +95 -0
  181. package/dist/chunk-WDANALHD.js.map +1 -0
  182. package/dist/chunk-WSQNPRGN.js +12 -0
  183. package/dist/chunk-WSQNPRGN.js.map +1 -0
  184. package/dist/chunk-YPP7QHYT.js +393 -0
  185. package/dist/chunk-YPP7QHYT.js.map +1 -0
  186. package/dist/chunk-YTU7BRDW.js +72 -0
  187. package/dist/chunk-YTU7BRDW.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/chunk-ZTFOR3AN.js +79 -0
  193. package/dist/chunk-ZTFOR3AN.js.map +1 -0
  194. package/dist/code-block.d.ts +14 -0
  195. package/dist/code-block.js +5 -0
  196. package/dist/code-block.js.map +1 -0
  197. package/dist/combobox.d.ts +26 -0
  198. package/dist/combobox.js +5 -0
  199. package/dist/combobox.js.map +1 -0
  200. package/dist/command-menu.d.ts +52 -0
  201. package/dist/command-menu.js +7 -0
  202. package/dist/command-menu.js.map +1 -0
  203. package/dist/container.d.ts +9 -0
  204. package/dist/container.js +5 -0
  205. package/dist/container.js.map +1 -0
  206. package/dist/cta-band.d.ts +12 -0
  207. package/dist/cta-band.js +5 -0
  208. package/dist/cta-band.js.map +1 -0
  209. package/dist/data-table.d.ts +58 -0
  210. package/dist/data-table.js +12 -0
  211. package/dist/data-table.js.map +1 -0
  212. package/dist/date-picker.d.ts +20 -0
  213. package/dist/date-picker.js +5 -0
  214. package/dist/date-picker.js.map +1 -0
  215. package/dist/dialog.d.ts +21 -0
  216. package/dist/dialog.js +5 -0
  217. package/dist/dialog.js.map +1 -0
  218. package/dist/divider.d.ts +12 -0
  219. package/dist/divider.js +5 -0
  220. package/dist/divider.js.map +1 -0
  221. package/dist/docker-stack-card.d.ts +21 -0
  222. package/dist/docker-stack-card.js +6 -0
  223. package/dist/docker-stack-card.js.map +1 -0
  224. package/dist/donut.d.ts +15 -0
  225. package/dist/donut.js +5 -0
  226. package/dist/donut.js.map +1 -0
  227. package/dist/dropdown-menu.d.ts +15 -0
  228. package/dist/dropdown-menu.js +5 -0
  229. package/dist/dropdown-menu.js.map +1 -0
  230. package/dist/empty.d.ts +12 -0
  231. package/dist/empty.js +5 -0
  232. package/dist/empty.js.map +1 -0
  233. package/dist/feature-card.d.ts +11 -0
  234. package/dist/feature-card.js +6 -0
  235. package/dist/feature-card.js.map +1 -0
  236. package/dist/file-upload.d.ts +20 -0
  237. package/dist/file-upload.js +5 -0
  238. package/dist/file-upload.js.map +1 -0
  239. package/dist/footer.d.ts +35 -0
  240. package/dist/footer.js +6 -0
  241. package/dist/footer.js.map +1 -0
  242. package/dist/form-row.d.ts +19 -0
  243. package/dist/form-row.js +5 -0
  244. package/dist/form-row.js.map +1 -0
  245. package/dist/glow-card.d.ts +14 -0
  246. package/dist/glow-card.js +5 -0
  247. package/dist/glow-card.js.map +1 -0
  248. package/dist/hero-block.d.ts +16 -0
  249. package/dist/hero-block.js +7 -0
  250. package/dist/hero-block.js.map +1 -0
  251. package/dist/host-card.d.ts +27 -0
  252. package/dist/host-card.js +6 -0
  253. package/dist/host-card.js.map +1 -0
  254. package/dist/incident-card.d.ts +23 -0
  255. package/dist/incident-card.js +5 -0
  256. package/dist/incident-card.js.map +1 -0
  257. package/dist/index.d.ts +77 -960
  258. package/dist/index.js +69 -3812
  259. package/dist/index.js.map +1 -1
  260. package/dist/input.d.ts +10 -0
  261. package/dist/input.js +5 -0
  262. package/dist/input.js.map +1 -0
  263. package/dist/kbd.d.ts +24 -0
  264. package/dist/kbd.js +5 -0
  265. package/dist/kbd.js.map +1 -0
  266. package/dist/log-viewer.d.ts +35 -0
  267. package/dist/log-viewer.js +5 -0
  268. package/dist/log-viewer.js.map +1 -0
  269. package/dist/meter.d.ts +23 -0
  270. package/dist/meter.js +5 -0
  271. package/dist/meter.js.map +1 -0
  272. package/dist/monogram.d.ts +20 -0
  273. package/dist/monogram.js +5 -0
  274. package/dist/monogram.js.map +1 -0
  275. package/dist/noir-glow-background.d.ts +56 -0
  276. package/dist/noir-glow-background.js +4 -0
  277. package/dist/noir-glow-background.js.map +1 -0
  278. package/dist/pagination.d.ts +16 -0
  279. package/dist/pagination.js +5 -0
  280. package/dist/pagination.js.map +1 -0
  281. package/dist/panel.d.ts +12 -0
  282. package/dist/panel.js +5 -0
  283. package/dist/panel.js.map +1 -0
  284. package/dist/password-input.d.ts +10 -0
  285. package/dist/password-input.js +5 -0
  286. package/dist/password-input.js.map +1 -0
  287. package/dist/pill.d.ts +17 -0
  288. package/dist/pill.js +5 -0
  289. package/dist/pill.js.map +1 -0
  290. package/dist/pricing-card.d.ts +20 -0
  291. package/dist/pricing-card.js +6 -0
  292. package/dist/pricing-card.js.map +1 -0
  293. package/dist/progress.d.ts +6 -0
  294. package/dist/progress.js +5 -0
  295. package/dist/progress.js.map +1 -0
  296. package/dist/radio-group.d.ts +18 -0
  297. package/dist/radio-group.js +5 -0
  298. package/dist/radio-group.js.map +1 -0
  299. package/dist/rollback-plan.d.ts +23 -0
  300. package/dist/rollback-plan.js +5 -0
  301. package/dist/rollback-plan.js.map +1 -0
  302. package/dist/segmented.d.ts +17 -0
  303. package/dist/segmented.js +5 -0
  304. package/dist/segmented.js.map +1 -0
  305. package/dist/select.d.ts +18 -0
  306. package/dist/select.js +5 -0
  307. package/dist/select.js.map +1 -0
  308. package/dist/service-health-card.d.ts +21 -0
  309. package/dist/service-health-card.js +6 -0
  310. package/dist/service-health-card.js.map +1 -0
  311. package/dist/sheet.d.ts +25 -0
  312. package/dist/sheet.js +5 -0
  313. package/dist/sheet.js.map +1 -0
  314. package/dist/skeleton.d.ts +13 -0
  315. package/dist/skeleton.js +5 -0
  316. package/dist/skeleton.js.map +1 -0
  317. package/dist/slider.d.ts +12 -0
  318. package/dist/slider.js +5 -0
  319. package/dist/slider.js.map +1 -0
  320. package/dist/sparkline.d.ts +16 -0
  321. package/dist/sparkline.js +5 -0
  322. package/dist/sparkline.js.map +1 -0
  323. package/dist/stat-card.d.ts +15 -0
  324. package/dist/stat-card.js +5 -0
  325. package/dist/stat-card.js.map +1 -0
  326. package/dist/status-dot.d.ts +13 -0
  327. package/dist/status-dot.js +5 -0
  328. package/dist/status-dot.js.map +1 -0
  329. package/dist/stepper.d.ts +16 -0
  330. package/dist/stepper.js +5 -0
  331. package/dist/stepper.js.map +1 -0
  332. package/dist/switch.d.ts +9 -0
  333. package/dist/switch.js +5 -0
  334. package/dist/switch.js.map +1 -0
  335. package/dist/tabs.d.ts +9 -0
  336. package/dist/tabs.js +5 -0
  337. package/dist/tabs.js.map +1 -0
  338. package/dist/tag-input.d.ts +20 -0
  339. package/dist/tag-input.js +5 -0
  340. package/dist/tag-input.js.map +1 -0
  341. package/dist/textarea.d.ts +10 -0
  342. package/dist/textarea.js +5 -0
  343. package/dist/textarea.js.map +1 -0
  344. package/dist/timeline.d.ts +30 -0
  345. package/dist/timeline.js +5 -0
  346. package/dist/timeline.js.map +1 -0
  347. package/dist/toaster.d.ts +10 -0
  348. package/dist/toaster.js +4 -0
  349. package/dist/toaster.js.map +1 -0
  350. package/dist/tooltip.d.ts +12 -0
  351. package/dist/tooltip.js +5 -0
  352. package/dist/tooltip.js.map +1 -0
  353. package/dist/utils.d.ts +5 -0
  354. package/dist/utils.js +4 -0
  355. package/dist/utils.js.map +1 -0
  356. package/globals-layers.css +1019 -0
  357. package/globals-v3.css +17 -0
  358. package/globals-v4.css +2 -0
  359. package/globals.css +12 -939
  360. package/package.json +85 -17
  361. package/registry.json +936 -0
  362. package/tailwind.css +9 -0
  363. package/tokens.css +20 -0
  364. package/tollerud-avatar-full.png +0 -0
  365. package/dist/index.cjs +0 -3938
  366. package/dist/index.cjs.map +0 -1
  367. package/dist/index.d.cts +0 -960
  368. /package/{tia-full-figure.svg → tollerud-avatar-full.svg} +0 -0
@@ -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":"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","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 }"]}
@@ -0,0 +1,36 @@
1
+ 'use client';
2
+ import { cn } from './chunk-WSQNPRGN.js';
3
+ import { forwardRef } from 'react';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+
6
+ var Textarea = forwardRef(
7
+ ({ className, label, error, id, ...props }, ref) => {
8
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
9
+ label && /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-xs font-medium text-tollerud-text-muted", children: label }),
10
+ /* @__PURE__ */ jsx(
11
+ "textarea",
12
+ {
13
+ ref,
14
+ id,
15
+ className: cn(
16
+ "font-sans text-base px-3 py-2 rounded min-h-[80px] resize-y",
17
+ "bg-tollerud-surface-raised border",
18
+ "text-tollerud-text-primary",
19
+ "placeholder:text-tollerud-text-muted",
20
+ "transition-[border-color] duration-[150ms]",
21
+ "focus:outline-none focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_#E8D500]",
22
+ error ? "border-tollerud-error" : "border-tollerud-border",
23
+ className
24
+ ),
25
+ ...props
26
+ }
27
+ ),
28
+ error && /* @__PURE__ */ jsx("p", { className: "text-xs text-tollerud-error mt-0.5", children: error })
29
+ ] });
30
+ }
31
+ );
32
+ Textarea.displayName = "Textarea";
33
+
34
+ export { Textarea };
35
+ //# sourceMappingURL=chunk-HWJVRTWO.js.map
36
+ //# sourceMappingURL=chunk-HWJVRTWO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../components/Textarea.tsx"],"names":[],"mappings":";;;;AAQA,IAAM,QAAA,GAAW,UAAA;AAAA,EACf,CAAC,EAAE,SAAA,EAAW,KAAA,EAAO,OAAO,EAAA,EAAI,GAAG,KAAA,EAAM,EAAG,GAAA,KAAQ;AAClD,IAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qBAAA,EACZ,QAAA,EAAA;AAAA,MAAA,KAAA,wBACE,OAAA,EAAA,EAAM,OAAA,EAAS,EAAA,EAAI,SAAA,EAAU,gDAC3B,QAAA,EAAA,KAAA,EACH,CAAA;AAAA,sBAEF,GAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,GAAA;AAAA,UACA,EAAA;AAAA,UACA,SAAA,EAAW,EAAA;AAAA,YACT,6DAAA;AAAA,YACA,mCAAA;AAAA,YACA,4BAAA;AAAA,YACA,sCAAA;AAAA,YACA,4CAAA;AAAA,YACA,kFAAA;AAAA,YACA,QAAQ,uBAAA,GAA0B,wBAAA;AAAA,YAClC;AAAA,WACF;AAAA,UACC,GAAG;AAAA;AAAA,OACN;AAAA,MACC,KAAA,oBACC,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,sCAAsC,QAAA,EAAA,KAAA,EAAM;AAAA,KAAA,EAE7D,CAAA;AAAA,EAEJ;AACF;AACA,QAAA,CAAS,WAAA,GAAc,UAAA","file":"chunk-HWJVRTWO.js","sourcesContent":["import { type TextareaHTMLAttributes, forwardRef } from 'react'\nimport { cn } from '@/lib/utils'\n\nexport interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {\n label?: string\n error?: string\n}\n\nconst Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(\n ({ className, label, error, id, ...props }, ref) => {\n return (\n <div className=\"flex flex-col gap-1\">\n {label && (\n <label htmlFor={id} className=\"text-xs font-medium text-tollerud-text-muted\">\n {label}\n </label>\n )}\n <textarea\n ref={ref}\n id={id}\n className={cn(\n 'font-sans text-base px-3 py-2 rounded min-h-[80px] resize-y',\n 'bg-tollerud-surface-raised border',\n 'text-tollerud-text-primary',\n '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 className\n )}\n {...props}\n />\n {error && (\n <p className=\"text-xs text-tollerud-error mt-0.5\">{error}</p>\n )}\n </div>\n )\n }\n)\nTextarea.displayName = 'Textarea'\n\nexport { Textarea }"]}
@@ -0,0 +1,77 @@
1
+ 'use client';
2
+ import { cn } from './chunk-WSQNPRGN.js';
3
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
4
+ import { forwardRef } from 'react';
5
+ import { jsx, jsxs } from 'react/jsx-runtime';
6
+
7
+ var Sheet = ({ open, onOpenChange, children }) => /* @__PURE__ */ jsx(DialogPrimitive.Root, { open, onOpenChange, children });
8
+ var SheetTrigger = DialogPrimitive.Trigger;
9
+ var SheetClose = DialogPrimitive.Close;
10
+ var SheetOverlay = forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
11
+ DialogPrimitive.Overlay,
12
+ {
13
+ ref,
14
+ className: cn(
15
+ "fixed inset-0 z-50 bg-black/60 backdrop-blur-sm",
16
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
17
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
18
+ className
19
+ ),
20
+ ...props
21
+ }
22
+ ));
23
+ SheetOverlay.displayName = "SheetOverlay";
24
+ var SheetContent = forwardRef(({ className, children, side = "right", ...props }, ref) => /* @__PURE__ */ jsx(SheetOverlay, { children: /* @__PURE__ */ jsxs(
25
+ DialogPrimitive.Content,
26
+ {
27
+ ref,
28
+ className: cn(
29
+ "fixed z-50 gap-4 bg-tollerud-noir-900 border-tollerud-border/30 p-6 shadow-lg",
30
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
31
+ side === "right" && [
32
+ "inset-y-0 right-0 h-full w-full max-w-md border-l",
33
+ "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right"
34
+ ],
35
+ side === "left" && [
36
+ "inset-y-0 left-0 h-full w-full max-w-md border-r",
37
+ "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left"
38
+ ],
39
+ className
40
+ ),
41
+ ...props,
42
+ children: [
43
+ children,
44
+ /* @__PURE__ */ jsxs(SheetClose, { className: "absolute right-4 top-4 rounded-sm opacity-70 hover:opacity-100 transition-opacity text-tollerud-text-muted hover:text-tollerud-text-primary", children: [
45
+ /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
46
+ /* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
47
+ /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
48
+ ] }),
49
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
50
+ ] })
51
+ ]
52
+ }
53
+ ) }));
54
+ SheetContent.displayName = "SheetContent";
55
+ var SheetHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-1.5 text-left", className), ...props });
56
+ var SheetTitle = forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
57
+ DialogPrimitive.Title,
58
+ {
59
+ ref,
60
+ className: cn("text-base font-semibold text-tollerud-text-primary", className),
61
+ ...props
62
+ }
63
+ ));
64
+ SheetTitle.displayName = "SheetTitle";
65
+ var SheetDescription = forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
66
+ DialogPrimitive.Description,
67
+ {
68
+ ref,
69
+ className: cn("text-sm text-tollerud-text-secondary", className),
70
+ ...props
71
+ }
72
+ ));
73
+ SheetDescription.displayName = "SheetDescription";
74
+
75
+ export { Sheet, SheetClose, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger };
76
+ //# sourceMappingURL=chunk-ILADNTUB.js.map
77
+ //# sourceMappingURL=chunk-ILADNTUB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../components/Sheet.tsx"],"names":[],"mappings":"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","file":"chunk-ILADNTUB.js","sourcesContent":["'use client'\n\nimport * as DialogPrimitive from '@radix-ui/react-dialog'\nimport { type ComponentPropsWithoutRef, type ReactNode, forwardRef } from 'react'\nimport { cn } from '@/lib/utils'\n\n/* ──────────────────── Sheet (slide-in panel) ──────────────────── */\n\nexport type SheetSide = 'left' | 'right'\n\nexport interface SheetProps {\n open?: boolean\n onOpenChange?: (open: boolean) => void\n children: ReactNode\n}\n\nconst Sheet = ({ open, onOpenChange, children }: SheetProps) => (\n <DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>\n {children}\n </DialogPrimitive.Root>\n)\n\nconst SheetTrigger = DialogPrimitive.Trigger\n\nconst SheetClose = DialogPrimitive.Close\n\ninterface SheetContentProps extends ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {\n side?: SheetSide\n}\n\nconst SheetOverlay = forwardRef<\n React.ComponentRef<typeof DialogPrimitive.Overlay>,\n ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\n 'fixed inset-0 z-50 bg-black/60 backdrop-blur-sm',\n 'data-[state=open]:animate-in data-[state=closed]:animate-out',\n 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',\n className\n )}\n {...props}\n />\n))\nSheetOverlay.displayName = 'SheetOverlay'\n\nconst SheetContent = forwardRef<\n React.ComponentRef<typeof DialogPrimitive.Content>,\n SheetContentProps\n>(({ className, children, side = 'right', ...props }, ref) => (\n <SheetOverlay>\n <DialogPrimitive.Content\n ref={ref}\n className={cn(\n 'fixed z-50 gap-4 bg-tollerud-noir-900 border-tollerud-border/30 p-6 shadow-lg',\n 'data-[state=open]:animate-in data-[state=closed]:animate-out',\n side === 'right' && [\n 'inset-y-0 right-0 h-full w-full max-w-md border-l',\n 'data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right',\n ],\n side === 'left' && [\n 'inset-y-0 left-0 h-full w-full max-w-md border-r',\n 'data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left',\n ],\n className\n )}\n {...props}\n >\n {children}\n <SheetClose className=\"absolute right-4 top-4 rounded-sm opacity-70 hover:opacity-100 transition-opacity text-tollerud-text-muted hover:text-tollerud-text-primary\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n <span className=\"sr-only\">Close</span>\n </SheetClose>\n </DialogPrimitive.Content>\n </SheetOverlay>\n))\nSheetContent.displayName = 'SheetContent'\n\nconst SheetHeader = ({ className, ...props }: { className?: string; children?: ReactNode }) => (\n <div className={cn('flex flex-col space-y-1.5 text-left', className)} {...props} />\n)\n\nconst SheetTitle = forwardRef<\n React.ComponentRef<typeof DialogPrimitive.Title>,\n ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn('text-base font-semibold text-tollerud-text-primary', className)}\n {...props}\n />\n))\nSheetTitle.displayName = 'SheetTitle'\n\nconst SheetDescription = forwardRef<\n React.ComponentRef<typeof DialogPrimitive.Description>,\n ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn('text-sm text-tollerud-text-secondary', className)}\n {...props}\n />\n))\nSheetDescription.displayName = 'SheetDescription'\n\nexport {\n Sheet,\n SheetTrigger,\n SheetClose,\n SheetContent,\n SheetHeader,\n SheetTitle,\n SheetDescription,\n}"]}
@@ -0,0 +1,31 @@
1
+ 'use client';
2
+ import { cn } from './chunk-WSQNPRGN.js';
3
+ import { forwardRef } from 'react';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+
6
+ var CTABand = forwardRef(
7
+ ({ className, title, description, actions, accentBar = true, ...props }, ref) => {
8
+ return /* @__PURE__ */ jsxs(
9
+ "div",
10
+ {
11
+ ref,
12
+ className: cn(
13
+ "tollerud-card rounded-xl border border-tollerud-border bg-tollerud-surface px-8 py-11 text-center",
14
+ className
15
+ ),
16
+ ...props,
17
+ children: [
18
+ /* @__PURE__ */ jsx("h2", { className: "tollerud-display text-[30px] text-tollerud-text-primary", children: title }),
19
+ description && /* @__PURE__ */ jsx("p", { className: "mx-auto mt-3 max-w-[440px] text-[15px] text-tollerud-text-secondary", children: description }),
20
+ actions && /* @__PURE__ */ jsx("div", { className: "mt-[22px] flex flex-wrap items-center justify-center gap-3", children: actions }),
21
+ accentBar && /* @__PURE__ */ jsx("hr", { className: "tollerud-accent-bar tollerud-accent-bar--inline mx-auto mt-8 max-w-[320px] border-0" })
22
+ ]
23
+ }
24
+ );
25
+ }
26
+ );
27
+ CTABand.displayName = "CTABand";
28
+
29
+ export { CTABand };
30
+ //# sourceMappingURL=chunk-IUPVQWO5.js.map
31
+ //# sourceMappingURL=chunk-IUPVQWO5.js.map