@hoardodile/ui 0.1.6 → 0.1.8

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 (273) hide show
  1. package/dist/chunk-2BVZEYNW.js +97 -0
  2. package/dist/chunk-2BVZEYNW.js.map +1 -0
  3. package/dist/chunk-2DTR6CD5.js +47 -0
  4. package/dist/chunk-2DTR6CD5.js.map +1 -0
  5. package/dist/chunk-2HWSCY4T.js +20 -0
  6. package/dist/chunk-2HWSCY4T.js.map +1 -0
  7. package/dist/chunk-363CCKQ7.js +75 -0
  8. package/dist/chunk-363CCKQ7.js.map +1 -0
  9. package/dist/chunk-3ZQJWBVB.js +30 -0
  10. package/dist/chunk-3ZQJWBVB.js.map +1 -0
  11. package/dist/chunk-4D36OO5W.js +277 -0
  12. package/dist/chunk-4D36OO5W.js.map +1 -0
  13. package/dist/chunk-4UITFLIN.js +31 -0
  14. package/dist/chunk-4UITFLIN.js.map +1 -0
  15. package/dist/chunk-6HAIUXNZ.js +11 -0
  16. package/dist/chunk-6HAIUXNZ.js.map +1 -0
  17. package/dist/chunk-7JMMFY67.js +62 -0
  18. package/dist/chunk-7JMMFY67.js.map +1 -0
  19. package/dist/chunk-7MWWHI6T.js +1149 -0
  20. package/dist/chunk-7MWWHI6T.js.map +1 -0
  21. package/dist/chunk-AEHTAUAQ.js +65 -0
  22. package/dist/chunk-AEHTAUAQ.js.map +1 -0
  23. package/dist/chunk-ASRGQ5BD.js +130 -0
  24. package/dist/chunk-ASRGQ5BD.js.map +1 -0
  25. package/dist/chunk-BFGOE5AI.js +187 -0
  26. package/dist/chunk-BFGOE5AI.js.map +1 -0
  27. package/dist/chunk-BRT25IDQ.js +46 -0
  28. package/dist/chunk-BRT25IDQ.js.map +1 -0
  29. package/dist/chunk-BU5DQOPM.js +29 -0
  30. package/dist/chunk-BU5DQOPM.js.map +1 -0
  31. package/dist/chunk-BWASOT5X.js +266 -0
  32. package/dist/chunk-BWASOT5X.js.map +1 -0
  33. package/dist/chunk-F32V4Z7Q.js +26 -0
  34. package/dist/chunk-F32V4Z7Q.js.map +1 -0
  35. package/dist/chunk-F7RHDRRJ.js +24 -0
  36. package/dist/chunk-F7RHDRRJ.js.map +1 -0
  37. package/dist/chunk-FH5RNCFR.js +21 -0
  38. package/dist/chunk-FH5RNCFR.js.map +1 -0
  39. package/dist/chunk-GKSX5ULZ.js +513 -0
  40. package/dist/chunk-GKSX5ULZ.js.map +1 -0
  41. package/dist/chunk-JPFDNRCA.js +26 -0
  42. package/dist/chunk-JPFDNRCA.js.map +1 -0
  43. package/dist/chunk-KNPRUZ4H.js +13 -0
  44. package/dist/chunk-KNPRUZ4H.js.map +1 -0
  45. package/dist/chunk-KOFWEPPB.js +254 -0
  46. package/dist/chunk-KOFWEPPB.js.map +1 -0
  47. package/dist/chunk-M5OJN54O.js +10 -0
  48. package/dist/chunk-M5OJN54O.js.map +1 -0
  49. package/dist/chunk-OEVALKQ5.js +51 -0
  50. package/dist/chunk-OEVALKQ5.js.map +1 -0
  51. package/dist/chunk-OW5FWLPH.js +33 -0
  52. package/dist/chunk-OW5FWLPH.js.map +1 -0
  53. package/dist/chunk-POX7TXBH.js +41 -0
  54. package/dist/chunk-POX7TXBH.js.map +1 -0
  55. package/dist/chunk-PWJBHUM2.js +58 -0
  56. package/dist/chunk-PWJBHUM2.js.map +1 -0
  57. package/dist/chunk-QGMDWGJT.js +23 -0
  58. package/dist/chunk-QGMDWGJT.js.map +1 -0
  59. package/dist/chunk-QGTQQNUL.js +36 -0
  60. package/dist/chunk-QGTQQNUL.js.map +1 -0
  61. package/dist/chunk-SAY52ZKI.js +152 -0
  62. package/dist/chunk-SAY52ZKI.js.map +1 -0
  63. package/dist/chunk-SKDPGSRL.js +166 -0
  64. package/dist/chunk-SKDPGSRL.js.map +1 -0
  65. package/dist/chunk-T56ARCUK.js +38 -0
  66. package/dist/chunk-T56ARCUK.js.map +1 -0
  67. package/dist/chunk-TDDHQZ62.js +227 -0
  68. package/dist/chunk-TDDHQZ62.js.map +1 -0
  69. package/dist/chunk-TMQ5OMBK.js +29 -0
  70. package/dist/chunk-TMQ5OMBK.js.map +1 -0
  71. package/dist/chunk-UEVND7AS.js +23 -0
  72. package/dist/chunk-UEVND7AS.js.map +1 -0
  73. package/dist/chunk-UPWHCG4S.js +40 -0
  74. package/dist/chunk-UPWHCG4S.js.map +1 -0
  75. package/dist/chunk-VWUNF7VX.js +3 -0
  76. package/dist/chunk-VWUNF7VX.js.map +1 -0
  77. package/dist/chunk-WI6O7GZK.js +45 -0
  78. package/dist/chunk-WI6O7GZK.js.map +1 -0
  79. package/dist/chunk-WVEK3WCX.js +21 -0
  80. package/dist/chunk-WVEK3WCX.js.map +1 -0
  81. package/dist/chunk-XBPQEZES.js +48 -0
  82. package/dist/chunk-XBPQEZES.js.map +1 -0
  83. package/dist/chunk-ZYAGKKR7.js +142 -0
  84. package/dist/chunk-ZYAGKKR7.js.map +1 -0
  85. package/dist/components/alert.js +2 -21
  86. package/dist/components/alert.js.map +1 -1
  87. package/dist/components/app-dialog.js +10 -654
  88. package/dist/components/app-dialog.js.map +1 -1
  89. package/dist/components/badge.js +2 -23
  90. package/dist/components/badge.js.map +1 -1
  91. package/dist/components/breadcrumb.js +5 -501
  92. package/dist/components/breadcrumb.js.map +1 -1
  93. package/dist/components/button.js +3 -76
  94. package/dist/components/button.js.map +1 -1
  95. package/dist/components/caption-bar.js +7 -518
  96. package/dist/components/caption-bar.js.map +1 -1
  97. package/dist/components/checkbox.js +4 -74
  98. package/dist/components/checkbox.js.map +1 -1
  99. package/dist/components/chip-button.js +4 -85
  100. package/dist/components/chip-button.js.map +1 -1
  101. package/dist/components/close-confirm-dialog.js +12 -662
  102. package/dist/components/close-confirm-dialog.js.map +1 -1
  103. package/dist/components/color-picker.js +7 -530
  104. package/dist/components/color-picker.js.map +1 -1
  105. package/dist/components/combobox.js +15 -934
  106. package/dist/components/combobox.js.map +1 -1
  107. package/dist/components/confirm-by-typing-dialog.js +12 -669
  108. package/dist/components/confirm-by-typing-dialog.js.map +1 -1
  109. package/dist/components/confirm-dialog.js +11 -637
  110. package/dist/components/confirm-dialog.js.map +1 -1
  111. package/dist/components/count-badge.js +2 -21
  112. package/dist/components/count-badge.js.map +1 -1
  113. package/dist/components/dialog.js +8 -569
  114. package/dist/components/dialog.js.map +1 -1
  115. package/dist/components/dropdown-menu.js +9 -1004
  116. package/dist/components/dropdown-menu.js.map +1 -1
  117. package/dist/components/dropdown-select.js +9 -880
  118. package/dist/components/dropdown-select.js.map +1 -1
  119. package/dist/components/empty.js +2 -21
  120. package/dist/components/empty.js.map +1 -1
  121. package/dist/components/filter-rail.js +7 -145
  122. package/dist/components/filter-rail.js.map +1 -1
  123. package/dist/components/fixed-action-bar.js +2 -21
  124. package/dist/components/fixed-action-bar.js.map +1 -1
  125. package/dist/components/font-picker.js +11 -586
  126. package/dist/components/font-picker.js.map +1 -1
  127. package/dist/components/form.js +9 -66
  128. package/dist/components/form.js.map +1 -1
  129. package/dist/components/icon-tile.js +3 -43
  130. package/dist/components/icon-tile.js.map +1 -1
  131. package/dist/components/icon-toggle.js +3 -43
  132. package/dist/components/icon-toggle.js.map +1 -1
  133. package/dist/components/icon.js +3 -47
  134. package/dist/components/icon.js.map +1 -1
  135. package/dist/components/image-crop-panel.js +19 -772
  136. package/dist/components/image-crop-panel.js.map +1 -1
  137. package/dist/components/image-cropper.js +1 -164
  138. package/dist/components/image-cropper.js.map +1 -1
  139. package/dist/components/input-group.js +6 -268
  140. package/dist/components/input-group.js.map +1 -1
  141. package/dist/components/input.js +3 -58
  142. package/dist/components/input.js.map +1 -1
  143. package/dist/components/label.js +3 -38
  144. package/dist/components/label.js.map +1 -1
  145. package/dist/components/list-empty-row.js +2 -21
  146. package/dist/components/list-empty-row.js.map +1 -1
  147. package/dist/components/management-empty.js +2 -21
  148. package/dist/components/management-empty.js.map +1 -1
  149. package/dist/components/management-skeleton.js +4 -59
  150. package/dist/components/management-skeleton.js.map +1 -1
  151. package/dist/components/meta-chip.js +2 -21
  152. package/dist/components/meta-chip.js.map +1 -1
  153. package/dist/components/mobile-drawer.js +7 -268
  154. package/dist/components/mobile-drawer.js.map +1 -1
  155. package/dist/components/page-header.js +3 -42
  156. package/dist/components/page-header.js.map +1 -1
  157. package/dist/components/page-scaffold.js +5 -76
  158. package/dist/components/page-scaffold.js.map +1 -1
  159. package/dist/components/pagination-bar.js +13 -614
  160. package/dist/components/pagination-bar.js.map +1 -1
  161. package/dist/components/pagination.js +6 -681
  162. package/dist/components/pagination.js.map +1 -1
  163. package/dist/components/panel-toolbar.js +11 -713
  164. package/dist/components/panel-toolbar.js.map +1 -1
  165. package/dist/components/pill-tabs.js +2 -21
  166. package/dist/components/pill-tabs.js.map +1 -1
  167. package/dist/components/plugin-download-consent.js +13 -663
  168. package/dist/components/plugin-download-consent.js.map +1 -1
  169. package/dist/components/popover.js +5 -242
  170. package/dist/components/popover.js.map +1 -1
  171. package/dist/components/preview-card.js +2 -21
  172. package/dist/components/preview-card.js.map +1 -1
  173. package/dist/components/progress.js +2 -20
  174. package/dist/components/progress.js.map +1 -1
  175. package/dist/components/radio-group.js +2 -21
  176. package/dist/components/radio-group.js.map +1 -1
  177. package/dist/components/scroll-area.js +2 -21
  178. package/dist/components/scroll-area.js.map +1 -1
  179. package/dist/components/search-field.js +6 -587
  180. package/dist/components/search-field.js.map +1 -1
  181. package/dist/components/secondary-stat.js +3 -40
  182. package/dist/components/secondary-stat.js.map +1 -1
  183. package/dist/components/section-header.js +3 -43
  184. package/dist/components/section-header.js.map +1 -1
  185. package/dist/components/section-label.js +3 -44
  186. package/dist/components/section-label.js.map +1 -1
  187. package/dist/components/section-tabs.js +2 -21
  188. package/dist/components/section-tabs.js.map +1 -1
  189. package/dist/components/separator.js +3 -47
  190. package/dist/components/separator.js.map +1 -1
  191. package/dist/components/sheet.js +8 -467
  192. package/dist/components/sheet.js.map +1 -1
  193. package/dist/components/sidebar.js +24 -1044
  194. package/dist/components/sidebar.js.map +1 -1
  195. package/dist/components/skeleton.js +3 -39
  196. package/dist/components/skeleton.js.map +1 -1
  197. package/dist/components/slider.js +2 -21
  198. package/dist/components/slider.js.map +1 -1
  199. package/dist/components/sortable-chip-list.js +2 -21
  200. package/dist/components/sortable-chip-list.js.map +1 -1
  201. package/dist/components/special-tag-surface.js +3 -531
  202. package/dist/components/special-tag-surface.js.map +1 -1
  203. package/dist/components/spinner.js +3 -63
  204. package/dist/components/spinner.js.map +1 -1
  205. package/dist/components/surface.js +3 -49
  206. package/dist/components/surface.js.map +1 -1
  207. package/dist/components/switch.js +3 -51
  208. package/dist/components/switch.js.map +1 -1
  209. package/dist/components/table.js +2 -20
  210. package/dist/components/table.js.map +1 -1
  211. package/dist/components/tag-chip.js +6 -617
  212. package/dist/components/tag-chip.js.map +1 -1
  213. package/dist/components/textarea.js +3 -56
  214. package/dist/components/textarea.js.map +1 -1
  215. package/dist/components/toast.js +8 -806
  216. package/dist/components/toast.js.map +1 -1
  217. package/dist/components/toggle.js +2 -20
  218. package/dist/components/toggle.js.map +1 -1
  219. package/dist/components/tooltip.js +3 -80
  220. package/dist/components/tooltip.js.map +1 -1
  221. package/dist/hooks/use-dialog-footer-actions.js +1 -9
  222. package/dist/hooks/use-dialog-footer-actions.js.map +1 -1
  223. package/dist/hooks/use-form-field.js +1 -34
  224. package/dist/hooks/use-form-field.js.map +1 -1
  225. package/dist/hooks/use-mobile.js +2 -27
  226. package/dist/hooks/use-mobile.js.map +1 -1
  227. package/dist/hooks/useMobileBackToClose.js +3 -232
  228. package/dist/hooks/useMobileBackToClose.js.map +1 -1
  229. package/dist/icons/actions.js +4 -521
  230. package/dist/icons/actions.js.map +1 -1
  231. package/dist/icons/icon-style.js +1 -49
  232. package/dist/icons/icon-style.js.map +1 -1
  233. package/dist/icons/marks.js +1 -46
  234. package/dist/icons/marks.js.map +1 -1
  235. package/dist/icons/registry.d.ts +2 -1
  236. package/dist/icons/registry.js +2 -1186
  237. package/dist/icons/registry.js.map +1 -1
  238. package/dist/index.js +10 -651
  239. package/dist/index.js.map +1 -1
  240. package/dist/lib/colors.js +1 -63
  241. package/dist/lib/colors.js.map +1 -1
  242. package/dist/lib/merge-config.js +1 -18
  243. package/dist/lib/merge-config.js.map +1 -1
  244. package/dist/lib/pagination.js +1 -24
  245. package/dist/lib/pagination.js.map +1 -1
  246. package/dist/lib/tag-surface.js +5 -613
  247. package/dist/lib/tag-surface.js.map +1 -1
  248. package/dist/lib/utils.js +2 -24
  249. package/dist/lib/utils.js.map +1 -1
  250. package/dist/res-card-template/format.d.ts +21 -0
  251. package/dist/res-card-template/format.js +3 -0
  252. package/dist/res-card-template/format.js.map +1 -0
  253. package/dist/res-card-template/icon.d.ts +38 -0
  254. package/dist/res-card-template/icon.js +3 -0
  255. package/dist/res-card-template/icon.js.map +1 -0
  256. package/dist/res-card-template/index.d.ts +4 -0
  257. package/dist/res-card-template/index.js +5 -0
  258. package/dist/res-card-template/index.js.map +1 -0
  259. package/dist/res-card-template/template.d.ts +48 -0
  260. package/dist/res-card-template/template.js +5 -0
  261. package/dist/res-card-template/template.js.map +1 -0
  262. package/dist/viewport.js +1 -11
  263. package/dist/viewport.js.map +1 -1
  264. package/package.json +9 -2
  265. package/src/components/image-crop-panel.dist.test.tsx +54 -0
  266. package/src/icons/registry.ts +8 -0
  267. package/src/res-card-template/format.test.ts +45 -0
  268. package/src/res-card-template/format.ts +36 -0
  269. package/src/res-card-template/icon.test.ts +100 -0
  270. package/src/res-card-template/icon.ts +86 -0
  271. package/src/res-card-template/index.ts +22 -0
  272. package/src/res-card-template/template.test.ts +457 -0
  273. package/src/res-card-template/template.ts +429 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/pagination.ts"],"names":[],"mappings":";AAIO,SAAS,WAAA,CAAY,OAAe,QAAA,EAA0B;AACpE,EAAA,OAAO,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,IAAA,CAAK,KAAA,GAAQ,QAAQ,CAAC,CAAA;AAC/C;AAaO,SAAS,iBACf,IAAA,EACA,SAAA,EACA,MAAA,GAAS,CAAA,EACT,QAAQ,CAAA,EACoB;AAC5B,EAAA,MAAM,WAAA,GAAc,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,OAAO,MAAM,CAAA;AAC7C,EAAA,MAAM,YAAY,IAAA,CAAK,GAAA,CAAI,SAAA,GAAY,CAAA,EAAG,OAAO,KAAK,CAAA;AACtD,EAAA,MAAM,KAAA,GAA0B,CAAC,CAAC,CAAA;AAClC,EAAA,IAAI,cAAc,CAAA,EAAG;AACpB,IAAA,KAAA,CAAM,KAAK,QAAG,CAAA;AAAA,EACf;AACA,EAAA,KAAA,IAAS,CAAA,GAAI,WAAA,EAAa,CAAA,IAAK,SAAA,EAAW,CAAA,EAAA,EAAK;AAC9C,IAAA,KAAA,CAAM,KAAK,CAAC,CAAA;AAAA,EACb;AACA,EAAA,IAAI,SAAA,GAAY,YAAY,CAAA,EAAG;AAC9B,IAAA,KAAA,CAAM,KAAK,QAAG,CAAA;AAAA,EACf;AACA,EAAA,IAAI,YAAY,CAAA,EAAG;AAClB,IAAA,KAAA,CAAM,KAAK,SAAS,CAAA;AAAA,EACrB;AACA,EAAA,OAAO,KAAA;AACR","file":"chunk-JPFDNRCA.js","sourcesContent":["/**\n * Compute the number of pages for a paginated list given a total row count\n * and the page size. Always at least 1 so the UI never renders \"1 / 0\".\n */\nexport function pageCountOf(total: number, pageSize: number): number {\n\treturn Math.max(1, Math.ceil(total / pageSize))\n}\n\n/**\n * The page numbers a windowed pager renders: the first and last pages are\n * always shown, with an asymmetric window around the active one (more\n * pages trailing than leading) and an ellipsis wherever the gap between\n * two segments is wider than a single page (adjacent numbers render\n * directly, no ellipsis).\n *\n * @example\n * paginationWindow(6, 68) // [1, \"…\", 5, 6, 7, 8, \"…\", 68]\n * paginationWindow(1, 5) // [1, 2, 3, 4, 5]\n */\nexport function paginationWindow(\n\tpage: number,\n\tpageCount: number,\n\tbefore = 1,\n\tafter = 2,\n): readonly (number | \"…\")[] {\n\tconst windowStart = Math.max(2, page - before)\n\tconst windowEnd = Math.min(pageCount - 1, page + after)\n\tconst items: (number | \"…\")[] = [1]\n\tif (windowStart > 2) {\n\t\titems.push(\"…\")\n\t}\n\tfor (let p = windowStart; p <= windowEnd; p++) {\n\t\titems.push(p)\n\t}\n\tif (windowEnd < pageCount - 1) {\n\t\titems.push(\"…\")\n\t}\n\tif (pageCount > 1) {\n\t\titems.push(pageCount)\n\t}\n\treturn items\n}\n"]}
@@ -0,0 +1,13 @@
1
+ // src/viewport.ts
2
+ var MOBILE_INITIAL_SCALE = 0.8;
3
+ var MOBILE_BREAKPOINT_PX = 768;
4
+ var MOBILE_BREAKPOINT_REM = MOBILE_BREAKPOINT_PX / 16;
5
+ var MOBILE_QUERY = `(max-width: ${MOBILE_BREAKPOINT_PX - 1}px)`;
6
+ var SIDEBAR_BREAKPOINT_PX = 1150;
7
+ var SIDEBAR_QUERY = `(max-width: ${SIDEBAR_BREAKPOINT_PX - 1}px)`;
8
+ var PANEL_BREAKPOINT_PX = 1440;
9
+ var PANEL_QUERY = `(max-width: ${PANEL_BREAKPOINT_PX - 1}px)`;
10
+
11
+ export { MOBILE_BREAKPOINT_PX, MOBILE_BREAKPOINT_REM, MOBILE_INITIAL_SCALE, MOBILE_QUERY, PANEL_BREAKPOINT_PX, PANEL_QUERY, SIDEBAR_BREAKPOINT_PX, SIDEBAR_QUERY };
12
+ //# sourceMappingURL=chunk-KNPRUZ4H.js.map
13
+ //# sourceMappingURL=chunk-KNPRUZ4H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/viewport.ts"],"names":[],"mappings":";AAKO,IAAM,oBAAA,GAAuB;AAU7B,IAAM,oBAAA,GAAuB;AAM7B,IAAM,wBAAwB,oBAAA,GAAuB;AAUrD,IAAM,YAAA,GAAe,CAAA,YAAA,EAAe,oBAAA,GAAuB,CAAC,CAAA,GAAA;AAW5D,IAAM,qBAAA,GAAwB;AAQ9B,IAAM,aAAA,GAAgB,CAAA,YAAA,EAAe,qBAAA,GAAwB,CAAC,CAAA,GAAA;AAW9D,IAAM,mBAAA,GAAsB;AAO5B,IAAM,WAAA,GAAc,CAAA,YAAA,EAAe,mBAAA,GAAsB,CAAC,CAAA,GAAA","file":"chunk-KNPRUZ4H.js","sourcesContent":["/**\n * Mobile viewport initial-scale factor used across the app shell and\n * plugin iframe previews. Kept in one place so it can be changed\n * without hunting through HTML templates and CSS rules.\n */\nexport const MOBILE_INITIAL_SCALE = 0.8\n\n/**\n * Width in pixels at which the app switches from below-md to md+ layouts.\n *\n * This value is intentionally aligned with Tailwind's `md` breakpoint\n * (768 px) so that JS detection and CSS responsive prefixes stay in sync.\n * Components that need to know whether they are in the \"below md\" viewport\n * should use {@link isBelowMd} or {@link useBelowMd} from @hoardodile/ui/hooks/use-mobile.\n */\nexport const MOBILE_BREAKPOINT_PX = 768\n\n/**\n * Same breakpoint expressed in rems, matching Tailwind's default theme\n * (`--breakpoint-md: 48rem`). Useful for CSS media queries and comments.\n */\nexport const MOBILE_BREAKPOINT_REM = MOBILE_BREAKPOINT_PX / 16\n\n/**\n * Media query string that matches viewports below the `md` breakpoint\n * (< 768 px). Shared between JS hooks and anywhere else that needs to detect\n * a below-md viewport.\n *\n * Keep this in sync with the CSS/Tailwind breakpoint so JS and CSS never\n * disagree about the viewport size.\n */\nexport const MOBILE_QUERY = `(max-width: ${MOBILE_BREAKPOINT_PX - 1}px)`\n\n/**\n * Width in pixels at which the app switches from a drawer to a fixed left\n * sidebar (the \"app shell\"). Raised above `md` so mid-size viewports keep\n * the whole width for their content column — below this the sidebar hides\n * behind the hamburger drawer.\n *\n * Use the CSS `sidebar:` / `max-sidebar:` variants (`--breakpoint-sidebar`)\n * at call sites — this value is the single source of truth for both.\n */\nexport const SIDEBAR_BREAKPOINT_PX = 1150\n\n/**\n * Media query string that matches viewports below the sidebar breakpoint\n * (`max-sidebar`), i.e. where the shell shows the drawer instead of the fixed\n * sidebar. Shared between JS hooks and anywhere else that needs the\n * below-sidebar viewport.\n */\nexport const SIDEBAR_QUERY = `(max-width: ${SIDEBAR_BREAKPOINT_PX - 1}px)`\n\n/**\n * Width in pixels at which the right filter rail switches from a fixed\n * column to a drawer (DESIGN.md — Layout: \"the panel becomes an\n * overlay below 1440px\"). Below this the sidebar + panel + content no\n * longer fit comfortably, so the rail hides behind a Filters button.\n *\n * Use the CSS `panel:` / `max-panel:` variants (`--breakpoint-panel`)\n * at call sites — this value is the single source of truth for both.\n */\nexport const PANEL_BREAKPOINT_PX = 1440\n\n/**\n * Media query string that matches viewports below the panel breakpoint\n * (< 1440 px), i.e. where the filter rail lives in a drawer. Shared\n * between JS hooks and anywhere else that needs the below-panel viewport.\n */\nexport const PANEL_QUERY = `(max-width: ${PANEL_BREAKPOINT_PX - 1}px)`\n"]}
@@ -0,0 +1,254 @@
1
+ import { useMobileBackToClose } from './chunk-TDDHQZ62.js';
2
+ import { Cross } from './chunk-XBPQEZES.js';
3
+ import { Button } from './chunk-PWJBHUM2.js';
4
+ import { cn } from './chunk-M5OJN54O.js';
5
+ import * as React from 'react';
6
+ import { Dialog as Dialog$1 } from '@base-ui/react/dialog';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
+
10
+ function Dialog({
11
+ open,
12
+ defaultOpen,
13
+ onOpenChange,
14
+ ...props
15
+ }) {
16
+ const [uncontrolledOpen, setUncontrolledOpen] = React.useState(
17
+ defaultOpen ?? false
18
+ );
19
+ const isControlled = open !== void 0;
20
+ const currentOpen = isControlled ? open : uncontrolledOpen;
21
+ function handleOpenChange(next, eventDetails) {
22
+ if (!isControlled) setUncontrolledOpen(next);
23
+ onOpenChange?.(next, eventDetails);
24
+ }
25
+ useMobileBackToClose(currentOpen, handleOpenChange);
26
+ return /* @__PURE__ */ jsx(
27
+ Dialog$1.Root,
28
+ {
29
+ "data-slot": "dialog",
30
+ open: currentOpen,
31
+ onOpenChange: handleOpenChange,
32
+ ...props
33
+ }
34
+ );
35
+ }
36
+ function DialogTrigger({ ...props }) {
37
+ return /* @__PURE__ */ jsx(Dialog$1.Trigger, { "data-slot": "dialog-trigger", ...props });
38
+ }
39
+ function DialogPortal({ ...props }) {
40
+ return /* @__PURE__ */ jsx(Dialog$1.Portal, { "data-slot": "dialog-portal", ...props });
41
+ }
42
+ function DialogClose({ ...props }) {
43
+ return /* @__PURE__ */ jsx(Dialog$1.Close, { "data-slot": "dialog-close", ...props });
44
+ }
45
+ function DialogOverlay({
46
+ className,
47
+ ...props
48
+ }) {
49
+ return /* @__PURE__ */ jsx(
50
+ Dialog$1.Backdrop,
51
+ {
52
+ "data-slot": "dialog-overlay",
53
+ className: cn(
54
+ // The dialog layer: a static whisper-of-ink scrim plus
55
+ // backdrop blur — part of the surface definition, not motion.
56
+ "fixed inset-0 isolate z-50 bg-foreground/5 transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-sm",
57
+ className
58
+ ),
59
+ ...props
60
+ }
61
+ );
62
+ }
63
+ function DialogContent({
64
+ className,
65
+ children,
66
+ showCloseButton = true,
67
+ overlayClassName,
68
+ contentMotion = "default",
69
+ ...props
70
+ }) {
71
+ const { t } = useTranslation("ui", { useSuspense: false });
72
+ const arranged = arrangeDialogChildren(children);
73
+ const motionClasses = contentMotion === "minimal" ? "data-ending-style:opacity-0 data-starting-style:opacity-0" : cn(
74
+ "data-ending-style:translate-y-10 data-ending-style:opacity-0 data-starting-style:translate-y-10 data-starting-style:opacity-0",
75
+ // ≥ sm the popup is centred with -translate-y-1/2, so the
76
+ // starting/ending styles must keep that offset (zoom + fade
77
+ // only, no slide) instead of clobbering it with translate-y-0.
78
+ "sm:data-ending-style:-translate-y-1/2 sm:data-ending-style:scale-95 sm:data-starting-style:-translate-y-1/2 sm:data-starting-style:scale-95"
79
+ );
80
+ return /* @__PURE__ */ jsxs(DialogPortal, { children: [
81
+ /* @__PURE__ */ jsx(DialogOverlay, { className: overlayClassName }),
82
+ /* @__PURE__ */ jsxs(
83
+ Dialog$1.Popup,
84
+ {
85
+ "data-slot": "dialog-content",
86
+ className: cn(
87
+ // The floating-card anatomy: `bg-card`
88
+ // hairline-bordered surface with the system's one deep shadow.
89
+ // Mobile: bottom sheet occupying most of the viewport so edit
90
+ // forms have room to breathe. The component never scrolls
91
+ // itself; only the inner DialogBody does. Header/body/footer
92
+ // parts are held apart by the card's section gap
93
+ // (`gap-4` between the header document, the body and the
94
+ // hairline footer).
95
+ "fixed z-50 flex flex-col gap-4 bg-card text-sm text-card-foreground shadow-dialog outline-hidden transition duration-200 ease-in-out",
96
+ "inset-x-0 bottom-0 max-h-[85svh] overflow-hidden rounded-t-2xl rounded-b-none border border-b-0 border-border",
97
+ motionClasses,
98
+ // ≥ sm: centred modal, allow up to 90vh.
99
+ "sm:inset-x-auto sm:bottom-auto sm:top-1/2 sm:left-1/2 sm:w-full sm:max-w-md sm:max-h-[90vh] sm:-translate-x-1/2 sm:-translate-y-1/2 sm:rounded-2xl",
100
+ className
101
+ ),
102
+ ...props,
103
+ children: [
104
+ arranged,
105
+ showCloseButton && /* @__PURE__ */ jsxs(
106
+ Dialog$1.Close,
107
+ {
108
+ "data-slot": "dialog-close",
109
+ className: "absolute top-3 right-3 z-20 flex size-6 items-center justify-center rounded-md text-muted-foreground transition-colors duration-150 hover:bg-muted hover:text-foreground",
110
+ children: [
111
+ /* @__PURE__ */ jsx(Cross, { className: "size-3" }),
112
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: t("aria.close") })
113
+ ]
114
+ }
115
+ )
116
+ ]
117
+ }
118
+ )
119
+ ] });
120
+ }
121
+ function DialogHeader({ className, ...props }) {
122
+ return /* @__PURE__ */ jsx(
123
+ "div",
124
+ {
125
+ "data-slot": "dialog-header",
126
+ className: cn(
127
+ // The header document: the card's `p-5`
128
+ // rhythm — 20px top and sides, no bottom padding — with the
129
+ // eyebrow, title row and description stacked tight; the section
130
+ // gap below separates it from the body.
131
+ "flex shrink-0 flex-col bg-card px-5 pt-5",
132
+ className
133
+ ),
134
+ ...props
135
+ }
136
+ );
137
+ }
138
+ function DialogBody({ className, ...props }) {
139
+ return /* @__PURE__ */ jsx(
140
+ "div",
141
+ {
142
+ "data-slot": "dialog-body",
143
+ className: cn(
144
+ "flex-1 overflow-y-auto overscroll-contain px-5",
145
+ className
146
+ ),
147
+ ...props
148
+ }
149
+ );
150
+ }
151
+ function DialogFooter({
152
+ className,
153
+ flush = false,
154
+ showCloseButton = false,
155
+ children,
156
+ ...props
157
+ }) {
158
+ return /* @__PURE__ */ jsxs(
159
+ "div",
160
+ {
161
+ "data-slot": "dialog-footer",
162
+ className: cn("flex shrink-0 flex-col bg-card", className),
163
+ ...props,
164
+ children: [
165
+ /* @__PURE__ */ jsx(
166
+ "div",
167
+ {
168
+ className: cn(
169
+ "border-t border-border",
170
+ flush ? "mx-4" : "mx-5"
171
+ )
172
+ }
173
+ ),
174
+ /* @__PURE__ */ jsxs(
175
+ "div",
176
+ {
177
+ className: cn(
178
+ // The hairline action bar: 16px above the
179
+ // buttons, the card's bottom padding (20px) below.
180
+ "flex flex-row justify-end flex-wrap gap-2",
181
+ flush ? "p-4" : "px-5 pt-4 pb-5"
182
+ ),
183
+ children: [
184
+ children,
185
+ showCloseButton && /* @__PURE__ */ jsx(Dialog$1.Close, { render: /* @__PURE__ */ jsx(Button, { variant: "outline" }), children: "Close" })
186
+ ]
187
+ }
188
+ )
189
+ ]
190
+ }
191
+ );
192
+ }
193
+ function DialogTitle({ className, ...props }) {
194
+ return /* @__PURE__ */ jsx(
195
+ Dialog$1.Title,
196
+ {
197
+ "data-slot": "dialog-title",
198
+ className: cn("text-sm font-semibold", className),
199
+ ...props
200
+ }
201
+ );
202
+ }
203
+ function DialogDescription({
204
+ className,
205
+ ...props
206
+ }) {
207
+ return /* @__PURE__ */ jsx(
208
+ Dialog$1.Description,
209
+ {
210
+ "data-slot": "dialog-description",
211
+ className: cn(
212
+ "text-xs leading-5 text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
213
+ className
214
+ ),
215
+ ...props
216
+ }
217
+ );
218
+ }
219
+ function arrangeDialogChildren(children) {
220
+ const headers = [];
221
+ const footers = [];
222
+ const bodyNodes = [];
223
+ let hasExplicitBody = false;
224
+ React.Children.forEach(children, (child) => {
225
+ if (!React.isValidElement(child)) {
226
+ bodyNodes.push(child);
227
+ return;
228
+ }
229
+ if (child.type === DialogHeader) {
230
+ headers.push(child);
231
+ return;
232
+ }
233
+ if (child.type === DialogFooter) {
234
+ footers.push(child);
235
+ return;
236
+ }
237
+ if (child.type === DialogBody) {
238
+ hasExplicitBody = true;
239
+ bodyNodes.push(child);
240
+ return;
241
+ }
242
+ bodyNodes.push(child);
243
+ });
244
+ const body = hasExplicitBody ? bodyNodes : /* @__PURE__ */ jsx(DialogBody, { children: bodyNodes });
245
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
246
+ headers,
247
+ body,
248
+ footers
249
+ ] });
250
+ }
251
+
252
+ export { Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger };
253
+ //# sourceMappingURL=chunk-KOFWEPPB.js.map
254
+ //# sourceMappingURL=chunk-KOFWEPPB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/dialog.tsx"],"names":["DialogPrimitive"],"mappings":"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","file":"chunk-KOFWEPPB.js","sourcesContent":["import * as React from \"react\"\nimport { Dialog as DialogPrimitive } from \"@base-ui/react/dialog\"\nimport { useTranslation } from \"react-i18next\"\n\nimport { cn } from \"@hoardodile/ui/lib/utils\"\nimport { Button } from \"@hoardodile/ui/components/button\"\nimport { useMobileBackToClose } from \"@hoardodile/ui/hooks/useMobileBackToClose\"\nimport { Cross } from \"@hoardodile/ui/icons/marks\"\n\nfunction Dialog({\n open,\n defaultOpen,\n onOpenChange,\n ...props\n}: Omit<DialogPrimitive.Root.Props, \"onOpenChange\"> & {\n /**\n * `eventDetails` is absent for closes triggered by the mobile back\n * gesture (a synthetic close with no underlying Base UI event).\n */\n onOpenChange?: (\n open: boolean,\n eventDetails?: DialogPrimitive.Root.ChangeEventDetails,\n ) => void\n}) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(\n defaultOpen ?? false,\n )\n const isControlled = open !== undefined\n const currentOpen = isControlled ? open : uncontrolledOpen\n function handleOpenChange(\n next: boolean,\n eventDetails?: DialogPrimitive.Root.ChangeEventDetails,\n ) {\n if (!isControlled) setUncontrolledOpen(next)\n onOpenChange?.(next, eventDetails)\n }\n useMobileBackToClose(currentOpen, handleOpenChange)\n return (\n <DialogPrimitive.Root\n data-slot=\"dialog\"\n open={currentOpen}\n onOpenChange={handleOpenChange}\n {...props}\n />\n )\n}\n\nfunction DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />\n}\n\nfunction DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {\n return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />\n}\n\nfunction DialogClose({ ...props }: DialogPrimitive.Close.Props) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: DialogPrimitive.Backdrop.Props) {\n return (\n <DialogPrimitive.Backdrop\n data-slot=\"dialog-overlay\"\n className={cn(\n // The dialog layer: a static whisper-of-ink scrim plus\n // backdrop blur — part of the surface definition, not motion.\n \"fixed inset-0 isolate z-50 bg-foreground/5 transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-sm\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DialogContent({\n className,\n children,\n showCloseButton = true,\n overlayClassName,\n contentMotion = \"default\",\n ...props\n}: DialogPrimitive.Popup.Props &\n React.RefAttributes<HTMLDivElement> & {\n showCloseButton?: boolean\n /** Merged into {@link DialogOverlay}. Use to drop backdrop blur over heavy GPU layers (e.g. WebGL). */\n overlayClassName?: string\n /**\n * `minimal` uses fade-only enter/exit (no slide/zoom) to reduce jank\n * when opening over canvas/video or other expensive surfaces.\n */\n contentMotion?: \"default\" | \"minimal\"\n }) {\n const { t } = useTranslation(\"ui\", { useSuspense: false })\n // Auto-wrap \"loose\" body children (anything that is not a DialogHeader\n // or DialogFooter) inside a DialogBody. This guarantees a single\n // scroll container — historically callers relied on DialogContent\n // itself scrolling and added their own nested `overflow-y-auto`\n // wrappers, which produced a confusing two-scrollbar behaviour on\n // touch devices. With a single inner scroller, header/footer stay\n // pinned and only the middle region scrolls on every viewport.\n const arranged = arrangeDialogChildren(children)\n const motionClasses =\n contentMotion === \"minimal\"\n ? \"data-ending-style:opacity-0 data-starting-style:opacity-0\"\n : cn(\n \"data-ending-style:translate-y-10 data-ending-style:opacity-0 data-starting-style:translate-y-10 data-starting-style:opacity-0\",\n // ≥ sm the popup is centred with -translate-y-1/2, so the\n // starting/ending styles must keep that offset (zoom + fade\n // only, no slide) instead of clobbering it with translate-y-0.\n \"sm:data-ending-style:-translate-y-1/2 sm:data-ending-style:scale-95 sm:data-starting-style:-translate-y-1/2 sm:data-starting-style:scale-95\"\n )\n return (\n <DialogPortal>\n <DialogOverlay className={overlayClassName} />\n <DialogPrimitive.Popup\n data-slot=\"dialog-content\"\n className={cn(\n // The floating-card anatomy: `bg-card`\n // hairline-bordered surface with the system's one deep shadow.\n // Mobile: bottom sheet occupying most of the viewport so edit\n // forms have room to breathe. The component never scrolls\n // itself; only the inner DialogBody does. Header/body/footer\n // parts are held apart by the card's section gap\n // (`gap-4` between the header document, the body and the\n // hairline footer).\n \"fixed z-50 flex flex-col gap-4 bg-card text-sm text-card-foreground shadow-dialog outline-hidden transition duration-200 ease-in-out\",\n \"inset-x-0 bottom-0 max-h-[85svh] overflow-hidden rounded-t-2xl rounded-b-none border border-b-0 border-border\",\n motionClasses,\n // ≥ sm: centred modal, allow up to 90vh.\n \"sm:inset-x-auto sm:bottom-auto sm:top-1/2 sm:left-1/2 sm:w-full sm:max-w-md sm:max-h-[90vh] sm:-translate-x-1/2 sm:-translate-y-1/2 sm:rounded-2xl\",\n className\n )}\n {...props}\n >\n {arranged}\n {showCloseButton && (\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n className=\"absolute top-3 right-3 z-20 flex size-6 items-center justify-center rounded-md text-muted-foreground transition-colors duration-150 hover:bg-muted hover:text-foreground\"\n >\n <Cross className=\"size-3\" />\n <span className=\"sr-only\">{t(\"aria.close\")}</span>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Popup>\n </DialogPortal>\n )\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\n // The header document: the card's `p-5`\n // rhythm — 20px top and sides, no bottom padding — with the\n // eyebrow, title row and description stacked tight; the section\n // gap below separates it from the body.\n \"flex shrink-0 flex-col bg-card px-5 pt-5\",\n className\n )}\n {...props}\n />\n )\n}\n\n/**\n * Scrolling body region for dialogs. The dialog itself never scrolls —\n * only this element does — so touch users always interact with one\n * obvious scroll surface. The card's `p-5` side rhythm lives here; the\n * section gaps (`gap-4`) separate it from the header and footer.\n */\nfunction DialogBody({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-body\"\n className={cn(\n \"flex-1 overflow-y-auto overscroll-contain px-5\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DialogFooter({\n className,\n flush = false,\n showCloseButton = false,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & {\n flush?: boolean\n showCloseButton?: boolean\n}) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\"flex shrink-0 flex-col bg-card\", className)}\n {...props}\n >\n {/* Inset hairline: the same side padding as the content below, so\n the divider never runs edge to edge. */}\n <div\n className={cn(\n \"border-t border-border\",\n flush ? \"mx-4\" : \"mx-5\"\n )}\n />\n <div\n className={cn(\n // The hairline action bar: 16px above the\n // buttons, the card's bottom padding (20px) below.\n \"flex flex-row justify-end flex-wrap gap-2\",\n flush ? \"p-4\" : \"px-5 pt-4 pb-5\"\n )}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close render={<Button variant=\"outline\" />}>\n Close\n </DialogPrimitive.Close>\n )}\n </div>\n </div>\n )\n}\n\nfunction DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"text-sm font-semibold\", className)}\n {...props}\n />\n )\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: DialogPrimitive.Description.Props) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\n \"text-xs leading-5 text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground\",\n className\n )}\n {...props}\n />\n )\n}\n\n/**\n * Splits dialog children into header/footer/body groups so loose body\n * nodes (passed by callers that pre-date `<DialogBody>`) get wrapped in\n * a single scrolling region. Children already wrapped in\n * `<DialogBody>` are passed through unchanged.\n */\nfunction arrangeDialogChildren(children: React.ReactNode): React.ReactNode {\n const headers: React.ReactNode[] = []\n const footers: React.ReactNode[] = []\n const bodyNodes: React.ReactNode[] = []\n let hasExplicitBody = false\n React.Children.forEach(children, (child) => {\n if (!React.isValidElement(child)) {\n bodyNodes.push(child)\n return\n }\n if (child.type === DialogHeader) {\n headers.push(child)\n return\n }\n if (child.type === DialogFooter) {\n footers.push(child)\n return\n }\n if (child.type === DialogBody) {\n hasExplicitBody = true\n bodyNodes.push(child)\n return\n }\n bodyNodes.push(child)\n })\n const body = hasExplicitBody ? (\n bodyNodes\n ) : (\n <DialogBody>{bodyNodes}</DialogBody>\n )\n return (\n <>\n {headers}\n {body}\n {footers}\n </>\n )\n}\n\nexport {\n Dialog,\n DialogBody,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import { twMerge } from './chunk-2HWSCY4T.js';
2
+ import { clsx } from 'clsx';
3
+
4
+ function cn(...inputs) {
5
+ return twMerge(clsx(inputs));
6
+ }
7
+
8
+ export { cn };
9
+ //# sourceMappingURL=chunk-M5OJN54O.js.map
10
+ //# sourceMappingURL=chunk-M5OJN54O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/utils.ts"],"names":[],"mappings":";;;AAGO,SAAS,MAAM,MAAA,EAAsB;AAC3C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC5B","file":"chunk-M5OJN54O.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"./merge-config\"\n\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs))\n}\n"]}
@@ -0,0 +1,51 @@
1
+ import { useSyncExternalStore, createElement } from 'react';
2
+
3
+ // src/icons/icon-style.ts
4
+ function readIconMode() {
5
+ return document.documentElement.dataset.iconStyle === "linear" ? "linear" : "boldDuotone";
6
+ }
7
+ var currentMode = "boldDuotone";
8
+ var observer;
9
+ var listeners = /* @__PURE__ */ new Set();
10
+ function ensureObserver() {
11
+ if (observer !== void 0 || typeof document === "undefined") return;
12
+ observer = new MutationObserver(() => {
13
+ const next = readIconMode();
14
+ if (next === currentMode) return;
15
+ currentMode = next;
16
+ for (const cb of listeners) cb();
17
+ });
18
+ observer.observe(document.documentElement, {
19
+ attributes: true,
20
+ attributeFilter: ["data-icon-style"]
21
+ });
22
+ }
23
+ function subscribeIconStyle(callback) {
24
+ ensureObserver();
25
+ currentMode = readIconMode();
26
+ listeners.add(callback);
27
+ return () => {
28
+ listeners.delete(callback);
29
+ };
30
+ }
31
+ function getIconStyleSnapshot() {
32
+ return currentMode;
33
+ }
34
+ function createIcon(variants) {
35
+ function Icon(props) {
36
+ const current = useSyncExternalStore(
37
+ subscribeIconStyle,
38
+ getIconStyleSnapshot,
39
+ () => "boldDuotone"
40
+ );
41
+ const { mode, ...rest } = props;
42
+ const Component = variants[mode ?? current];
43
+ const className = typeof rest.className === "string" ? `hd-icon ${rest.className}` : "hd-icon";
44
+ return createElement(Component, { ...rest, className });
45
+ }
46
+ return Icon;
47
+ }
48
+
49
+ export { createIcon };
50
+ //# sourceMappingURL=chunk-OEVALKQ5.js.map
51
+ //# sourceMappingURL=chunk-OEVALKQ5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/icons/icon-style.ts"],"names":[],"mappings":";;;AA6BA,SAAS,YAAA,GAAyB;AACjC,EAAA,OAAO,QAAA,CAAS,eAAA,CAAgB,OAAA,CAAQ,SAAA,KAAc,WACnD,QAAA,GACA,aAAA;AACJ;AAKA,IAAI,WAAA,GAAwB,aAAA;AAC5B,IAAI,QAAA;AACJ,IAAM,SAAA,uBAAgB,GAAA,EAAgB;AAEtC,SAAS,cAAA,GAAuB;AAC/B,EAAA,IAAI,QAAA,KAAa,MAAA,IAAa,OAAO,QAAA,KAAa,WAAA,EAAa;AAC/D,EAAA,QAAA,GAAW,IAAI,iBAAiB,MAAM;AACrC,IAAA,MAAM,OAAO,YAAA,EAAa;AAC1B,IAAA,IAAI,SAAS,WAAA,EAAa;AAC1B,IAAA,WAAA,GAAc,IAAA;AACd,IAAA,KAAA,MAAW,EAAA,IAAM,WAAW,EAAA,EAAG;AAAA,EAChC,CAAC,CAAA;AACD,EAAA,QAAA,CAAS,OAAA,CAAQ,SAAS,eAAA,EAAiB;AAAA,IAC1C,UAAA,EAAY,IAAA;AAAA,IACZ,eAAA,EAAiB,CAAC,iBAAiB;AAAA,GACnC,CAAA;AACF;AAEA,SAAS,mBAAmB,QAAA,EAAkC;AAC7D,EAAA,cAAA,EAAe;AACf,EAAA,WAAA,GAAc,YAAA,EAAa;AAC3B,EAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AACtB,EAAA,OAAO,MAAM;AACZ,IAAA,SAAA,CAAU,OAAO,QAAQ,CAAA;AAAA,EAC1B,CAAA;AACD;AAEA,SAAS,oBAAA,GAAiC;AACzC,EAAA,OAAO,WAAA;AACR;AAeO,SAAS,WACf,QAAA,EACyC;AACzC,EAAA,SAAS,KAAK,KAAA,EAAgC;AAC7C,IAAA,MAAM,OAAA,GAAU,oBAAA;AAAA,MACf,kBAAA;AAAA,MACA,oBAAA;AAAA,MACA,MAAM;AAAA,KACP;AACA,IAAA,MAAM,EAAE,IAAA,EAAM,GAAG,IAAA,EAAK,GAAI,KAAA;AAI1B,IAAA,MAAM,SAAA,GAAY,QAAA,CAAU,IAAA,IAAQ,OAAoB,CAAA;AACxD,IAAA,MAAM,SAAA,GACL,OAAO,IAAA,CAAK,SAAA,KAAc,WACvB,CAAA,QAAA,EAAW,IAAA,CAAK,SAAS,CAAA,CAAA,GACzB,SAAA;AACJ,IAAA,OAAO,cAAc,SAAA,EAAW,EAAE,GAAG,IAAA,EAAM,WAAW,CAAA;AAAA,EACvD;AACA,EAAA,OAAO,IAAA;AACR","file":"chunk-OEVALKQ5.js","sourcesContent":["import { type ComponentType, createElement, useSyncExternalStore } from \"react\"\nimport type { IconType } from \"../components/icon.tsx\"\n\n/**\n * Icon-mode-aware glyph wrapper (Settings → Icons in the host app).\n *\n * The active icon style lives as `data-icon-style` on `document.documentElement`\n * — the host app writes it from its preference store, plugin iframes receive\n * it through the `themeChanged` push — so this module needs no context or\n * injected state: a single shared `MutationObserver` watches the attribute\n * and every wrapped component re-renders through `useSyncExternalStore`\n * when it flips.\n *\n * Only `linear` needs a component swap: the boldDuotone glyphs change to\n * their thin-line linear counterparts. `grayscale` is pure CSS (theme.css\n * recolors the `.hd-icon` hook), so the store only tracks the mode.\n */\nexport type IconStyleValue = \"duotone\" | \"grayscale\" | \"linear\"\n\n/**\n * The parallel Solar weights an icon registers — named after Solar's own\n * weights (DESIGN.md — Iconography). Every icon carries all of them;\n * adding a mode here forces every registry entry to fill it in.\n */\nexport type IconMode = \"bold\" | \"boldDuotone\" | \"linear\"\n\n/** All three mode glyphs of one icon. Mandatory and complete by type. */\nexport type IconVariants = Record<IconMode, IconType>\n\nfunction readIconMode(): IconMode {\n\treturn document.documentElement.dataset.iconStyle === \"linear\"\n\t\t? \"linear\"\n\t\t: \"boldDuotone\"\n}\n\n// Never read `document` at module load — pure-logic consumers (node test\n// environments) import this module without a DOM. The first subscribe\n// (component mount) snapshots the current value.\nlet currentMode: IconMode = \"boldDuotone\"\nlet observer: MutationObserver | undefined\nconst listeners = new Set<() => void>()\n\nfunction ensureObserver(): void {\n\tif (observer !== undefined || typeof document === \"undefined\") return\n\tobserver = new MutationObserver(() => {\n\t\tconst next = readIconMode()\n\t\tif (next === currentMode) return\n\t\tcurrentMode = next\n\t\tfor (const cb of listeners) cb()\n\t})\n\tobserver.observe(document.documentElement, {\n\t\tattributes: true,\n\t\tattributeFilter: [\"data-icon-style\"],\n\t})\n}\n\nfunction subscribeIconStyle(callback: () => void): () => void {\n\tensureObserver()\n\tcurrentMode = readIconMode()\n\tlisteners.add(callback)\n\treturn () => {\n\t\tlisteners.delete(callback)\n\t}\n}\n\nfunction getIconStyleSnapshot(): IconMode {\n\treturn currentMode\n}\n\n/**\n * Wraps a Solar glyph family so its render follows the host's icon style.\n *\n * `createIcon({ bold, boldDuotone, linear })` — the three parallel weights\n * of one icon. Without a `mode` prop the wrapper renders the glyph for the\n * active icon style; an explicit `mode` overrides it (selected states pass\n * `\"bold\"` through `Icon`).\n *\n * Every wrapped render merges the `hd-icon` hook class, so the duotone /\n * grayscale CSS in theme.css applies to direct renders too. Returns\n * `undefined`-free components callable through the `Icon` wrapper or bare\n * JSX alike.\n */\nexport function createIcon(\n\tvariants: IconVariants,\n): ComponentType<Record<string, unknown>> {\n\tfunction Icon(props: Record<string, unknown>) {\n\t\tconst current = useSyncExternalStore(\n\t\t\tsubscribeIconStyle,\n\t\t\tgetIconStyleSnapshot,\n\t\t\t() => \"boldDuotone\",\n\t\t)\n\t\tconst { mode, ...rest } = props as { readonly mode?: IconMode } & Record<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>\n\t\tconst Component = variants[(mode ?? current) as IconMode]\n\t\tconst className =\n\t\t\ttypeof rest.className === \"string\"\n\t\t\t\t? `hd-icon ${rest.className}`\n\t\t\t\t: \"hd-icon\"\n\t\treturn createElement(Component, { ...rest, className })\n\t}\n\treturn Icon\n}\n"]}
@@ -0,0 +1,33 @@
1
+ import { cn } from './chunk-M5OJN54O.js';
2
+ import { Switch as Switch$1 } from '@base-ui/react/switch';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ function Switch({
6
+ className,
7
+ size = "default",
8
+ ...props
9
+ }) {
10
+ return /* @__PURE__ */ jsx(
11
+ Switch$1.Root,
12
+ {
13
+ "data-slot": "switch",
14
+ "data-size": size,
15
+ className: cn(
16
+ "peer group/switch relative inline-flex shrink-0 cursor-pointer items-center rounded-full border border-transparent shadow-xs transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-5 data-[size=default]:w-9 data-[size=default]:p-[3px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",
17
+ className
18
+ ),
19
+ ...props,
20
+ children: /* @__PURE__ */ jsx(
21
+ Switch$1.Thumb,
22
+ {
23
+ "data-slot": "switch-thumb",
24
+ className: "pointer-events-none block rounded-full bg-background ring-1 ring-foreground/10 transition-transform group-data-[size=default]/switch:size-3.5 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%+2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground"
25
+ }
26
+ )
27
+ }
28
+ );
29
+ }
30
+
31
+ export { Switch };
32
+ //# sourceMappingURL=chunk-OW5FWLPH.js.map
33
+ //# sourceMappingURL=chunk-OW5FWLPH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/switch.tsx"],"names":["SwitchPrimitive"],"mappings":";;;;AAIA,SAAS,MAAA,CAAO;AAAA,EACd,SAAA;AAAA,EACA,IAAA,GAAO,SAAA;AAAA,EACP,GAAG;AACL,CAAA,EAEG;AACD,EAAA,uBACE,GAAA;AAAA,IAACA,QAAA,CAAgB,IAAA;AAAA,IAAhB;AAAA,MACC,WAAA,EAAU,QAAA;AAAA,MACV,WAAA,EAAW,IAAA;AAAA,MACX,SAAA,EAAW,EAAA;AAAA,QACT,8rBAAA;AAAA,QACA;AAAA,OACF;AAAA,MACC,GAAG,KAAA;AAAA,MAEJ,QAAA,kBAAA,GAAA;AAAA,QAACA,QAAA,CAAgB,KAAA;AAAA,QAAhB;AAAA,UACC,WAAA,EAAU,cAAA;AAAA,UACV,SAAA,EAAU;AAAA;AAAA;AACZ;AAAA,GACF;AAEJ","file":"chunk-OW5FWLPH.js","sourcesContent":["import { Switch as SwitchPrimitive } from \"@base-ui/react/switch\"\n\nimport { cn } from \"@hoardodile/ui/lib/utils\"\n\nfunction Switch({\n className,\n size = \"default\",\n ...props\n}: SwitchPrimitive.Root.Props & {\n size?: \"sm\" | \"default\"\n}) {\n return (\n <SwitchPrimitive.Root\n data-slot=\"switch\"\n data-size={size}\n className={cn(\n \"peer group/switch relative inline-flex shrink-0 cursor-pointer items-center rounded-full border border-transparent shadow-xs transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-5 data-[size=default]:w-9 data-[size=default]:p-[3px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50\",\n className\n )}\n {...props}\n >\n <SwitchPrimitive.Thumb\n data-slot=\"switch-thumb\"\n className=\"pointer-events-none block rounded-full bg-background ring-1 ring-foreground/10 transition-transform group-data-[size=default]/switch:size-3.5 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%+2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground\"\n />\n </SwitchPrimitive.Root>\n )\n}\n\nexport { Switch }\n"]}
@@ -0,0 +1,41 @@
1
+ // src/res-card-template/icon.ts
2
+ var LEGACY_ICON_ALIASES = {
3
+ Files: "file",
4
+ Film: "video-frame",
5
+ Image: "gallery",
6
+ Info: "info-circle",
7
+ Music: "music-notes",
8
+ Search: "magnifier",
9
+ Sparkle: "star",
10
+ Video: "video-frame"
11
+ };
12
+ function normalizeSolarGlyphName(raw) {
13
+ const trimmed = raw.trim();
14
+ if (trimmed.length === 0) return void 0;
15
+ const aliased = LEGACY_ICON_ALIASES[trimmed];
16
+ if (aliased !== void 0) return aliased;
17
+ if (/^[a-z][a-z0-9-]*$/.test(trimmed)) return trimmed;
18
+ if (/^[A-Za-z][A-Za-z0-9]*$/.test(trimmed)) {
19
+ return trimmed.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
20
+ }
21
+ return void 0;
22
+ }
23
+ function parseIconRef(raw, pluginId, buildAssetUrl) {
24
+ const trimmed = raw.trim();
25
+ if (trimmed.length === 0) return void 0;
26
+ if (trimmed.startsWith("http://") || trimmed.startsWith("https://") || trimmed.startsWith("data:")) {
27
+ return void 0;
28
+ }
29
+ if (trimmed.includes(".") || trimmed.includes("/") || trimmed.includes("\\")) {
30
+ const rel = trimmed.replace(/^\.[\\/]/, "");
31
+ if (rel.length === 0 || rel.split("/").includes("..")) return void 0;
32
+ return { kind: "asset", url: buildAssetUrl(pluginId, rel) };
33
+ }
34
+ const name = normalizeSolarGlyphName(trimmed);
35
+ if (name === void 0) return void 0;
36
+ return { kind: "icon", name };
37
+ }
38
+
39
+ export { normalizeSolarGlyphName, parseIconRef };
40
+ //# sourceMappingURL=chunk-POX7TXBH.js.map
41
+ //# sourceMappingURL=chunk-POX7TXBH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/res-card-template/icon.ts"],"names":[],"mappings":";AAcA,IAAM,mBAAA,GAAwD;AAAA,EAC7D,KAAA,EAAO,MAAA;AAAA,EACP,IAAA,EAAM,aAAA;AAAA,EACN,KAAA,EAAO,SAAA;AAAA,EACP,IAAA,EAAM,aAAA;AAAA,EACN,KAAA,EAAO,aAAA;AAAA,EACP,MAAA,EAAQ,WAAA;AAAA,EACR,OAAA,EAAS,MAAA;AAAA,EACT,KAAA,EAAO;AACR,CAAA;AAQO,SAAS,wBAAwB,GAAA,EAAiC;AACxE,EAAA,MAAM,OAAA,GAAU,IAAI,IAAA,EAAK;AACzB,EAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,CAAA,EAAG,OAAO,MAAA;AACjC,EAAA,MAAM,OAAA,GAAU,oBAAoB,OAAO,CAAA;AAC3C,EAAA,IAAI,OAAA,KAAY,QAAW,OAAO,OAAA;AAClC,EAAA,IAAI,mBAAA,CAAoB,IAAA,CAAK,OAAO,CAAA,EAAG,OAAO,OAAA;AAC9C,EAAA,IAAI,wBAAA,CAAyB,IAAA,CAAK,OAAO,CAAA,EAAG;AAC3C,IAAA,OAAO,OAAA,CAAQ,OAAA,CAAQ,oBAAA,EAAsB,OAAO,EAAE,WAAA,EAAY;AAAA,EACnE;AACA,EAAA,OAAO,MAAA;AACR;AAeO,SAAS,YAAA,CACf,GAAA,EACA,QAAA,EACA,aAAA,EACsB;AACtB,EAAA,MAAM,OAAA,GAAU,IAAI,IAAA,EAAK;AACzB,EAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,CAAA,EAAG,OAAO,MAAA;AACjC,EAAA,IACC,OAAA,CAAQ,UAAA,CAAW,SAAS,CAAA,IAC5B,OAAA,CAAQ,UAAA,CAAW,UAAU,CAAA,IAC7B,OAAA,CAAQ,UAAA,CAAW,OAAO,CAAA,EACzB;AACD,IAAA,OAAO,MAAA;AAAA,EACR;AACA,EAAA,IACC,OAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,IACpB,OAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,IACpB,OAAA,CAAQ,QAAA,CAAS,IAAI,CAAA,EACpB;AACD,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,OAAA,CAAQ,UAAA,EAAY,EAAE,CAAA;AAC1C,IAAA,IAAI,GAAA,CAAI,MAAA,KAAW,CAAA,IAAK,GAAA,CAAI,KAAA,CAAM,GAAG,CAAA,CAAE,QAAA,CAAS,IAAI,CAAA,EAAG,OAAO,MAAA;AAC9D,IAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,KAAK,aAAA,CAAc,QAAA,EAAU,GAAG,CAAA,EAAE;AAAA,EAC3D;AACA,EAAA,MAAM,IAAA,GAAO,wBAAwB,OAAO,CAAA;AAC5C,EAAA,IAAI,IAAA,KAAS,QAAW,OAAO,MAAA;AAC/B,EAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAK;AAC7B","file":"chunk-POX7TXBH.js","sourcesContent":["import type { ReactNode } from \"react\"\n\n/**\n * A resolved template-icon reference: a Solar glyph name, or an\n * asset image URL derived from a manifest-relative path.\n */\nexport type IconRef =\n\t| { readonly kind: \"icon\"; readonly name: string }\n\t| { readonly kind: \"asset\"; readonly url: string }\n\n/**\n * Legacy whitelist name → Solar kebab glyph. Derived names are handled by\n * the PascalCase conversion; only these diverge.\n */\nconst LEGACY_ICON_ALIASES: Readonly<Record<string, string>> = {\n\tFiles: \"file\",\n\tFilm: \"video-frame\",\n\tImage: \"gallery\",\n\tInfo: \"info-circle\",\n\tMusic: \"music-notes\",\n\tSearch: \"magnifier\",\n\tSparkle: \"star\",\n\tVideo: \"video-frame\",\n}\n\n/**\n * Normalize a raw icon name to Solar kebab-case, or `undefined` when the\n * string cannot name a glyph at all (schemes, separators, punctuation).\n * This is syntax only — membership in the Solar glyph index is left to\n * the renderer that consumes the name.\n */\nexport function normalizeSolarGlyphName(raw: string): string | undefined {\n\tconst trimmed = raw.trim()\n\tif (trimmed.length === 0) return undefined\n\tconst aliased = LEGACY_ICON_ALIASES[trimmed]\n\tif (aliased !== undefined) return aliased\n\tif (/^[a-z][a-z0-9-]*$/.test(trimmed)) return trimmed\n\tif (/^[A-Za-z][A-Za-z0-9]*$/.test(trimmed)) {\n\t\treturn trimmed.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase()\n\t}\n\treturn undefined\n}\n\n/**\n * Parse a manifest-level icon string into a render-ready ref.\n *\n * `<SolarGlyph>` — Solar glyph name (manifest/template icons are\n * Solar-only; PascalCase and the legacy whitelist\n * names normalize to the kebab glyph)\n * `<relative/path>` — resolved through `buildAssetUrl(pluginId, path)`\n * (leading `./` stripped)\n *\n * Empty inputs, schemes (`http(s)`, `data:`), `..`-shaped paths and any\n * string that cannot name a glyph return `undefined`. The renderer treats\n * that as \"nothing\" — icon resolution never throws.\n */\nexport function parseIconRef(\n\traw: string,\n\tpluginId: string,\n\tbuildAssetUrl: (pluginId: string, path: string) => string,\n): IconRef | undefined {\n\tconst trimmed = raw.trim()\n\tif (trimmed.length === 0) return undefined\n\tif (\n\t\ttrimmed.startsWith(\"http://\") ||\n\t\ttrimmed.startsWith(\"https://\") ||\n\t\ttrimmed.startsWith(\"data:\")\n\t) {\n\t\treturn undefined\n\t}\n\tif (\n\t\ttrimmed.includes(\".\") ||\n\t\ttrimmed.includes(\"/\") ||\n\t\ttrimmed.includes(\"\\\\\")\n\t) {\n\t\tconst rel = trimmed.replace(/^\\.[\\\\/]/, \"\")\n\t\tif (rel.length === 0 || rel.split(\"/\").includes(\"..\")) return undefined\n\t\treturn { kind: \"asset\", url: buildAssetUrl(pluginId, rel) }\n\t}\n\tconst name = normalizeSolarGlyphName(trimmed)\n\tif (name === undefined) return undefined\n\treturn { kind: \"icon\", name }\n}\n\n/** How a parsed {@link IconRef} is drawn inside a rendered badge/template. */\nexport type RenderIcon = (ref: IconRef, className?: string) => ReactNode\n"]}
@@ -0,0 +1,58 @@
1
+ import { cn } from './chunk-M5OJN54O.js';
2
+ import { Button as Button$1 } from '@base-ui/react/button';
3
+ import { cva } from 'class-variance-authority';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ var buttonVariants = cva(
7
+ "group/button inline-flex shrink-0 items-center justify-center rounded-lg text-ui font-medium whitespace-nowrap outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-primary px-4! text-primary-foreground hover:bg-primary/80",
12
+ outline: "border-border bg-background shadow-xs hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
13
+ secondary: "bg-secondary text-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-foreground",
14
+ ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
15
+ destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
16
+ danger: "bg-[color-mix(in_oklab,var(--destructive),black_12%)] text-white hover:bg-[color-mix(in_oklab,var(--destructive),black_4%)] focus-visible:border-destructive/40 focus-visible:ring-destructive/20 disabled:bg-destructive/10 disabled:text-muted-foreground dark:focus-visible:ring-destructive/40",
17
+ link: "text-primary underline-offset-4 hover:underline"
18
+ },
19
+ size: {
20
+ default: "h-control gap-2 px-3 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
21
+ xs: "h-6 gap-2 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
22
+ sm: "h-chip gap-2 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5",
23
+ lg: "h-10 gap-2 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
24
+ icon: "size-9",
25
+ "icon-xs": "size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3",
26
+ "icon-sm": "size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md",
27
+ "icon-lg": "size-10"
28
+ }
29
+ },
30
+ defaultVariants: {
31
+ variant: "default",
32
+ size: "default"
33
+ }
34
+ }
35
+ );
36
+ function Button({
37
+ active,
38
+ className,
39
+ variant = "default",
40
+ size = "default",
41
+ ...props
42
+ }) {
43
+ return /* @__PURE__ */ jsx(
44
+ Button$1,
45
+ {
46
+ "data-slot": "button",
47
+ className: cn(
48
+ buttonVariants({ variant, size, className }),
49
+ active && "bg-accent text-accent-foreground hover:bg-accent"
50
+ ),
51
+ ...props
52
+ }
53
+ );
54
+ }
55
+
56
+ export { Button, buttonVariants };
57
+ //# sourceMappingURL=chunk-PWJBHUM2.js.map
58
+ //# sourceMappingURL=chunk-PWJBHUM2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/button.tsx"],"names":["ButtonPrimitive"],"mappings":";;;;;AAKA,IAAM,cAAA,GAAiB,GAAA;AAAA,EACrB,yfAAA;AAAA,EACA;AAAA,IACE,QAAA,EAAU;AAAA,MACR,OAAA,EAAS;AAAA,QACP,OAAA,EAAS,8DAAA;AAAA,QACT,OAAA,EACE,2LAAA;AAAA,QACF,SAAA,EACE,4JAAA;AAAA,QACF,KAAA,EACE,kHAAA;AAAA,QACF,WAAA,EACE,6NAAA;AAAA,QACF,MAAA,EACE,oSAAA;AAAA,QACF,IAAA,EAAM;AAAA,OACR;AAAA,MACA,IAAA,EAAM;AAAA,QACJ,OAAA,EACE,+HAAA;AAAA,QACF,EAAA,EAAI,8MAAA;AAAA,QACJ,EAAA,EAAI,uKAAA;AAAA,QACJ,EAAA,EAAI,qFAAA;AAAA,QACJ,IAAA,EAAM,QAAA;AAAA,QACN,SAAA,EACE,wHAAA;AAAA,QACF,SAAA,EACE,oFAAA;AAAA,QACF,SAAA,EAAW;AAAA;AACb,KACF;AAAA,IACA,eAAA,EAAiB;AAAA,MACf,OAAA,EAAS,SAAA;AAAA,MACT,IAAA,EAAM;AAAA;AACR;AAEJ;AAEA,SAAS,MAAA,CAAO;AAAA,EACd,MAAA;AAAA,EACA,SAAA;AAAA,EACA,OAAA,GAAU,SAAA;AAAA,EACV,IAAA,GAAO,SAAA;AAAA,EACP,GAAG;AACL,CAAA,EAMK;AACH,EAAA,uBACE,GAAA;AAAA,IAACA,QAAA;AAAA,IAAA;AAAA,MACC,WAAA,EAAU,QAAA;AAAA,MACV,SAAA,EAAW,EAAA;AAAA,QACT,cAAA,CAAe,EAAE,OAAA,EAAS,IAAA,EAAM,WAAW,CAAA;AAAA,QAC3C,MAAA,IAAU;AAAA,OACZ;AAAA,MACC,GAAG;AAAA;AAAA,GACN;AAEJ","file":"chunk-PWJBHUM2.js","sourcesContent":["import { Button as ButtonPrimitive } from \"@base-ui/react/button\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@hoardodile/ui/lib/utils\"\n\nconst buttonVariants = cva(\n \"group/button inline-flex shrink-0 items-center justify-center rounded-lg text-ui font-medium whitespace-nowrap outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n {\n variants: {\n variant: {\n default: \"bg-primary px-4! text-primary-foreground hover:bg-primary/80\",\n outline:\n \"border-border bg-background shadow-xs hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\",\n secondary:\n \"bg-secondary text-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-foreground\",\n ghost:\n \"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50\",\n destructive:\n \"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40\",\n danger:\n \"bg-[color-mix(in_oklab,var(--destructive),black_12%)] text-white hover:bg-[color-mix(in_oklab,var(--destructive),black_4%)] focus-visible:border-destructive/40 focus-visible:ring-destructive/20 disabled:bg-destructive/10 disabled:text-muted-foreground dark:focus-visible:ring-destructive/40\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default:\n \"h-control gap-2 px-3 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\n xs: \"h-6 gap-2 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3\",\n sm: \"h-chip gap-2 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5\",\n lg: \"h-10 gap-2 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\n icon: \"size-9\",\n \"icon-xs\":\n \"size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3\",\n \"icon-sm\":\n \"size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nfunction Button({\n active,\n className,\n variant = \"default\",\n size = \"default\",\n ...props\n}: ButtonPrimitive.Props &\n VariantProps<typeof buttonVariants> & {\n /** Latched toggle state: accent fill, hover pinned so an active\n button never shifts. Callers keep choosing the matching aria\n attribute (`aria-pressed` / `aria-expanded`). */\n active?: boolean\n }) {\n return (\n <ButtonPrimitive\n data-slot=\"button\"\n className={cn(\n buttonVariants({ variant, size, className }),\n active && \"bg-accent text-accent-foreground hover:bg-accent\"\n )}\n {...props}\n />\n )\n}\n\nexport { Button, buttonVariants }\n"]}
@@ -0,0 +1,23 @@
1
+ import { MOBILE_QUERY, SIDEBAR_QUERY, PANEL_QUERY } from './chunk-KNPRUZ4H.js';
2
+ import { useMedia } from 'react-use';
3
+
4
+ function isBelowMd() {
5
+ if (typeof window === "undefined") return false;
6
+ return window.matchMedia(MOBILE_QUERY).matches;
7
+ }
8
+ function useBelowMd() {
9
+ return useMedia(MOBILE_QUERY, false);
10
+ }
11
+ function useBelowSidebar() {
12
+ return useMedia(SIDEBAR_QUERY, false);
13
+ }
14
+ function useBelowPanel() {
15
+ return useMedia(PANEL_QUERY, false);
16
+ }
17
+ function useIsMobile() {
18
+ return useBelowMd();
19
+ }
20
+
21
+ export { isBelowMd, useBelowMd, useBelowPanel, useBelowSidebar, useIsMobile };
22
+ //# sourceMappingURL=chunk-QGMDWGJT.js.map
23
+ //# sourceMappingURL=chunk-QGMDWGJT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/use-mobile.ts"],"names":[],"mappings":";;;AAcO,SAAS,SAAA,GAAqB;AACpC,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,KAAA;AAC1C,EAAA,OAAO,MAAA,CAAO,UAAA,CAAW,YAAY,CAAA,CAAE,OAAA;AACxC;AAEO,SAAS,UAAA,GAAsB;AACrC,EAAA,OAAO,QAAA,CAAS,cAAc,KAAK,CAAA;AACpC;AASO,SAAS,eAAA,GAA2B;AAC1C,EAAA,OAAO,QAAA,CAAS,eAAe,KAAK,CAAA;AACrC;AAQO,SAAS,aAAA,GAAyB;AACxC,EAAA,OAAO,QAAA,CAAS,aAAa,KAAK,CAAA;AACnC;AASO,SAAS,WAAA,GAAuB;AACtC,EAAA,OAAO,UAAA,EAAW;AACnB","file":"chunk-QGMDWGJT.js","sourcesContent":["import {\n\tMOBILE_QUERY,\n\tPANEL_QUERY,\n\tSIDEBAR_QUERY,\n} from \"@hoardodile/ui/viewport\"\nimport { useMedia } from \"react-use\"\n\n/**\n * One-shot check for viewports below the `md` breakpoint (< 768 px).\n *\n * Prefer {@link useBelowMd} in React components so they re-render when the\n * viewport crosses the breakpoint. Use this helper only in event handlers,\n * utility functions, or effects where a reactive hook is not appropriate.\n */\nexport function isBelowMd(): boolean {\n\tif (typeof window === \"undefined\") return false\n\treturn window.matchMedia(MOBILE_QUERY).matches\n}\n\nexport function useBelowMd(): boolean {\n\treturn useMedia(MOBILE_QUERY, false)\n}\n\n/**\n * Reactive check for viewports below the app-shell sidebar breakpoint\n * (`max-sidebar`), where the fixed sidebar is replaced by the hamburger\n * drawer. Keep in sync with `SIDEBAR_BREAKPOINT_PX` and the\n * `sidebar:` / `max-sidebar:` CSS variants. The shell alone decides its own chrome;\n * compact-UI consumers keep using {@link useBelowMd}.\n */\nexport function useBelowSidebar(): boolean {\n\treturn useMedia(SIDEBAR_QUERY, false)\n}\n\n/**\n * Reactive check for viewports below the filter-rail breakpoint\n * (< 1440 px), where the rail becomes a drawer instead of a fixed right\n * column. Keep in sync with `PANEL_BREAKPOINT_PX` and the\n * `panel:` / `max-panel:` CSS variants.\n */\nexport function useBelowPanel(): boolean {\n\treturn useMedia(PANEL_QUERY, false)\n}\n\n/**\n * Compatibility alias for official shadcn-generated components (e.g.\n * `sidebar.tsx`), which import `useIsMobile` from this module. Mobile\n * detection rules are owned by THIS file for the whole repo (apps and\n * plugins alike) — never let the registry version of `use-mobile.ts`\n * replace it; restore after every `shadcn add`.\n */\nexport function useIsMobile(): boolean {\n\treturn useBelowMd()\n}\n"]}
@@ -0,0 +1,36 @@
1
+ import * as React from 'react';
2
+ import { useFormContext, useFormState } from 'react-hook-form';
3
+
4
+ // src/hooks/use-form-field.ts
5
+ var FormFieldContext = React.createContext(
6
+ void 0
7
+ );
8
+ var FormItemContext = React.createContext(
9
+ void 0
10
+ );
11
+ function useFormField() {
12
+ const fieldContext = React.useContext(FormFieldContext);
13
+ const itemContext = React.useContext(FormItemContext);
14
+ const { getFieldState } = useFormContext();
15
+ const formState = useFormState({ name: fieldContext?.name });
16
+ if (!fieldContext) {
17
+ throw new Error("useFormField should be used within <FormField>");
18
+ }
19
+ if (!itemContext) {
20
+ throw new Error("useFormField should be used within <FormItem>");
21
+ }
22
+ const fieldState = getFieldState(fieldContext.name, formState);
23
+ const { id } = itemContext;
24
+ return {
25
+ id,
26
+ name: fieldContext.name,
27
+ formItemId: `${id}-form-item`,
28
+ formDescriptionId: `${id}-form-item-description`,
29
+ formMessageId: `${id}-form-item-message`,
30
+ ...fieldState
31
+ };
32
+ }
33
+
34
+ export { FormFieldContext, FormItemContext, useFormField };
35
+ //# sourceMappingURL=chunk-QGTQQNUL.js.map
36
+ //# sourceMappingURL=chunk-QGTQQNUL.js.map