@weasel-js/ui 1.8.0 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. package/dist/chunks/{ActionBar-xA73vzzF.js → ActionBar-CfXS7-9f.js} +2 -2
  2. package/dist/chunks/{ActionBar-xA73vzzF.js.map → ActionBar-CfXS7-9f.js.map} +1 -1
  3. package/dist/chunks/{Badge-jYQjrTKc.js → Badge-4ShpqA0h.js} +2 -2
  4. package/dist/chunks/{Badge-jYQjrTKc.js.map → Badge-4ShpqA0h.js.map} +1 -1
  5. package/dist/chunks/{Badge-CTc9Lkwn.js → Badge-qkV9EqKs.js} +6 -2
  6. package/dist/chunks/Badge-qkV9EqKs.js.map +1 -0
  7. package/dist/chunks/{Button-XB-T75YL.js → Button-CQA-K1g9.js} +17 -17
  8. package/dist/chunks/Button-CQA-K1g9.js.map +1 -0
  9. package/dist/chunks/Button-KF-qSGtA.js +11 -0
  10. package/dist/chunks/Button-KF-qSGtA.js.map +1 -0
  11. package/dist/chunks/{ButtonBar-CIMKPPRS.js → ButtonBar-B-cYuoem.js} +10 -4
  12. package/dist/chunks/ButtonBar-B-cYuoem.js.map +1 -0
  13. package/dist/chunks/{Callout-DpTO3fkb.js → Callout-VyYDIP7w.js} +18 -18
  14. package/dist/chunks/Callout-VyYDIP7w.js.map +1 -0
  15. package/dist/chunks/{CloseButton-wb7Or5Eg.js → CloseButton-CKarkTT9.js} +3 -3
  16. package/dist/chunks/{CloseButton-wb7Or5Eg.js.map → CloseButton-CKarkTT9.js.map} +1 -1
  17. package/dist/chunks/{Code-AsKpgnv6.js → Code-ChwKP1V0.js} +8 -2
  18. package/dist/chunks/Code-ChwKP1V0.js.map +1 -0
  19. package/dist/chunks/{ColorModeControl-DET6Itwq.js → ColorModeControl-CH9PmDlT.js} +6 -6
  20. package/dist/chunks/{ColorModeControl-DET6Itwq.js.map → ColorModeControl-CH9PmDlT.js.map} +1 -1
  21. package/dist/chunks/{ComboBox-DKln_mOd.js → ComboBox-BMj_IKBj.js} +4 -4
  22. package/dist/chunks/ComboBox-BMj_IKBj.js.map +1 -0
  23. package/dist/chunks/{DataGrid-B6IS9vUk.js → DataGrid-CjiVKoaA.js} +2 -2
  24. package/dist/chunks/{DataGrid-B6IS9vUk.js.map → DataGrid-CjiVKoaA.js.map} +1 -1
  25. package/dist/chunks/{Dialog-h-5MeYpu.js → Dialog-Du4POZ52.js} +5 -5
  26. package/dist/chunks/Dialog-Du4POZ52.js.map +1 -0
  27. package/dist/chunks/{DragGhost-BeYDJyLZ.js → DragGhost-_IpuN_xM.js} +2 -2
  28. package/dist/chunks/{DragGhost-BeYDJyLZ.js.map → DragGhost-_IpuN_xM.js.map} +1 -1
  29. package/dist/chunks/{FallthroughDiagram-CS1IhQaU.js → FallthroughDiagram-Cn6QLWKA.js} +9 -9
  30. package/dist/chunks/{FallthroughDiagram-CS1IhQaU.js.map → FallthroughDiagram-Cn6QLWKA.js.map} +1 -1
  31. package/dist/chunks/{FillStrokeSwatch-C4h3rUFM.js → FillStrokeSwatch-BkUhlpQL.js} +2 -2
  32. package/dist/chunks/{FillStrokeSwatch-C4h3rUFM.js.map → FillStrokeSwatch-BkUhlpQL.js.map} +1 -1
  33. package/dist/chunks/{FontFamilySelect-DEvMXyZd.js → FontFamilySelect-CXN7O2-p.js} +2 -2
  34. package/dist/chunks/{FontFamilySelect-DEvMXyZd.js.map → FontFamilySelect-CXN7O2-p.js.map} +1 -1
  35. package/dist/chunks/{FontWeightSelect-BLQ_LMJF.js → FontWeightSelect-BBuJGe_2.js} +2 -2
  36. package/dist/chunks/{FontWeightSelect-BLQ_LMJF.js.map → FontWeightSelect-BBuJGe_2.js.map} +1 -1
  37. package/dist/chunks/{GestureRoute-BXESrHI0.js → GestureRoute-BqHUrGeQ.js} +13 -13
  38. package/dist/chunks/{GestureRoute-BXESrHI0.js.map → GestureRoute-BqHUrGeQ.js.map} +1 -1
  39. package/dist/chunks/{GradientEditor-vz771M-N.js → GradientEditor-DX4sb1Hf.js} +3 -3
  40. package/dist/chunks/{GradientEditor-vz771M-N.js.map → GradientEditor-DX4sb1Hf.js.map} +1 -1
  41. package/dist/chunks/{Icon-DPRUIo3G.js → Icon-BfqYtAX5.js} +30 -1
  42. package/dist/chunks/Icon-BfqYtAX5.js.map +1 -0
  43. package/dist/chunks/{ItemList-CRu9y7hL.js → ItemList-nbakLf3N.js} +32 -32
  44. package/dist/chunks/ItemList-nbakLf3N.js.map +1 -0
  45. package/dist/chunks/{Jog-TE6QI9i1.js → Jog-BU0xuM3G.js} +6 -6
  46. package/dist/chunks/{Jog-TE6QI9i1.js.map → Jog-BU0xuM3G.js.map} +1 -1
  47. package/dist/chunks/{Keycaps-C-FJyG8k.js → Keycaps-CM-gOj0e.js} +8 -2
  48. package/dist/chunks/Keycaps-CM-gOj0e.js.map +1 -0
  49. package/dist/chunks/LayerList-vfAoImxX.js +432 -0
  50. package/dist/chunks/LayerList-vfAoImxX.js.map +1 -0
  51. package/dist/chunks/{ListEditor-DGtwj99m.js → ListEditor-BAKs47bN.js} +3 -3
  52. package/dist/chunks/{ListEditor-DGtwj99m.js.map → ListEditor-BAKs47bN.js.map} +1 -1
  53. package/dist/chunks/{MenuButton-TAFg88Lp.js → MenuButton-CJGdhwGr.js} +5 -5
  54. package/dist/chunks/MenuButton-CJGdhwGr.js.map +1 -0
  55. package/dist/chunks/{MeshEditor-8CRsu-Td.js → MeshEditor-rhf7cXfG.js} +2 -2
  56. package/dist/chunks/{MeshEditor-8CRsu-Td.js.map → MeshEditor-rhf7cXfG.js.map} +1 -1
  57. package/dist/chunks/{OptionsBar-DeTAjVb7.js → OptionsBar-DyTXbev-.js} +9 -3
  58. package/dist/chunks/OptionsBar-DyTXbev-.js.map +1 -0
  59. package/dist/chunks/{PaintField-DX4uYlF9.js → PaintField--GbF8sL7.js} +7 -7
  60. package/dist/chunks/PaintField--GbF8sL7.js.map +1 -0
  61. package/dist/chunks/{PaintInput-CWyOJXUM.js → PaintInput-DSS5IjXn.js} +5 -5
  62. package/dist/chunks/{PaintInput-CWyOJXUM.js.map → PaintInput-DSS5IjXn.js.map} +1 -1
  63. package/dist/chunks/{PatternPicker-0j51aDJ3.js → PatternPicker-82DCUvUk.js} +2 -2
  64. package/dist/chunks/{PatternPicker-0j51aDJ3.js.map → PatternPicker-82DCUvUk.js.map} +1 -1
  65. package/dist/chunks/{Powerline-6wIFmDik.js → Powerline-CogJv9VU.js} +2 -2
  66. package/dist/chunks/{Powerline-6wIFmDik.js.map → Powerline-CogJv9VU.js.map} +1 -1
  67. package/dist/chunks/{Prefs-gbjYqvSD.js → Prefs-BSSKOvPa.js} +41 -41
  68. package/dist/chunks/Prefs-BSSKOvPa.js.map +1 -0
  69. package/dist/chunks/{Properties-BbNuMiic.js → Properties-DteSVEWs.js} +4 -4
  70. package/dist/chunks/{Properties-BbNuMiic.js.map → Properties-DteSVEWs.js.map} +1 -1
  71. package/dist/chunks/{PropertyField-CWUtNK1w.js → PropertyField-CLbUOynA.js} +67 -67
  72. package/dist/chunks/PropertyField-CLbUOynA.js.map +1 -0
  73. package/dist/chunks/{Select-ouV8O6cp.js → Select-BhHwjNsb.js} +13 -13
  74. package/dist/chunks/Select-BhHwjNsb.js.map +1 -0
  75. package/dist/chunks/{SelectionPanel-BpUqP75b.js → SelectionPanel-B6HinSdB.js} +2 -2
  76. package/dist/chunks/{SelectionPanel-BpUqP75b.js.map → SelectionPanel-B6HinSdB.js.map} +1 -1
  77. package/dist/chunks/{SidebarPanel--dJ-k9-V.js → SidebarPanel-P3a8tIIj.js} +2 -2
  78. package/dist/chunks/{SidebarPanel--dJ-k9-V.js.map → SidebarPanel-P3a8tIIj.js.map} +1 -1
  79. package/dist/chunks/{StatusBar-BEOfOX0D.js → StatusBar-DEh7J2Gj.js} +14 -9
  80. package/dist/chunks/StatusBar-DEh7J2Gj.js.map +1 -0
  81. package/dist/chunks/{SwatchStrip-BY6A511W.js → SwatchStrip-D7YNn_p0.js} +2 -2
  82. package/dist/chunks/{SwatchStrip-BY6A511W.js.map → SwatchStrip-D7YNn_p0.js.map} +1 -1
  83. package/dist/chunks/{ThemeSwitcher-tnATkIo1.js → ThemeSwitcher-BJWP1l87.js} +6 -6
  84. package/dist/chunks/{ThemeSwitcher-tnATkIo1.js.map → ThemeSwitcher-BJWP1l87.js.map} +1 -1
  85. package/dist/chunks/{Timeline-FlLUm8nd.js → Timeline-CisRg82u.js} +2 -2
  86. package/dist/chunks/{Timeline-FlLUm8nd.js.map → Timeline-CisRg82u.js.map} +1 -1
  87. package/dist/chunks/{Toast-CuHkT0x0.js → Toast-DhkU9Z0b.js} +3 -3
  88. package/dist/chunks/{Toast-CuHkT0x0.js.map → Toast-DhkU9Z0b.js.map} +1 -1
  89. package/dist/chunks/ToggleBar-CRc2k89_.js +10 -0
  90. package/dist/chunks/ToggleBar-CRc2k89_.js.map +1 -0
  91. package/dist/chunks/{ToggleBar-BZ4Gk1IU.js → ToggleBar-CfYbMK3x.js} +9 -9
  92. package/dist/chunks/ToggleBar-CfYbMK3x.js.map +1 -0
  93. package/dist/chunks/{TokenPanel-DongdiZM.js → TokenPanel-cL37E39j.js} +5 -5
  94. package/dist/chunks/{TokenPanel-DongdiZM.js.map → TokenPanel-cL37E39j.js.map} +1 -1
  95. package/dist/chunks/{ToolButton-BURp4YIO.js → ToolButton-Bo7WH1wt.js} +2 -2
  96. package/dist/chunks/{ToolButton-BURp4YIO.js.map → ToolButton-Bo7WH1wt.js.map} +1 -1
  97. package/dist/chunks/{ToolOptionsBar-wTPJFfOn.js → ToolOptionsBar-DmhzrOmd.js} +2 -2
  98. package/dist/chunks/{ToolOptionsBar-wTPJFfOn.js.map → ToolOptionsBar-DmhzrOmd.js.map} +1 -1
  99. package/dist/chunks/{ToolPalette-DKDFWOX-.js → ToolPalette-BTD84k9C.js} +2 -2
  100. package/dist/chunks/{ToolPalette-DKDFWOX-.js.map → ToolPalette-BTD84k9C.js.map} +1 -1
  101. package/dist/chunks/{Tooltip-C6eFCYxw.js → Tooltip-DljsPEQG.js} +4 -4
  102. package/dist/chunks/Tooltip-DljsPEQG.js.map +1 -0
  103. package/dist/chunks/Tree-B_PW6K-W.js +215 -0
  104. package/dist/chunks/Tree-B_PW6K-W.js.map +1 -0
  105. package/dist/chunks/{icons-CofIOTiI.js → icons-B_fPKEis.js} +52 -43
  106. package/dist/chunks/icons-B_fPKEis.js.map +1 -0
  107. package/dist/chunks/{model-qQj8Ny9w.js → model-Ca0f95r0.js} +5 -5
  108. package/dist/chunks/{model-qQj8Ny9w.js.map → model-Ca0f95r0.js.map} +1 -1
  109. package/dist/chunks/{portalHost-CE70wLzo.js → portalHost-RUQPxIoW.js} +3 -2
  110. package/dist/chunks/{portalHost-CE70wLzo.js.map → portalHost-RUQPxIoW.js.map} +1 -1
  111. package/dist/chunks/{prefField-CCm_nzlM.js → prefField-DPPbAmRR.js} +3 -3
  112. package/dist/chunks/{prefField-CCm_nzlM.js.map → prefField-DPPbAmRR.js.map} +1 -1
  113. package/dist/chunks/{segmentTooltip-CGxCOWNz.js → segmentTooltip-8VBPNgEc.js} +2 -2
  114. package/dist/chunks/{segmentTooltip-CGxCOWNz.js.map → segmentTooltip-8VBPNgEc.js.map} +1 -1
  115. package/dist/chunks/segmentedControl.module-62qjNWJk.js +11 -0
  116. package/dist/chunks/{segmentedControl.module-BzGXUL5G.js.map → segmentedControl.module-62qjNWJk.js.map} +1 -1
  117. package/dist/chunks/{useReorderDragList-CStYKjHl.js → useReorderDragList-DnqqvRGK.js} +38 -30
  118. package/dist/chunks/useReorderDragList-DnqqvRGK.js.map +1 -0
  119. package/dist/components/ActionBar/index.js +1 -1
  120. package/dist/components/Badge/index.d.ts +1 -0
  121. package/dist/components/Badge/index.d.ts.map +1 -1
  122. package/dist/components/Badge/index.js +3 -3
  123. package/dist/components/Badge/variantIcons.d.ts +5 -0
  124. package/dist/components/Badge/variantIcons.d.ts.map +1 -0
  125. package/dist/components/Button/index.d.ts +1 -0
  126. package/dist/components/Button/index.d.ts.map +1 -1
  127. package/dist/components/Button/index.js +3 -2
  128. package/dist/components/Button/variantIcons.d.ts +5 -0
  129. package/dist/components/Button/variantIcons.d.ts.map +1 -0
  130. package/dist/components/ButtonBar/index.d.ts +1 -0
  131. package/dist/components/ButtonBar/index.d.ts.map +1 -1
  132. package/dist/components/ButtonBar/index.js +2 -2
  133. package/dist/components/ButtonBar/variantIcons.d.ts +5 -0
  134. package/dist/components/ButtonBar/variantIcons.d.ts.map +1 -0
  135. package/dist/components/Callout/Callout.d.ts.map +1 -1
  136. package/dist/components/Callout/index.js +1 -1
  137. package/dist/components/CloseButton/index.js +1 -1
  138. package/dist/components/Code/index.d.ts +1 -0
  139. package/dist/components/Code/index.d.ts.map +1 -1
  140. package/dist/components/Code/index.js +2 -2
  141. package/dist/components/Code/variantIcons.d.ts +5 -0
  142. package/dist/components/Code/variantIcons.d.ts.map +1 -0
  143. package/dist/components/ColorModeControl/index.js +1 -1
  144. package/dist/components/ComboBox/ComboBox.d.ts.map +1 -1
  145. package/dist/components/ComboBox/index.js +1 -1
  146. package/dist/components/DataGrid/index.js +1 -1
  147. package/dist/components/Dialog/Dialog.d.ts.map +1 -1
  148. package/dist/components/Dialog/index.js +1 -1
  149. package/dist/components/DragGhost/index.js +1 -1
  150. package/dist/components/FallthroughDiagram/index.js +1 -1
  151. package/dist/components/FillStrokeSwatch/index.js +1 -1
  152. package/dist/components/FontFamilySelect/index.js +1 -1
  153. package/dist/components/FontWeightSelect/index.js +1 -1
  154. package/dist/components/GestureRoute/index.js +1 -1
  155. package/dist/components/GradientEditor/index.js +1 -1
  156. package/dist/components/ItemList/index.js +1 -1
  157. package/dist/components/Jog/index.js +1 -1
  158. package/dist/components/Keycaps/index.d.ts +1 -0
  159. package/dist/components/Keycaps/index.d.ts.map +1 -1
  160. package/dist/components/Keycaps/index.js +2 -2
  161. package/dist/components/Keycaps/variantIcons.d.ts +5 -0
  162. package/dist/components/Keycaps/variantIcons.d.ts.map +1 -0
  163. package/dist/components/LayerList/LayerList.d.ts.map +1 -1
  164. package/dist/components/LayerList/index.js +1 -1
  165. package/dist/components/ListEditor/index.js +1 -1
  166. package/dist/components/MenuButton/MenuButton.d.ts.map +1 -1
  167. package/dist/components/MenuButton/index.js +1 -1
  168. package/dist/components/MeshEditor/index.js +1 -1
  169. package/dist/components/OptionsBar/index.d.ts +1 -0
  170. package/dist/components/OptionsBar/index.d.ts.map +1 -1
  171. package/dist/components/OptionsBar/index.js +2 -2
  172. package/dist/components/OptionsBar/variantIcons.d.ts +5 -0
  173. package/dist/components/OptionsBar/variantIcons.d.ts.map +1 -0
  174. package/dist/components/PaintField/PaintField.d.ts.map +1 -1
  175. package/dist/components/PaintField/index.js +1 -1
  176. package/dist/components/PaintInput/index.js +1 -1
  177. package/dist/components/PatternPicker/index.js +1 -1
  178. package/dist/components/Powerline/index.js +2 -2
  179. package/dist/components/Prefs/index.js +2 -2
  180. package/dist/components/Properties/index.js +2 -2
  181. package/dist/components/Select/Select.d.ts.map +1 -1
  182. package/dist/components/Select/index.js +1 -1
  183. package/dist/components/SelectionPanel/index.js +2 -2
  184. package/dist/components/SidebarPanel/index.js +1 -1
  185. package/dist/components/StatusBar/StatusBar.d.ts +2 -0
  186. package/dist/components/StatusBar/StatusBar.d.ts.map +1 -1
  187. package/dist/components/StatusBar/index.js +1 -1
  188. package/dist/components/SwatchStrip/index.js +1 -1
  189. package/dist/components/ThemeSwitcher/index.js +1 -1
  190. package/dist/components/Timeline/index.js +1 -1
  191. package/dist/components/Toast/index.js +1 -1
  192. package/dist/components/ToggleBar/index.d.ts +1 -0
  193. package/dist/components/ToggleBar/index.d.ts.map +1 -1
  194. package/dist/components/ToggleBar/index.js +3 -2
  195. package/dist/components/ToggleBar/variantIcons.d.ts +5 -0
  196. package/dist/components/ToggleBar/variantIcons.d.ts.map +1 -0
  197. package/dist/components/TokenPanel/index.js +1 -1
  198. package/dist/components/ToolButton/index.js +1 -1
  199. package/dist/components/ToolOptionsBar/index.js +1 -1
  200. package/dist/components/ToolPalette/index.js +1 -1
  201. package/dist/components/Tooltip/Tooltip.d.ts.map +1 -1
  202. package/dist/components/Tooltip/index.js +1 -1
  203. package/dist/components/Tree/Tree.d.ts +1 -1
  204. package/dist/components/Tree/Tree.d.ts.map +1 -1
  205. package/dist/components/Tree/index.js +1 -1
  206. package/dist/icons/index.d.ts +6 -0
  207. package/dist/icons/index.d.ts.map +1 -1
  208. package/dist/icons/paths.d.ts +12 -0
  209. package/dist/icons/paths.d.ts.map +1 -1
  210. package/dist/index.js +100 -98
  211. package/dist/index.js.map +1 -1
  212. package/dist/overlays/portalHost.d.ts +10 -4
  213. package/dist/overlays/portalHost.d.ts.map +1 -1
  214. package/dist/style.css +1 -1
  215. package/dist/useReorderDragList.d.ts +3 -0
  216. package/dist/useReorderDragList.d.ts.map +1 -1
  217. package/package.json +10 -9
  218. package/dist/chunks/Badge-CTc9Lkwn.js.map +0 -1
  219. package/dist/chunks/Button-XB-T75YL.js.map +0 -1
  220. package/dist/chunks/ButtonBar-CIMKPPRS.js.map +0 -1
  221. package/dist/chunks/Callout-DpTO3fkb.js.map +0 -1
  222. package/dist/chunks/Code-AsKpgnv6.js.map +0 -1
  223. package/dist/chunks/ComboBox-DKln_mOd.js.map +0 -1
  224. package/dist/chunks/Dialog-h-5MeYpu.js.map +0 -1
  225. package/dist/chunks/Icon-DPRUIo3G.js.map +0 -1
  226. package/dist/chunks/ItemList-CRu9y7hL.js.map +0 -1
  227. package/dist/chunks/Keycaps-C-FJyG8k.js.map +0 -1
  228. package/dist/chunks/LayerList-BXTAjK-4.js +0 -421
  229. package/dist/chunks/LayerList-BXTAjK-4.js.map +0 -1
  230. package/dist/chunks/MenuButton-TAFg88Lp.js.map +0 -1
  231. package/dist/chunks/OptionsBar-DeTAjVb7.js.map +0 -1
  232. package/dist/chunks/PaintField-DX4uYlF9.js.map +0 -1
  233. package/dist/chunks/Prefs-gbjYqvSD.js.map +0 -1
  234. package/dist/chunks/PropertyField-CWUtNK1w.js.map +0 -1
  235. package/dist/chunks/Select-ouV8O6cp.js.map +0 -1
  236. package/dist/chunks/StatusBar-BEOfOX0D.js.map +0 -1
  237. package/dist/chunks/ToggleBar-BZ4Gk1IU.js.map +0 -1
  238. package/dist/chunks/Tooltip-C6eFCYxw.js.map +0 -1
  239. package/dist/chunks/Tree-hzIbj2q1.js +0 -203
  240. package/dist/chunks/Tree-hzIbj2q1.js.map +0 -1
  241. package/dist/chunks/icons-CofIOTiI.js.map +0 -1
  242. package/dist/chunks/interactiveTarget-BNZmTow2.js +0 -24
  243. package/dist/chunks/interactiveTarget-BNZmTow2.js.map +0 -1
  244. package/dist/chunks/segmentedControl.module-BzGXUL5G.js +0 -11
  245. package/dist/chunks/useReorderDragList-CStYKjHl.js.map +0 -1
  246. package/dist/interactiveTarget.d.ts +0 -9
  247. package/dist/interactiveTarget.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"DataGrid-B6IS9vUk.js","names":["s","_Fragment","Fragment","useReorderDragList","Fragment$1","DataGrid_module_default","INTERACTIVE","fromControl","hit","DataGrid","props","sort","setSort","useState","defaultSort","ownExpanded","setOwnExpanded","defaultExpandedIds","expanded","expandedIds","detailIdBase","useId","toggleExpanded","next","id","onExpandedChange","sortedRows","useMemo","rows","col","columns","c","get","row","dir","av","a","bv","b","cycleSort","cur","columnId","drag","r","onReorder","dragEnabled","detailEnabled","renderDetail","colCount","cls","className","sortable","active","indicator","Fragment","empty","isDragging","target","expandable","rowExpandable","isExpanded","detail","detailId","rowCls","onRowClick","i","rowClassName","e","Disclosure","content"],"sources":["../../src/components/DataGrid/DataGrid.module.css","../../src/components/DataGrid/DataGrid.tsx"],"sourcesContent":[".grid {\n position: relative;\n width: 100%;\n}\n\n.table {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--wzl-font-size-sm);\n}\n\n.table th,\n.table td {\n text-align: left;\n padding: 3px var(--wzl-space-3);\n vertical-align: middle;\n}\n.numeric {\n composes: numeric from '@weasel-js/theme/numeric.module.css';\n}\n.table .numeric {\n text-align: end;\n}\n.table tbody tr {\n border-bottom: 1px solid var(--wzl-line-subtle);\n}\n\n.table th {\n font-weight: var(--wzl-font-weight-bold);\n color: var(--wzl-fg-muted);\n position: sticky;\n top: 0;\n background: var(--wzl-surface);\n z-index: 1;\n border-bottom: 1px solid var(--wzl-border);\n}\n\n.sortButton {\n all: unset;\n cursor: pointer;\n user-select: none;\n display: block;\n width: 100%;\n}\n.sortButton:hover { color: var(--wzl-fg); }\n.sortButton:focus-visible {\n outline: 2px solid var(--wzl-focus-ring);\n outline-offset: -2px;\n}\n.sortActive { color: var(--wzl-fg); }\n\n.empty {\n color: var(--wzl-fg-muted);\n font-style: italic;\n padding: var(--wzl-space-4) 0;\n text-align: center;\n}\n\n/* Drag-handle column. */\n.handleCol { width: 14px; }\n.handleCell {\n width: 14px;\n cursor: grab;\n color: var(--wzl-fg-muted);\n user-select: none;\n text-align: center;\n}\n.handleCell:active { cursor: grabbing; }\n\n.dragging { opacity: 0.4; }\n\n.dropBefore > td { box-shadow: inset 0 2px 0 0 var(--wzl-accent); }\n.dropAfter > td { box-shadow: inset 0 -2px 0 0 var(--wzl-accent); }\n\n/* Row activation (`onRowClick`). */\n.clickable { cursor: pointer; }\n.clickable:hover > td { background: var(--wzl-surface-hover); }\n.clickable:focus-visible {\n outline: 2px solid var(--wzl-focus-ring);\n outline-offset: -2px;\n}\n\n/* Disclosure column and the detail row it opens. */\n.detailCol,\n.detailCell {\n width: var(--wzl-control-h-sm);\n padding-inline: 0;\n text-align: center;\n}\n.table .detailCell { padding-block: 0; }\n.detailRow > td {\n padding: var(--wzl-space-2) var(--wzl-space-3) var(--wzl-space-3);\n background: var(--wzl-surface-sunken);\n}\n","/**\n * Lightweight sortable data grid. Use for inspector tables — action\n * registries, route maps, telemetry rows. Intentionally narrow:\n *\n * - Sortable columns (click header to cycle asc → desc → none)\n * - Custom cell render via per-column `render`\n * - Optional drag handles for row reordering (when `onReorder` is set)\n * - Per-row class, row activation, and expandable full-width detail rows\n *\n * Not: virtual scrolling, column resizing, multi-column sort, filtering,\n * inline editing. Reach for a real grid library when you need those.\n */\nimport { Fragment, useId, useMemo, useState } from 'react';\nimport type { KeyboardEvent, MouseEvent, ReactNode } from 'react';\nimport { useReorderDragList } from '../../useReorderDragList';\nimport { Disclosure } from '../Disclosure';\nimport s from './DataGrid.module.css';\n\n/**\n * One column of a {@link DataGrid}. `id` doubles as the default property name\n * read off each row when no `accessor` is given.\n */\nexport interface DataGridColumn<Row> {\n id: string;\n header: ReactNode;\n /** Pull a sortable value from the row. Defaults to `(row as any)[column.id]`. */\n accessor?: (row: Row) => string | number | null | undefined;\n /** Render the cell. Defaults to `String(accessor(row) ?? '')`. */\n render?: (row: Row) => ReactNode;\n /** Default true. Set false to suppress the sort affordance on this column. */\n sortable?: boolean;\n /** Optional CSS class on every <td> in this column. */\n className?: string;\n /** A column of figures: equal-width digits, aligned to the end so the column\n * reads down. Header included, so its label sits over the numbers. */\n numeric?: boolean;\n}\n\n/** Props for {@link DataGrid}. */\nexport interface DataGridProps<Row extends { id: string }> {\n rows: readonly Row[];\n columns: readonly DataGridColumn<Row>[];\n /** Initial sort. Omit for unsorted (rows render in input order). */\n defaultSort?: { columnId: string; direction: 'asc' | 'desc' };\n /** Enable drag handles in a leading column. Receives the reorder spec\n * (the existing `useReorderDragList` semantics — see weasel-ui). */\n onReorder?: (ids: string[], targetIndex: number) => void;\n /** Empty-state row text. Default `'—'`. */\n empty?: ReactNode;\n className?: string;\n /** Extra class on a row's `<tr>`, for row-level state such as a verdict. */\n rowClassName?: (row: Row) => string | undefined;\n /**\n * Activate a row. Makes each row focusable: a click anywhere in it, or\n * Enter/Space while it has focus, calls this. Clicks and keys landing on a\n * control inside a cell (button, link, input, …) are left to that control.\n */\n onRowClick?: (row: Row) => void;\n /**\n * Content for a full-width detail row under `row`, shown while the row is\n * expanded. Setting it adds a leading disclosure column. Return `null` for\n * no detail row.\n */\n renderDetail?: (row: Row) => ReactNode;\n /** Which rows offer the disclosure. Default: every row. */\n rowExpandable?: (row: Row) => boolean;\n /** Expanded row ids, controlled. Pair with `onExpandedChange`. */\n expandedIds?: ReadonlySet<string>;\n /** Initially expanded row ids when uncontrolled. */\n defaultExpandedIds?: Iterable<string>;\n /** Called with the next expanded set when a disclosure toggles. */\n onExpandedChange?: (ids: ReadonlySet<string>) => void;\n}\n\ntype SortState = { columnId: string; direction: 'asc' | 'desc' } | null;\n\nconst INTERACTIVE = 'a[href], button, input, select, textarea, label, summary, [role=\"button\"], [contenteditable=\"true\"]';\n\n/** Did this event start on a control inside the row rather than on the row itself? */\nfunction fromControl(e: MouseEvent | KeyboardEvent): boolean {\n const target = e.target as Element | null;\n const hit = target?.closest?.(INTERACTIVE);\n return !!hit && e.currentTarget.contains(hit);\n}\n\n/**\n * Sortable table for inspector-style data. Rows are keyed by `id`. Clicking a\n * sortable header cycles ascending, descending, unsorted; nullish values sort\n * last regardless of direction. Passing `onReorder` adds a leading drag-handle\n * column — note that the drag indices it reports are into the *sorted* row\n * order, not the input order.\n */\nexport function DataGrid<Row extends { id: string }>(props: DataGridProps<Row>) {\n const {\n rows, columns, defaultSort, onReorder, empty = '—', className,\n rowClassName, onRowClick, renderDetail, rowExpandable, expandedIds, defaultExpandedIds, onExpandedChange,\n } = props;\n const [sort, setSort] = useState<SortState>(defaultSort ?? null);\n const [ownExpanded, setOwnExpanded] = useState<ReadonlySet<string>>(() => new Set(defaultExpandedIds));\n const expanded = expandedIds ?? ownExpanded;\n const detailIdBase = useId();\n\n const toggleExpanded = (id: string) => {\n const next = new Set(expanded);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n if (!expandedIds) setOwnExpanded(next);\n onExpandedChange?.(next);\n };\n\n const sortedRows = useMemo(() => {\n if (!sort) return [...rows];\n const col = columns.find((c) => c.id === sort.columnId);\n if (!col) return [...rows];\n const get = col.accessor ?? ((row: Row) => (row as unknown as Record<string, unknown>)[col.id] as string | number | null | undefined);\n const dir = sort.direction === 'asc' ? 1 : -1;\n return [...rows].sort((a, b) => {\n const av = get(a);\n const bv = get(b);\n if (av == null && bv == null) return 0;\n if (av == null) return 1;\n if (bv == null) return -1;\n if (av < bv) return -1 * dir;\n if (av > bv) return 1 * dir;\n return 0;\n });\n }, [rows, columns, sort]);\n\n const cycleSort = (columnId: string) => {\n setSort((cur) => {\n if (!cur || cur.columnId !== columnId) return { columnId, direction: 'asc' };\n if (cur.direction === 'asc') return { columnId, direction: 'desc' };\n return null;\n });\n };\n\n // Drag handles via useReorderDragList. Adapter: the hook expects\n // `{ id, label }`-shaped items; we map our rows to that shape (label\n // unused — the hook only uses id and locked).\n const drag = useReorderDragList({\n items: sortedRows.map((r) => ({ id: r.id, label: '' })),\n selectedIds: [],\n onReorder: onReorder ?? (() => {}),\n rowSelector: ':not([data-detail-of])',\n });\n const dragEnabled = !!onReorder;\n const detailEnabled = !!renderDetail;\n const colCount = columns.length + (dragEnabled ? 1 : 0) + (detailEnabled ? 1 : 0);\n\n const cls = [s.grid, className].filter(Boolean).join(' ');\n\n return (\n <div\n className={cls}\n >\n <table className={s.table}>\n <thead>\n <tr>\n {dragEnabled && <th className={s.handleCol} aria-label=\"drag\" />}\n {detailEnabled && <th className={s.detailCol} aria-label=\"details\" />}\n {columns.map((col) => {\n const sortable = col.sortable !== false;\n const active = sort?.columnId === col.id;\n const indicator = active ? (sort.direction === 'asc' ? ' ↑' : ' ↓') : '';\n return (\n <th\n key={col.id}\n className={[col.className, col.numeric && s.numeric, active && s.sortActive].filter(Boolean).join(' ') || undefined}\n aria-sort={sortable ? (active ? (sort.direction === 'asc' ? 'ascending' : 'descending') : 'none') : undefined}\n >\n {sortable ? (\n <button type=\"button\" className={s.sortButton} onClick={() => cycleSort(col.id)}>\n {col.header}{indicator}\n </button>\n ) : (\n <>{col.header}{indicator}</>\n )}\n </th>\n );\n })}\n </tr>\n </thead>\n <tbody ref={dragEnabled ? (drag.containerProps.ref as React.RefCallback<HTMLTableSectionElement>) : undefined}>\n {sortedRows.length === 0 && (\n <tr>\n <td colSpan={colCount} className={s.empty}>\n {empty}\n </td>\n </tr>\n )}\n {sortedRows.map((row, i) => {\n const isDragging = drag.state.draggedIds?.includes(row.id) ?? false;\n const target = drag.state.targetIndex;\n const expandable = detailEnabled && (rowExpandable?.(row) ?? true);\n const isExpanded = expandable && expanded.has(row.id);\n const detail = isExpanded ? renderDetail!(row) : null;\n const detailId = `${detailIdBase}-detail-${row.id}`;\n const rowCls = [\n onRowClick && s.clickable,\n isDragging && s.dragging,\n target === i && s.dropBefore,\n target === sortedRows.length && i === sortedRows.length - 1 && s.dropAfter,\n rowClassName?.(row),\n ].filter(Boolean).join(' ');\n return (\n <Fragment key={row.id}>\n <tr\n className={rowCls || undefined}\n data-expanded={isExpanded || undefined}\n tabIndex={onRowClick ? 0 : undefined}\n onClick={onRowClick ? (e) => { if (!fromControl(e)) onRowClick(row); } : undefined}\n onKeyDown={onRowClick ? (e) => {\n if (e.target !== e.currentTarget || (e.key !== 'Enter' && e.key !== ' ')) return;\n e.preventDefault();\n onRowClick(row);\n } : undefined}\n >\n {dragEnabled && (\n <td\n className={s.handleCell}\n onPointerDown={(e) => drag.rowProps(row.id, i).onPointerDown(e)}\n >\n <span aria-hidden=\"true\">⋮⋮</span>\n </td>\n )}\n {detailEnabled && (\n <td className={s.detailCell}>\n {expandable && (\n <Disclosure\n open={isExpanded}\n onToggle={() => toggleExpanded(row.id)}\n label={isExpanded ? 'Hide details' : 'Show details'}\n controls={detail != null ? detailId : undefined}\n />\n )}\n </td>\n )}\n {columns.map((col) => {\n const get = col.accessor ?? ((r: Row) => (r as unknown as Record<string, unknown>)[col.id] as string | number | null | undefined);\n const content = col.render ? col.render(row) : String(get(row) ?? '');\n return (\n <td key={col.id} className={[col.className, col.numeric && s.numeric].filter(Boolean).join(' ') || undefined}>\n {content}\n </td>\n );\n })}\n </tr>\n {detail != null && (\n <tr id={detailId} className={s.detailRow} data-detail-of={row.id}>\n <td colSpan={colCount}>{detail}</td>\n </tr>\n )}\n </Fragment>\n );\n })}\n </tbody>\n </table>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DataGrid-CjiVKoaA.js","names":["s","_Fragment","Fragment","useReorderDragList","Fragment$1","DataGrid_module_default","INTERACTIVE","fromControl","hit","DataGrid","props","sort","setSort","useState","defaultSort","ownExpanded","setOwnExpanded","defaultExpandedIds","expanded","expandedIds","detailIdBase","useId","toggleExpanded","next","id","onExpandedChange","sortedRows","useMemo","rows","col","columns","c","get","row","dir","av","a","bv","b","cycleSort","cur","columnId","drag","r","onReorder","dragEnabled","detailEnabled","renderDetail","colCount","cls","className","sortable","active","indicator","Fragment","empty","isDragging","target","expandable","rowExpandable","isExpanded","detail","detailId","rowCls","onRowClick","i","rowClassName","e","Disclosure","content"],"sources":["../../src/components/DataGrid/DataGrid.module.css","../../src/components/DataGrid/DataGrid.tsx"],"sourcesContent":[".grid {\n position: relative;\n width: 100%;\n}\n\n.table {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--wzl-font-size-sm);\n}\n\n.table th,\n.table td {\n text-align: left;\n padding: 3px var(--wzl-space-3);\n vertical-align: middle;\n}\n.numeric {\n composes: numeric from '@weasel-js/theme/numeric.module.css';\n}\n.table .numeric {\n text-align: end;\n}\n.table tbody tr {\n border-bottom: 1px solid var(--wzl-line-subtle);\n}\n\n.table th {\n font-weight: var(--wzl-font-weight-bold);\n color: var(--wzl-fg-muted);\n position: sticky;\n top: 0;\n background: var(--wzl-surface);\n z-index: 1;\n border-bottom: 1px solid var(--wzl-border);\n}\n\n.sortButton {\n all: unset;\n cursor: pointer;\n user-select: none;\n display: block;\n width: 100%;\n}\n.sortButton:hover { color: var(--wzl-fg); }\n.sortButton:focus-visible {\n outline: 2px solid var(--wzl-focus-ring);\n outline-offset: -2px;\n}\n.sortActive { color: var(--wzl-fg); }\n\n.empty {\n color: var(--wzl-fg-muted);\n font-style: italic;\n padding: var(--wzl-space-4) 0;\n text-align: center;\n}\n\n/* Drag-handle column. */\n.handleCol { width: 14px; }\n.handleCell {\n width: 14px;\n cursor: grab;\n color: var(--wzl-fg-muted);\n user-select: none;\n text-align: center;\n}\n.handleCell:active { cursor: grabbing; }\n\n.dragging { opacity: 0.4; }\n\n.dropBefore > td { box-shadow: inset 0 2px 0 0 var(--wzl-accent); }\n.dropAfter > td { box-shadow: inset 0 -2px 0 0 var(--wzl-accent); }\n\n/* Row activation (`onRowClick`). */\n.clickable { cursor: pointer; }\n.clickable:hover > td { background: var(--wzl-surface-hover); }\n.clickable:focus-visible {\n outline: 2px solid var(--wzl-focus-ring);\n outline-offset: -2px;\n}\n\n/* Disclosure column and the detail row it opens. */\n.detailCol,\n.detailCell {\n width: var(--wzl-control-h-sm);\n padding-inline: 0;\n text-align: center;\n}\n.table .detailCell { padding-block: 0; }\n.detailRow > td {\n padding: var(--wzl-space-2) var(--wzl-space-3) var(--wzl-space-3);\n background: var(--wzl-surface-sunken);\n}\n","/**\n * Lightweight sortable data grid. Use for inspector tables — action\n * registries, route maps, telemetry rows. Intentionally narrow:\n *\n * - Sortable columns (click header to cycle asc → desc → none)\n * - Custom cell render via per-column `render`\n * - Optional drag handles for row reordering (when `onReorder` is set)\n * - Per-row class, row activation, and expandable full-width detail rows\n *\n * Not: virtual scrolling, column resizing, multi-column sort, filtering,\n * inline editing. Reach for a real grid library when you need those.\n */\nimport { Fragment, useId, useMemo, useState } from 'react';\nimport type { KeyboardEvent, MouseEvent, ReactNode } from 'react';\nimport { useReorderDragList } from '../../useReorderDragList';\nimport { Disclosure } from '../Disclosure';\nimport s from './DataGrid.module.css';\n\n/**\n * One column of a {@link DataGrid}. `id` doubles as the default property name\n * read off each row when no `accessor` is given.\n */\nexport interface DataGridColumn<Row> {\n id: string;\n header: ReactNode;\n /** Pull a sortable value from the row. Defaults to `(row as any)[column.id]`. */\n accessor?: (row: Row) => string | number | null | undefined;\n /** Render the cell. Defaults to `String(accessor(row) ?? '')`. */\n render?: (row: Row) => ReactNode;\n /** Default true. Set false to suppress the sort affordance on this column. */\n sortable?: boolean;\n /** Optional CSS class on every <td> in this column. */\n className?: string;\n /** A column of figures: equal-width digits, aligned to the end so the column\n * reads down. Header included, so its label sits over the numbers. */\n numeric?: boolean;\n}\n\n/** Props for {@link DataGrid}. */\nexport interface DataGridProps<Row extends { id: string }> {\n rows: readonly Row[];\n columns: readonly DataGridColumn<Row>[];\n /** Initial sort. Omit for unsorted (rows render in input order). */\n defaultSort?: { columnId: string; direction: 'asc' | 'desc' };\n /** Enable drag handles in a leading column. Receives the reorder spec\n * (the existing `useReorderDragList` semantics — see weasel-ui). */\n onReorder?: (ids: string[], targetIndex: number) => void;\n /** Empty-state row text. Default `'—'`. */\n empty?: ReactNode;\n className?: string;\n /** Extra class on a row's `<tr>`, for row-level state such as a verdict. */\n rowClassName?: (row: Row) => string | undefined;\n /**\n * Activate a row. Makes each row focusable: a click anywhere in it, or\n * Enter/Space while it has focus, calls this. Clicks and keys landing on a\n * control inside a cell (button, link, input, …) are left to that control.\n */\n onRowClick?: (row: Row) => void;\n /**\n * Content for a full-width detail row under `row`, shown while the row is\n * expanded. Setting it adds a leading disclosure column. Return `null` for\n * no detail row.\n */\n renderDetail?: (row: Row) => ReactNode;\n /** Which rows offer the disclosure. Default: every row. */\n rowExpandable?: (row: Row) => boolean;\n /** Expanded row ids, controlled. Pair with `onExpandedChange`. */\n expandedIds?: ReadonlySet<string>;\n /** Initially expanded row ids when uncontrolled. */\n defaultExpandedIds?: Iterable<string>;\n /** Called with the next expanded set when a disclosure toggles. */\n onExpandedChange?: (ids: ReadonlySet<string>) => void;\n}\n\ntype SortState = { columnId: string; direction: 'asc' | 'desc' } | null;\n\nconst INTERACTIVE = 'a[href], button, input, select, textarea, label, summary, [role=\"button\"], [contenteditable=\"true\"]';\n\n/** Did this event start on a control inside the row rather than on the row itself? */\nfunction fromControl(e: MouseEvent | KeyboardEvent): boolean {\n const target = e.target as Element | null;\n const hit = target?.closest?.(INTERACTIVE);\n return !!hit && e.currentTarget.contains(hit);\n}\n\n/**\n * Sortable table for inspector-style data. Rows are keyed by `id`. Clicking a\n * sortable header cycles ascending, descending, unsorted; nullish values sort\n * last regardless of direction. Passing `onReorder` adds a leading drag-handle\n * column — note that the drag indices it reports are into the *sorted* row\n * order, not the input order.\n */\nexport function DataGrid<Row extends { id: string }>(props: DataGridProps<Row>) {\n const {\n rows, columns, defaultSort, onReorder, empty = '—', className,\n rowClassName, onRowClick, renderDetail, rowExpandable, expandedIds, defaultExpandedIds, onExpandedChange,\n } = props;\n const [sort, setSort] = useState<SortState>(defaultSort ?? null);\n const [ownExpanded, setOwnExpanded] = useState<ReadonlySet<string>>(() => new Set(defaultExpandedIds));\n const expanded = expandedIds ?? ownExpanded;\n const detailIdBase = useId();\n\n const toggleExpanded = (id: string) => {\n const next = new Set(expanded);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n if (!expandedIds) setOwnExpanded(next);\n onExpandedChange?.(next);\n };\n\n const sortedRows = useMemo(() => {\n if (!sort) return [...rows];\n const col = columns.find((c) => c.id === sort.columnId);\n if (!col) return [...rows];\n const get = col.accessor ?? ((row: Row) => (row as unknown as Record<string, unknown>)[col.id] as string | number | null | undefined);\n const dir = sort.direction === 'asc' ? 1 : -1;\n return [...rows].sort((a, b) => {\n const av = get(a);\n const bv = get(b);\n if (av == null && bv == null) return 0;\n if (av == null) return 1;\n if (bv == null) return -1;\n if (av < bv) return -1 * dir;\n if (av > bv) return 1 * dir;\n return 0;\n });\n }, [rows, columns, sort]);\n\n const cycleSort = (columnId: string) => {\n setSort((cur) => {\n if (!cur || cur.columnId !== columnId) return { columnId, direction: 'asc' };\n if (cur.direction === 'asc') return { columnId, direction: 'desc' };\n return null;\n });\n };\n\n // Drag handles via useReorderDragList. Adapter: the hook expects\n // `{ id, label }`-shaped items; we map our rows to that shape (label\n // unused — the hook only uses id and locked).\n const drag = useReorderDragList({\n items: sortedRows.map((r) => ({ id: r.id, label: '' })),\n selectedIds: [],\n onReorder: onReorder ?? (() => {}),\n rowSelector: ':not([data-detail-of])',\n });\n const dragEnabled = !!onReorder;\n const detailEnabled = !!renderDetail;\n const colCount = columns.length + (dragEnabled ? 1 : 0) + (detailEnabled ? 1 : 0);\n\n const cls = [s.grid, className].filter(Boolean).join(' ');\n\n return (\n <div\n className={cls}\n >\n <table className={s.table}>\n <thead>\n <tr>\n {dragEnabled && <th className={s.handleCol} aria-label=\"drag\" />}\n {detailEnabled && <th className={s.detailCol} aria-label=\"details\" />}\n {columns.map((col) => {\n const sortable = col.sortable !== false;\n const active = sort?.columnId === col.id;\n const indicator = active ? (sort.direction === 'asc' ? ' ↑' : ' ↓') : '';\n return (\n <th\n key={col.id}\n className={[col.className, col.numeric && s.numeric, active && s.sortActive].filter(Boolean).join(' ') || undefined}\n aria-sort={sortable ? (active ? (sort.direction === 'asc' ? 'ascending' : 'descending') : 'none') : undefined}\n >\n {sortable ? (\n <button type=\"button\" className={s.sortButton} onClick={() => cycleSort(col.id)}>\n {col.header}{indicator}\n </button>\n ) : (\n <>{col.header}{indicator}</>\n )}\n </th>\n );\n })}\n </tr>\n </thead>\n <tbody ref={dragEnabled ? (drag.containerProps.ref as React.RefCallback<HTMLTableSectionElement>) : undefined}>\n {sortedRows.length === 0 && (\n <tr>\n <td colSpan={colCount} className={s.empty}>\n {empty}\n </td>\n </tr>\n )}\n {sortedRows.map((row, i) => {\n const isDragging = drag.state.draggedIds?.includes(row.id) ?? false;\n const target = drag.state.targetIndex;\n const expandable = detailEnabled && (rowExpandable?.(row) ?? true);\n const isExpanded = expandable && expanded.has(row.id);\n const detail = isExpanded ? renderDetail!(row) : null;\n const detailId = `${detailIdBase}-detail-${row.id}`;\n const rowCls = [\n onRowClick && s.clickable,\n isDragging && s.dragging,\n target === i && s.dropBefore,\n target === sortedRows.length && i === sortedRows.length - 1 && s.dropAfter,\n rowClassName?.(row),\n ].filter(Boolean).join(' ');\n return (\n <Fragment key={row.id}>\n <tr\n className={rowCls || undefined}\n data-expanded={isExpanded || undefined}\n tabIndex={onRowClick ? 0 : undefined}\n onClick={onRowClick ? (e) => { if (!fromControl(e)) onRowClick(row); } : undefined}\n onKeyDown={onRowClick ? (e) => {\n if (e.target !== e.currentTarget || (e.key !== 'Enter' && e.key !== ' ')) return;\n e.preventDefault();\n onRowClick(row);\n } : undefined}\n >\n {dragEnabled && (\n <td\n className={s.handleCell}\n onPointerDown={(e) => drag.rowProps(row.id, i).onPointerDown(e)}\n >\n <span aria-hidden=\"true\">⋮⋮</span>\n </td>\n )}\n {detailEnabled && (\n <td className={s.detailCell}>\n {expandable && (\n <Disclosure\n open={isExpanded}\n onToggle={() => toggleExpanded(row.id)}\n label={isExpanded ? 'Hide details' : 'Show details'}\n controls={detail != null ? detailId : undefined}\n />\n )}\n </td>\n )}\n {columns.map((col) => {\n const get = col.accessor ?? ((r: Row) => (r as unknown as Record<string, unknown>)[col.id] as string | number | null | undefined);\n const content = col.render ? col.render(row) : String(get(row) ?? '');\n return (\n <td key={col.id} className={[col.className, col.numeric && s.numeric].filter(Boolean).join(' ') || undefined}>\n {content}\n </td>\n );\n })}\n </tr>\n {detail != null && (\n <tr id={detailId} className={s.detailRow} data-detail-of={row.id}>\n <td colSpan={colCount}>{detail}</td>\n </tr>\n )}\n </Fragment>\n );\n })}\n </tbody>\n </table>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { i as e } from "./portalHost-CE70wLzo.js";
1
+ import { i as e } from "./portalHost-RUQPxIoW.js";
2
2
  import { t } from "./stance-BFQpS_fL.js";
3
- import { t as n } from "./CloseButton-wb7Or5Eg.js";
3
+ import { t as n } from "./CloseButton-CKarkTT9.js";
4
4
  import { Fragment as r, jsx as i, jsxs as a } from "react/jsx-runtime";
5
5
  import { Dialog as o, Heading as s, Modal as c, ModalOverlay as l } from "react-aria-components";
6
6
  var u = {
@@ -18,8 +18,8 @@ function d(d) {
18
18
  let { title: f, showCloseButton: p, children: m, footer: h, role: g = "dialog", className: _, bodyClassName: v, isOpen: y, onOpenChange: b, portalContainer: x, stance: S, tone: C, ...w } = d, T = t({
19
19
  stance: S,
20
20
  tone: C
21
- }), E = p ?? !!b, { anchor: D, portalProps: O } = e(x);
22
- return /* @__PURE__ */ a(r, { children: [D, /* @__PURE__ */ i(l, {
21
+ }), E = p ?? !!b, { anchor: D, portalProps: O, ready: k } = e(x);
22
+ return /* @__PURE__ */ a(r, { children: [D, k && /* @__PURE__ */ i(l, {
23
23
  ...w,
24
24
  isOpen: y,
25
25
  onOpenChange: b,
@@ -60,4 +60,4 @@ function d(d) {
60
60
  //#endregion
61
61
  export { d as t };
62
62
 
63
- //# sourceMappingURL=Dialog-h-5MeYpu.js.map
63
+ //# sourceMappingURL=Dialog-Du4POZ52.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Dialog-Du4POZ52.js","names":["Dialog","_Fragment","s","RACModal","RACDialog","useOverlayPortal","CloseButton","Dialog_module_default","Dialog$1","rest","props","stanced","useStance","showClose","showCloseButton","onOpenChange","portalContainer","Fragment","anchor","ready","ModalOverlay","portalProps","Modal","className","Dialog","title","Heading","close","bodyClassName","footer"],"sources":["../../src/components/Dialog/Dialog.module.css","../../src/components/Dialog/Dialog.tsx"],"sourcesContent":[".overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.4);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n opacity: 0;\n transition: opacity var(--wzl-motion-fast);\n}\n\n.overlay[data-entering] {\n opacity: 0;\n}\n\n.overlay[data-entering][data-rac] {\n opacity: 1;\n}\n\n.overlay[data-exiting] {\n opacity: 0;\n}\n\n.overlay[data-rac]:not([data-entering]):not([data-exiting]) {\n opacity: 1;\n}\n\n.modal {\n background: var(--_s-fill);\n border: var(--_s-border-width) var(--_s-border-style) var(--_s-border-color);\n border-radius: var(--_s-radius);\n box-shadow: 0 24px 64px rgba(0, 0, 0, 0.45);\n color: var(--wzl-fg);\n font-family: var(--wzl-font-ui);\n max-width: min(900px, calc(100vw - 32px));\n max-height: calc(100vh - 32px);\n display: flex;\n flex-direction: column;\n outline: none;\n}\n\n/* BEGIN GENERATED stance rules: dialog — `npm run gen:stances`, do not edit. */\n\n.modal {\n --wzl-tone: initial;\n}\n\n.modal {\n --_s-surface: var(--wzl-surface);\n --_s-border-color: var(--wzl-border);\n --_s-border-style: solid;\n --_s-border-width: 1px;\n --_s-radius: var(--wzl-radius-md);\n --_s-tone-mix: var(--wzl-panel-tone-mix);\n --_s-tone: var(--wzl-tone, var(--_s-surface));\n --_s-accent: var(--wzl-tone);\n --_s-title-font: var(--wzl-font-ui);\n --_s-title-weight: var(--wzl-font-weight-bold);\n --_s-title-size: var(--wzl-font-size);\n --_s-title-case: none;\n --_s-title-tracking: normal;\n --_s-title-color: var(--wzl-fg);\n}\n\n.modal[data-stance='scope'] {\n --_s-surface: var(--wzl-stance-scope-surface, var(--wzl-surface));\n --_s-border-color: var(--wzl-stance-scope-border-color, var(--wzl-border));\n --_s-border-style: var(--wzl-stance-scope-border-style, solid);\n --_s-border-width: var(--wzl-stance-scope-border-width, 1px);\n --_s-radius: var(--wzl-stance-scope-radius, var(--wzl-radius-md));\n --_s-tone-mix: var(--wzl-stance-scope-tone-mix, var(--wzl-panel-tone-mix));\n --_s-tone: var(--wzl-tone, var(--wzl-stance-scope-tone, var(--_s-surface)));\n --_s-accent: var(--wzl-stance-scope-accent, var(--wzl-tone));\n --_s-title-font: var(--wzl-stance-scope-title-font, var(--wzl-font-ui));\n --_s-title-weight: var(--wzl-stance-scope-title-weight, var(--wzl-font-weight-bold));\n --_s-title-size: var(--wzl-stance-scope-title-size, var(--wzl-font-size));\n --_s-title-case: var(--wzl-stance-scope-title-case, none);\n --_s-title-tracking: var(--wzl-stance-scope-title-tracking, normal);\n --_s-title-color: var(--wzl-stance-scope-title-color, var(--wzl-fg));\n}\n\n.modal[data-stance='aside'] {\n --_s-surface: var(--wzl-stance-aside-surface, var(--wzl-surface));\n --_s-border-color: var(--wzl-stance-aside-border-color, var(--wzl-border));\n --_s-border-style: var(--wzl-stance-aside-border-style, solid);\n --_s-border-width: var(--wzl-stance-aside-border-width, 1px);\n --_s-radius: var(--wzl-stance-aside-radius, var(--wzl-radius-md));\n --_s-tone-mix: var(--wzl-stance-aside-tone-mix, var(--wzl-panel-tone-mix));\n --_s-tone: var(--wzl-tone, var(--wzl-stance-aside-tone, var(--_s-surface)));\n --_s-accent: var(--wzl-stance-aside-accent, var(--wzl-tone));\n --_s-title-font: var(--wzl-stance-aside-title-font, var(--wzl-font-ui));\n --_s-title-weight: var(--wzl-stance-aside-title-weight, var(--wzl-font-weight-bold));\n --_s-title-size: var(--wzl-stance-aside-title-size, var(--wzl-font-size));\n --_s-title-case: var(--wzl-stance-aside-title-case, none);\n --_s-title-tracking: var(--wzl-stance-aside-title-tracking, normal);\n --_s-title-color: var(--wzl-stance-aside-title-color, var(--wzl-fg));\n}\n\n.modal[data-stance='advanced'] {\n --_s-surface: var(--wzl-stance-advanced-surface, var(--wzl-surface));\n --_s-border-color: var(--wzl-stance-advanced-border-color, var(--wzl-border));\n --_s-border-style: var(--wzl-stance-advanced-border-style, solid);\n --_s-border-width: var(--wzl-stance-advanced-border-width, 1px);\n --_s-radius: var(--wzl-stance-advanced-radius, var(--wzl-radius-md));\n --_s-tone-mix: var(--wzl-stance-advanced-tone-mix, var(--wzl-panel-tone-mix));\n --_s-tone: var(--wzl-tone, var(--wzl-stance-advanced-tone, var(--_s-surface)));\n --_s-accent: var(--wzl-stance-advanced-accent, var(--wzl-tone));\n --_s-title-font: var(--wzl-stance-advanced-title-font, var(--wzl-font-ui));\n --_s-title-weight: var(--wzl-stance-advanced-title-weight, var(--wzl-font-weight-bold));\n --_s-title-size: var(--wzl-stance-advanced-title-size, var(--wzl-font-size));\n --_s-title-case: var(--wzl-stance-advanced-title-case, none);\n --_s-title-tracking: var(--wzl-stance-advanced-title-tracking, normal);\n --_s-title-color: var(--wzl-stance-advanced-title-color, var(--wzl-fg));\n}\n\n.modal[data-stance='debug'] {\n --_s-surface: var(--wzl-stance-debug-surface, var(--wzl-surface));\n --_s-border-color: var(--wzl-stance-debug-border-color, var(--wzl-border));\n --_s-border-style: var(--wzl-stance-debug-border-style, solid);\n --_s-border-width: var(--wzl-stance-debug-border-width, 1px);\n --_s-radius: var(--wzl-stance-debug-radius, var(--wzl-radius-md));\n --_s-tone-mix: var(--wzl-stance-debug-tone-mix, var(--wzl-panel-tone-mix));\n --_s-tone: var(--wzl-tone, var(--wzl-stance-debug-tone, var(--_s-surface)));\n --_s-accent: var(--wzl-stance-debug-accent, var(--wzl-tone));\n --_s-title-font: var(--wzl-stance-debug-title-font, var(--wzl-font-ui));\n --_s-title-weight: var(--wzl-stance-debug-title-weight, var(--wzl-font-weight-bold));\n --_s-title-size: var(--wzl-stance-debug-title-size, var(--wzl-font-size));\n --_s-title-case: var(--wzl-stance-debug-title-case, none);\n --_s-title-tracking: var(--wzl-stance-debug-title-tracking, normal);\n --_s-title-color: var(--wzl-stance-debug-title-color, var(--wzl-fg));\n}\n\n.modal[data-stance='danger'] {\n --_s-surface: var(--wzl-stance-danger-surface, var(--wzl-surface));\n --_s-border-color: var(--wzl-stance-danger-border-color, var(--wzl-border));\n --_s-border-style: var(--wzl-stance-danger-border-style, solid);\n --_s-border-width: var(--wzl-stance-danger-border-width, 1px);\n --_s-radius: var(--wzl-stance-danger-radius, var(--wzl-radius-md));\n --_s-tone-mix: var(--wzl-stance-danger-tone-mix, var(--wzl-panel-tone-mix));\n --_s-tone: var(--wzl-tone, var(--wzl-stance-danger-tone, var(--_s-surface)));\n --_s-accent: var(--wzl-stance-danger-accent, var(--wzl-tone));\n --_s-title-font: var(--wzl-stance-danger-title-font, var(--wzl-font-ui));\n --_s-title-weight: var(--wzl-stance-danger-title-weight, var(--wzl-font-weight-bold));\n --_s-title-size: var(--wzl-stance-danger-title-size, var(--wzl-font-size));\n --_s-title-case: var(--wzl-stance-danger-title-case, none);\n --_s-title-tracking: var(--wzl-stance-danger-title-tracking, normal);\n --_s-title-color: var(--wzl-stance-danger-title-color, var(--wzl-fg));\n}\n\n.modal[data-stance='notice'] {\n --_s-surface: var(--wzl-stance-notice-surface, var(--wzl-surface));\n --_s-border-color: var(--wzl-stance-notice-border-color, var(--wzl-border));\n --_s-border-style: var(--wzl-stance-notice-border-style, solid);\n --_s-border-width: var(--wzl-stance-notice-border-width, 1px);\n --_s-radius: var(--wzl-stance-notice-radius, var(--wzl-radius-md));\n --_s-tone-mix: var(--wzl-stance-notice-tone-mix, var(--wzl-panel-tone-mix));\n --_s-tone: var(--wzl-tone, var(--wzl-stance-notice-tone, var(--_s-surface)));\n --_s-accent: var(--wzl-stance-notice-accent, var(--wzl-tone));\n --_s-title-font: var(--wzl-stance-notice-title-font, var(--wzl-font-ui));\n --_s-title-weight: var(--wzl-stance-notice-title-weight, var(--wzl-font-weight-bold));\n --_s-title-size: var(--wzl-stance-notice-title-size, var(--wzl-font-size));\n --_s-title-case: var(--wzl-stance-notice-title-case, none);\n --_s-title-tracking: var(--wzl-stance-notice-title-tracking, normal);\n --_s-title-color: var(--wzl-stance-notice-title-color, var(--wzl-fg));\n}\n\n.modal[data-stance='important'] {\n --_s-surface: var(--wzl-stance-important-surface, var(--wzl-surface));\n --_s-border-color: var(--wzl-stance-important-border-color, var(--wzl-border));\n --_s-border-style: var(--wzl-stance-important-border-style, solid);\n --_s-border-width: var(--wzl-stance-important-border-width, 1px);\n --_s-radius: var(--wzl-stance-important-radius, var(--wzl-radius-md));\n --_s-tone-mix: var(--wzl-stance-important-tone-mix, var(--wzl-panel-tone-mix));\n --_s-tone: var(--wzl-tone, var(--wzl-stance-important-tone, var(--_s-surface)));\n --_s-accent: var(--wzl-stance-important-accent, var(--wzl-tone));\n --_s-title-font: var(--wzl-stance-important-title-font, var(--wzl-font-ui));\n --_s-title-weight: var(--wzl-stance-important-title-weight, var(--wzl-font-weight-bold));\n --_s-title-size: var(--wzl-stance-important-title-size, var(--wzl-font-size));\n --_s-title-case: var(--wzl-stance-important-title-case, none);\n --_s-title-tracking: var(--wzl-stance-important-title-tracking, normal);\n --_s-title-color: var(--wzl-stance-important-title-color, var(--wzl-fg));\n}\n\n.modal[data-stance='preview'] {\n --_s-surface: var(--wzl-stance-preview-surface, var(--wzl-surface));\n --_s-border-color: var(--wzl-stance-preview-border-color, var(--wzl-border));\n --_s-border-style: var(--wzl-stance-preview-border-style, solid);\n --_s-border-width: var(--wzl-stance-preview-border-width, 1px);\n --_s-radius: var(--wzl-stance-preview-radius, var(--wzl-radius-md));\n --_s-tone-mix: var(--wzl-stance-preview-tone-mix, var(--wzl-panel-tone-mix));\n --_s-tone: var(--wzl-tone, var(--wzl-stance-preview-tone, var(--_s-surface)));\n --_s-accent: var(--wzl-stance-preview-accent, var(--wzl-tone));\n --_s-title-font: var(--wzl-stance-preview-title-font, var(--wzl-font-ui));\n --_s-title-weight: var(--wzl-stance-preview-title-weight, var(--wzl-font-weight-bold));\n --_s-title-size: var(--wzl-stance-preview-title-size, var(--wzl-font-size));\n --_s-title-case: var(--wzl-stance-preview-title-case, none);\n --_s-title-tracking: var(--wzl-stance-preview-title-tracking, normal);\n --_s-title-color: var(--wzl-stance-preview-title-color, var(--wzl-fg));\n}\n\n.modal {\n --_s-fill: var(--_s-surface);\n}\n\n.modal[data-tone],\n.modal[data-stance] {\n --_s-fill: color-mix(in oklab, var(--_s-tone) var(--_s-tone-mix), var(--_s-surface));\n}\n\n.modal[data-tone] {\n --wzl-surface-sunken: oklch(from var(--_s-fill) calc(l - 0.06) c h);\n}\n\n.modal[data-tone] {\n --wzl-accent: var(--_s-accent);\n --wzl-accent-base: var(--_s-accent);\n --wzl-accent-strong: color-mix(in oklab, var(--_s-accent) 70%, var(--wzl-fg));\n --wzl-accent-soft: color-mix(in oklab, var(--_s-accent) 45%, var(--wzl-surface));\n --wzl-accent-hover: var(--wzl-accent-strong);\n --wzl-accent-fg: var(--wzl-accent-strong);\n --wzl-focus-ring: var(--wzl-accent-strong);\n}\n\n/* END GENERATED stance rules: dialog */\n\n.dialog {\n display: flex;\n flex-direction: column;\n min-height: 0;\n flex: 1;\n outline: none;\n}\n\n.header {\n display: flex;\n align-items: center;\n gap: var(--wzl-space-6);\n padding: var(--wzl-space-7) var(--wzl-space-8);\n border-bottom: 1px solid var(--wzl-border);\n flex: 0 0 auto;\n}\n\n.title {\n flex: 1;\n margin: 0;\n font-family: var(--_s-title-font);\n font-size: var(--_s-title-size);\n font-weight: var(--_s-title-weight);\n text-transform: var(--_s-title-case);\n letter-spacing: var(--_s-title-tracking);\n color: var(--_s-title-color);\n}\n\n.body {\n flex: 1;\n}\n\n/* Defaults, not rules: `bodyClassName` exists to turn these off for content\n that scrolls itself, and a plain class would tie with it on specificity and\n be decided by stylesheet order. `min-height` is here for the same reason —\n left in the rule above it silently beat a consumer class asking the body to\n hold a height, and the dialog resized itself around every pane. */\n:where(.body) {\n min-height: 0;\n overflow: auto;\n padding: var(--wzl-space-8);\n}\n\n.footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--wzl-space-4);\n padding: var(--wzl-space-6) var(--wzl-space-8);\n border-top: 1px solid var(--wzl-border);\n flex: 0 0 auto;\n}\n","import type { ReactNode } from 'react';\nimport {\n Modal as RACModal,\n ModalOverlay,\n Dialog as RACDialog,\n Heading,\n type ModalOverlayProps,\n type DialogProps as RACDialogProps,\n} from 'react-aria-components';\nimport { useOverlayPortal, type OverlayPortalProps, type WithoutPortalTarget } from '../../overlays/portalHost';\nimport { CloseButton } from '../CloseButton';\nimport { type StanceProps, useStance } from '../stance';\nimport s from './Dialog.module.css';\n\n/** Props for {@link Dialog}, on top of React Aria's `ModalOverlay` props. */\nexport type DialogProps = Omit<\n WithoutPortalTarget<ModalOverlayProps>,\n 'children' | 'className'\n> &\n OverlayPortalProps &\n StanceProps & {\n /** Heading rendered in the dialog's default header. Omit when supplying\n * a custom `header` slot via children. */\n title?: ReactNode;\n /** Show the built-in close button. Defaults to true when `onOpenChange`\n * is wired so the user has an escape hatch. */\n showCloseButton?: boolean;\n /** Body content. */\n children?: ReactNode;\n /** Footer slot — typically action buttons. */\n footer?: ReactNode;\n /** ARIA role. Defaults to `dialog`. Use `alertdialog` for confirms. */\n role?: RACDialogProps['role'];\n /** Class applied to the modal box (inside the overlay). */\n className?: string;\n /** Class applied to the scrolling body. For content that owns its own\n * scrolling — a form with a fixed navigation rail beside a scrolling\n * pane — this is where the body's padding and overflow come off. */\n bodyClassName?: string;\n };\n\n/**\n * Modal dialog wrapping React Aria. Supplies a default header (title +\n * close), scrollable body, and footer slot. Pass `isOpen` + `onOpenChange`\n * for controlled visibility, or omit both and use a `<DialogTrigger>` from\n * `react-aria-components` upstream of this component.\n *\n * Escape, click-outside, focus trap, and scroll lock all come from the\n * underlying primitives. `stance` and `tone` work as on `<PropertyPanel>` —\n * a destructive confirm is `stance=\"danger\"`.\n */\nexport function Dialog(props: DialogProps) {\n const {\n title,\n showCloseButton,\n children,\n footer,\n role = 'dialog',\n className,\n bodyClassName,\n isOpen,\n onOpenChange,\n portalContainer,\n stance,\n tone,\n ...rest\n } = props;\n const stanced = useStance({ stance, tone });\n\n const showClose = showCloseButton ?? Boolean(onOpenChange);\n const { anchor, portalProps, ready } = useOverlayPortal(portalContainer);\n\n return (\n <>\n {anchor}\n {ready && (\n <ModalOverlay\n {...rest}\n isOpen={isOpen}\n onOpenChange={onOpenChange}\n className={s.overlay}\n data-weasel-overlay=\"\"\n {...portalProps}\n >\n <RACModal className={[s.modal, className].filter(Boolean).join(' ')} {...stanced}>\n <RACDialog role={role} className={s.dialog}>\n {({ close }) => (\n <>\n {(title !== undefined || showClose) && (\n <header className={s.header}>\n {title !== undefined && (\n <Heading slot=\"title\" className={s.title}>{title}</Heading>\n )}\n {showClose && (\n <CloseButton ariaLabel=\"Close dialog\" onClick={close} />\n )}\n </header>\n )}\n <div className={[s.body, bodyClassName].filter(Boolean).join(' ')}>{children}</div>\n {footer !== undefined && <footer className={s.footer}>{footer}</footer>}\n </>\n )}\n </RACDialog>\n </RACModal>\n </ModalOverlay>\n )}\n </>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;ACmDA,SAAgBA,EAAO,GAAoB;CACzC,IAAM,EACJ,UACA,oBACA,aACA,WACA,UAAO,UACP,cACA,kBACA,WACA,iBACA,oBACA,WACA,SACA,GAAGS,MACDC,GACEC,IAAUC,EAAU;EAAE;EAAQ;CAAK,CAAC,GAEpCC,IAAYC,KAAmB,EAAQC,GACvC,EAAE,WAAQ,gBAAa,aAAUV,EAAiBW,CAAe;CAEvE,OACE,kBAAAf,GAAA,EAAA,UAAA,CACGiB,GACAC,KACC,kBAACC,GAAD;EACE,GAAIX;EACI;EACM;EACd,WAAWP,EAAE;EACb,uBAAoB;EACpB,GAAImB;EAEJ,UAAA,kBAAClB,GAAD;GAAU,WAAW,CAACD,EAAE,OAAOqB,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;GAAG,GAAIZ;GACvE,UAAA,kBAACP,GAAD;IAAiB;IAAM,WAAWF,EAAE;IAChC,WAAA,EAAE,eACF,kBAAAD,GAAA,EAAA,UAAA;MACIwB,MAAU,KAAA,KAAaZ,MACvB,kBAAC,UAAD;MAAQ,WAAWX,EAAE;MAArB,UAAA,CACGuB,MAAU,KAAA,KACT,kBAACC,GAAD;OAAS,MAAK;OAAQ,WAAWxB,EAAE;OAAQ,UAAAuB;MAAe,CAAA,GAE3DZ,KACC,kBAACP,GAAD;OAAa,WAAU;OAAe,SAASqB;MAAQ,CAAA,CAEnD;;KAEV,kBAAC,OAAD;MAAK,WAAW,CAACzB,EAAE,MAAM0B,CAAa,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;MAAI;KAAc,CAAA;KACjFC,MAAW,KAAA,KAAa,kBAAC,UAAD;MAAQ,WAAW3B,EAAE;MAAS,UAAA2B;KAAe,CAAA;IACtE,EAAA,CAAA;GAEK,CAAA;EACH,CAAA;CACE,CAAA,CAEhB,EAAA,CAAA;AAEN"}
@@ -1,4 +1,4 @@
1
- import { n as e } from "./portalHost-CE70wLzo.js";
1
+ import { n as e } from "./portalHost-RUQPxIoW.js";
2
2
  import { jsx as t } from "react/jsx-runtime";
3
3
  import { createPortal as n } from "react-dom";
4
4
  var r = { ghost: "_ghost_gsqub_1" };
@@ -27,4 +27,4 @@ function a({ at: a, from: o, children: s }) {
27
27
  //#endregion
28
28
  export { a as t };
29
29
 
30
- //# sourceMappingURL=DragGhost-BeYDJyLZ.js.map
30
+ //# sourceMappingURL=DragGhost-_IpuN_xM.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DragGhost-BeYDJyLZ.js","names":["s","nearestPortalHost","DragGhost_module_default","containsFixed","cs","el","DragGhost","host","from","origin","style","at","createPortal"],"sources":["../../src/components/DragGhost/DragGhost.module.css","../../src/components/DragGhost/DragGhost.tsx"],"sourcesContent":[".ghost {\n position: fixed;\n z-index: var(--wzl-z-overlay);\n display: flex;\n flex-direction: column;\n gap: 1px;\n padding: 1px;\n border-radius: var(--wzl-radius-sm);\n background: var(--wzl-surface-raised);\n box-shadow: 0 4px 12px var(--wzl-shadow);\n opacity: 0.85;\n pointer-events: none;\n}\n","import type { CSSProperties, ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\nimport { nearestPortalHost } from '../../overlays/portalHost';\nimport s from './DragGhost.module.css';\n\n/** Props for `<DragGhost>`. */\nexport interface DragGhostProps {\n /** The ghost's top-left in client space, and its width — `useReorderDragList`'s `state.ghost` is one. */\n at: { left: number; top: number; width: number };\n /** An element inside the list being dragged from. The ghost portals to its nearest themed ancestor, so it resolves\n * the list's tokens without being clipped by the list's panel. */\n from: Element;\n /** What to draw: copies of the dragged items, or any stand-in for them. */\n children: ReactNode;\n}\n\n/** Whether `el` is the containing block of its fixed-position descendants rather than the viewport. */\nfunction containsFixed(el: Element): boolean {\n const cs = getComputedStyle(el);\n return (\n cs.transform !== 'none' ||\n cs.filter !== 'none' ||\n cs.backdropFilter !== 'none' ||\n cs.perspective !== 'none' ||\n /paint|layout|strict|content/.test(cs.contain)\n );\n}\n\n/**\n * What follows the pointer during a drag while the dragged items hold their place: `children` on a raised surface at\n * a client-space point. Hidden from assistive tech and from the pointer, so it never becomes the drop target.\n */\nexport function DragGhost({ at, from, children }: DragGhostProps) {\n const host = nearestPortalHost(from) ?? document.body;\n const origin = host !== document.body && containsFixed(host) ? host.getBoundingClientRect() : { left: 0, top: 0 };\n // Pixel positioning: the point is the pointer's, known only at runtime.\n const style: CSSProperties = { left: at.left - origin.left, top: at.top - origin.top, width: at.width };\n return createPortal(\n <div className={s.ghost} style={style} aria-hidden=\"true\">\n {children}\n </div>,\n host,\n );\n}\n"],"mappings":";;;;;;ACiBA,SAASG,EAAc,GAAsB;CAC3C,IAAMC,IAAK,iBAAiBC,CAAE;CAC9B,OACED,EAAG,cAAc,UACjBA,EAAG,WAAW,UACdA,EAAG,mBAAmB,UACtBA,EAAG,gBAAgB,UACnB,8BAA8B,KAAKA,EAAG,OAAO;AAEjD;AAMA,SAAgBE,EAAU,EAAE,OAAI,SAAM,eAA4B;CAChE,IAAMC,IAAON,EAAkBO,CAAI,KAAK,SAAS,MAC3CC,IAASF,MAAS,SAAS,QAAQJ,EAAcI,CAAI,IAAIA,EAAK,sBAAsB,IAAI;EAAE,MAAM;EAAG,KAAK;CAAE,GAE1GG,IAAuB;EAAE,MAAMC,EAAG,OAAOF,EAAO;EAAM,KAAKE,EAAG,MAAMF,EAAO;EAAK,OAAOE,EAAG;CAAM;CACtG,OAAOC,EACL,kBAAC,OAAD;EAAK,WAAWZ,EAAE;EAAc;EAAO,eAAY;EAChD;CACE,CAAA,GACLO,CACF;AACF"}
1
+ {"version":3,"file":"DragGhost-_IpuN_xM.js","names":["s","nearestPortalHost","DragGhost_module_default","containsFixed","cs","el","DragGhost","host","from","origin","style","at","createPortal"],"sources":["../../src/components/DragGhost/DragGhost.module.css","../../src/components/DragGhost/DragGhost.tsx"],"sourcesContent":[".ghost {\n position: fixed;\n z-index: var(--wzl-z-overlay);\n display: flex;\n flex-direction: column;\n gap: 1px;\n padding: 1px;\n border-radius: var(--wzl-radius-sm);\n background: var(--wzl-surface-raised);\n box-shadow: 0 4px 12px var(--wzl-shadow);\n opacity: 0.85;\n pointer-events: none;\n}\n","import type { CSSProperties, ReactNode } from 'react';\nimport { createPortal } from 'react-dom';\nimport { nearestPortalHost } from '../../overlays/portalHost';\nimport s from './DragGhost.module.css';\n\n/** Props for `<DragGhost>`. */\nexport interface DragGhostProps {\n /** The ghost's top-left in client space, and its width — `useReorderDragList`'s `state.ghost` is one. */\n at: { left: number; top: number; width: number };\n /** An element inside the list being dragged from. The ghost portals to its nearest themed ancestor, so it resolves\n * the list's tokens without being clipped by the list's panel. */\n from: Element;\n /** What to draw: copies of the dragged items, or any stand-in for them. */\n children: ReactNode;\n}\n\n/** Whether `el` is the containing block of its fixed-position descendants rather than the viewport. */\nfunction containsFixed(el: Element): boolean {\n const cs = getComputedStyle(el);\n return (\n cs.transform !== 'none' ||\n cs.filter !== 'none' ||\n cs.backdropFilter !== 'none' ||\n cs.perspective !== 'none' ||\n /paint|layout|strict|content/.test(cs.contain)\n );\n}\n\n/**\n * What follows the pointer during a drag while the dragged items hold their place: `children` on a raised surface at\n * a client-space point. Hidden from assistive tech and from the pointer, so it never becomes the drop target.\n */\nexport function DragGhost({ at, from, children }: DragGhostProps) {\n const host = nearestPortalHost(from) ?? document.body;\n const origin = host !== document.body && containsFixed(host) ? host.getBoundingClientRect() : { left: 0, top: 0 };\n // Pixel positioning: the point is the pointer's, known only at runtime.\n const style: CSSProperties = { left: at.left - origin.left, top: at.top - origin.top, width: at.width };\n return createPortal(\n <div className={s.ghost} style={style} aria-hidden=\"true\">\n {children}\n </div>,\n host,\n );\n}\n"],"mappings":";;;;;;ACiBA,SAASG,EAAc,GAAsB;CAC3C,IAAMC,IAAK,iBAAiBC,CAAE;CAC9B,OACED,EAAG,cAAc,UACjBA,EAAG,WAAW,UACdA,EAAG,mBAAmB,UACtBA,EAAG,gBAAgB,UACnB,8BAA8B,KAAKA,EAAG,OAAO;AAEjD;AAMA,SAAgBE,EAAU,EAAE,OAAI,SAAM,eAA4B;CAChE,IAAMC,IAAON,EAAkBO,CAAI,KAAK,SAAS,MAC3CC,IAASF,MAAS,SAAS,QAAQJ,EAAcI,CAAI,IAAIA,EAAK,sBAAsB,IAAI;EAAE,MAAM;EAAG,KAAK;CAAE,GAE1GG,IAAuB;EAAE,MAAMC,EAAG,OAAOF,EAAO;EAAM,KAAKE,EAAG,MAAMF,EAAO;EAAK,OAAOE,EAAG;CAAM;CACtG,OAAOC,EACL,kBAAC,OAAD;EAAK,WAAWZ,EAAE;EAAc;EAAO,eAAY;EAChD;CACE,CAAA,GACLO,CACF;AACF"}
@@ -1,7 +1,7 @@
1
- import { t as e } from "./Badge-jYQjrTKc.js";
2
- import { t } from "./Code-AsKpgnv6.js";
3
- import { n, o as r, r as i, s as a } from "./Keycaps-C-FJyG8k.js";
4
- import { t as o } from "./GestureRoute-BXESrHI0.js";
1
+ import { t as e } from "./Badge-4ShpqA0h.js";
2
+ import { n as t } from "./Code-ChwKP1V0.js";
3
+ import { c as n, i as r, r as i, s as a } from "./Keycaps-CM-gOj0e.js";
4
+ import { t as o } from "./GestureRoute-BqHUrGeQ.js";
5
5
  import { Fragment as s, jsx as c, jsxs as l } from "react/jsx-runtime";
6
6
  import { useId as u } from "react";
7
7
  var d = {
@@ -57,16 +57,16 @@ function g({ input: t, predicted: o }) {
57
57
  className: d.field,
58
58
  children: [/* @__PURE__ */ c("dt", { children: "Key" }), /* @__PURE__ */ c("dd", {
59
59
  "data-key": t.key,
60
- children: /* @__PURE__ */ c(i, {
61
- label: r(t.key, { legend: "text" }).label,
60
+ children: /* @__PURE__ */ c(r, {
61
+ label: a(t.key, { legend: "text" }).label,
62
62
  variant: "minimal"
63
63
  })
64
64
  })]
65
65
  }),
66
66
  /* @__PURE__ */ l("div", {
67
67
  className: d.field,
68
- children: [/* @__PURE__ */ c("dt", { children: "Modifiers" }), /* @__PURE__ */ c("dd", { children: s.length > 0 ? /* @__PURE__ */ c(n, {
69
- keys: a(s),
68
+ children: [/* @__PURE__ */ c("dt", { children: "Modifiers" }), /* @__PURE__ */ c("dd", { children: s.length > 0 ? /* @__PURE__ */ c(i, {
69
+ keys: n(s),
70
70
  variant: "minimal"
71
71
  }) : "none" })]
72
72
  }),
@@ -281,4 +281,4 @@ function C({ record: e, className: t }) {
281
281
  //#endregion
282
282
  export { C as t };
283
283
 
284
- //# sourceMappingURL=FallthroughDiagram-CS1IhQaU.js.map
284
+ //# sourceMappingURL=FallthroughDiagram-Cn6QLWKA.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FallthroughDiagram-CS1IhQaU.js","names":["_Fragment","s","Badge","keySpecFromKey","KeyCap","keySpecsFromMods","GestureRoute","FallthroughDiagram_module_default","placedByLabel","p","walkLabel","w","Fragment","isWinner","MOD_ORDER","InputLine","mods","input","m","KeySequence","predicted","Routes","candidate","r","Stage","id","useId","filter","title","note","children","MatchedStage","matched","Code","c","i","dropReason","d","FilterBand","mine","dropped","RankedStage","ranked","winner","FallthroughDiagram","cls","className","record"],"sources":["../../src/components/FallthroughDiagram/FallthroughDiagram.module.css","../../src/components/FallthroughDiagram/FallthroughDiagram.tsx"],"sourcesContent":[".diagram {\n display: grid;\n gap: var(--wzl-space-3);\n color: var(--wzl-fg);\n font-family: var(--wzl-font-ui);\n font-size: var(--wzl-font-size-sm);\n}\n\n.input {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--wzl-space-2) var(--wzl-space-4);\n margin: 0;\n color: var(--wzl-fg-muted);\n}\n.field { display: flex; align-items: baseline; gap: var(--wzl-space-1); }\n.field dt { margin: 0; }\n.field dd {\n margin: 0;\n color: var(--wzl-fg);\n font-weight: var(--wzl-font-weight-medium);\n}\n\n.stage {\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-md);\n background: var(--wzl-surface);\n padding: var(--wzl-space-2) var(--wzl-space-3);\n /* Ids and rules do not wrap mid-word; a narrow host scrolls the stage instead. */\n overflow-x: auto;\n}\n.stage[data-filter] { background: var(--wzl-surface-sunken); }\n\n.stageTitle {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--wzl-space-2);\n margin: 0;\n font-size: var(--wzl-font-size-sm);\n font-weight: var(--wzl-font-weight-bold);\n}\n.stageTitle + .table { margin-top: var(--wzl-space-2); }\n.note { color: var(--wzl-fg-muted); font-weight: var(--wzl-font-weight-medium); }\n\n.table { width: 100%; border-collapse: collapse; }\n.table th {\n text-align: start;\n color: var(--wzl-fg-muted);\n font-size: var(--wzl-font-size-xs);\n font-weight: var(--wzl-font-weight-bold);\n padding: var(--wzl-space-1) var(--wzl-space-2);\n}\n.table td {\n padding: var(--wzl-space-1) var(--wzl-space-2);\n border-top: 1px solid var(--wzl-border);\n vertical-align: top;\n white-space: nowrap;\n}\n\n.table code { white-space: nowrap; overflow-wrap: normal; }\n\n.routes { display: inline-flex; flex-direction: column; align-items: flex-start; gap: var(--wzl-space-1); }\n\n.num {\n composes: numeric from '@weasel-js/theme/numeric.module.css';\n text-align: end;\n white-space: nowrap;\n}\n\n.muted { color: var(--wzl-fg-muted); }\n\n.walk { color: var(--wzl-fg-muted); }\n.walk[data-walk='fired'],\n.walk[data-walk='would-fire'] { color: var(--wzl-success); font-weight: var(--wzl-font-weight-bold); }\n.walk[data-walk='declined'],\n.walk[data-walk='empty-handle'],\n.walk[data-walk='misbound'],\n.walk[data-walk='no-such-action'] { color: var(--wzl-fg); }\n\n.winner td { background: color-mix(in srgb, var(--wzl-success) 12%, transparent); }\n.winner td:first-child { box-shadow: inset 3px 0 var(--wzl-success); }\n\n.nothing { margin: 0; color: var(--wzl-fg-muted); }\n","import { useId, type ReactNode } from 'react';\nimport type {\n DispatchRecord,\n DispatchRecordInput,\n DroppedCandidate,\n PlacedBy,\n RankedCandidate,\n RecordCandidate,\n WalkStep,\n} from '@weasel-js/core/routing';\nimport { Badge } from '../Badge';\nimport { Code } from '../Code';\nimport { GestureRoute } from '../GestureRoute';\nimport { KeyCap, KeySequence, keySpecFromKey, keySpecsFromMods, type LogicalModSpec } from '../Keycaps';\nimport s from './FallthroughDiagram.module.css';\n\nfunction placedByLabel(p: PlacedBy): string {\n return p.step === 'specificity' ? `specificity · ${p.part}` : p.step;\n}\n\nfunction walkLabel(w: WalkStep): ReactNode {\n switch (w.kind) {\n case 'fired': return 'fired';\n case 'would-fire': return 'would fire';\n case 'declined': return <>declined: {w.reason}</>;\n case 'empty-handle': return 'empty handle';\n case 'misbound': return 'misbound';\n case 'duplicate': return 'duplicate';\n case 'no-such-action': return 'no such action';\n case 'not-asked': return 'not asked';\n case 'outranked': return 'outranked';\n }\n}\n\nconst isWinner = (w: WalkStep) => w.kind === 'fired' || w.kind === 'would-fire';\n\nconst MOD_ORDER = ['shift', 'alt', 'ctrl', 'meta'] as const;\n\nfunction InputLine({ input, predicted }: { input: DispatchRecordInput; predicted: boolean }) {\n const mods: LogicalModSpec[] = MOD_ORDER.filter((m) => input.modifiers[m]).map((name) => ({ name }));\n return (\n <dl className={s.input} aria-label=\"Input\">\n <div className={s.field}>\n <dt>Event</dt>\n <dd>{input.eventKind}</dd>\n </div>\n {input.key !== undefined && (\n <div className={s.field}>\n <dt>Key</dt>\n <dd data-key={input.key}>\n <KeyCap label={keySpecFromKey(input.key, { legend: 'text' }).label} variant=\"minimal\" />\n </dd>\n </div>\n )}\n <div className={s.field}>\n <dt>Modifiers</dt>\n <dd>{mods.length > 0 ? <KeySequence keys={keySpecsFromMods(mods)} variant=\"minimal\" /> : 'none'}</dd>\n </div>\n {input.viewId !== null && (\n <div className={s.field}>\n <dt>View</dt>\n <dd>{input.viewId}</dd>\n </div>\n )}\n {input.mode !== undefined && (\n <div className={s.field}>\n <dt>Mode</dt>\n <dd>{input.mode}</dd>\n </div>\n )}\n {predicted && (\n <div className={s.field}>\n <dd><Badge size=\"xs\" status=\"accent\" variant=\"subtle\">predicted</Badge></dd>\n </div>\n )}\n </dl>\n );\n}\n\nfunction Routes({ candidate }: { candidate: RecordCandidate }) {\n return (\n <span className={s.routes}>\n {candidate.routes.map((r) => <GestureRoute key={r} route={r} size=\"sm\" />)}\n </span>\n );\n}\n\nfunction Stage({ title, note, filter, children }: {\n title: string;\n note?: ReactNode;\n filter?: DroppedCandidate['filter'];\n children?: ReactNode;\n}) {\n const id = useId();\n return (\n <section className={s.stage} aria-labelledby={id} data-filter={filter}>\n <h3 className={s.stageTitle}>\n <span id={id}>{title}</span>\n {note && <span className={s.note}>{note}</span>}\n </h3>\n {children}\n </section>\n );\n}\n\nfunction MatchedStage({ matched }: { matched: readonly RecordCandidate[] }) {\n return (\n <Stage title=\"Matched\" note=\"every binding that fits the input\">\n <table className={s.table}>\n <thead>\n <tr><th>Action</th><th>Route</th><th>Owner</th><th>Tier</th></tr>\n </thead>\n <tbody>\n {matched.map((c, i) => (\n <tr key={i}>\n <td><Code size=\"sm\">{c.actionId}</Code></td>\n <td><Routes candidate={c} /></td>\n <td className={c.ownerId === null ? s.muted : undefined}>{c.ownerId ?? 'action'}</td>\n <td>{c.scope}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </Stage>\n );\n}\n\nfunction dropReason(d: DroppedCandidate): ReactNode {\n if (d.filter === 'ineligible') return <><Code size=\"sm\" variant=\"plain\">{d.rule}</Code> does not hold</>;\n return d.owner !== undefined\n ? <>barred by the exclusive claim of <Code size=\"sm\" variant=\"plain\">{d.owner}</Code></>\n : 'barred by an exclusive claim';\n}\n\nfunction FilterBand({ title, filter, dropped }: {\n title: string;\n filter: DroppedCandidate['filter'];\n dropped: readonly DroppedCandidate[];\n}) {\n const mine = dropped.filter((d) => d.filter === filter);\n if (mine.length === 0) return <Stage title={title} filter={filter} note=\"dropped nothing\" />;\n return (\n <Stage title={title} filter={filter} note=\"dropped\">\n <table className={s.table}>\n <thead>\n <tr><th>Action</th><th>Route</th><th>Why</th></tr>\n </thead>\n <tbody>\n {mine.map((d, i) => (\n <tr key={i}>\n <td><Code size=\"sm\">{d.candidate.actionId}</Code></td>\n <td><Routes candidate={d.candidate} /></td>\n <td>{dropReason(d)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </Stage>\n );\n}\n\nfunction RankedStage({ ranked }: { ranked: readonly RankedCandidate[] }) {\n if (ranked.length === 0) return <Stage title=\"Ranked\" note=\"nothing survived the filters\" />;\n return (\n <Stage title=\"Ranked\" note=\"highest first, with what the walk did at each\">\n <table className={s.table}>\n <thead>\n <tr>\n <th className={s.num}>#</th><th>Action</th><th>Placed by</th><th>Walk</th>\n <th>Tier</th><th>Specificity</th><th>Rule</th><th>Route</th>\n </tr>\n </thead>\n <tbody>\n {ranked.map((r, i) => {\n const c = r.candidate;\n const winner = isWinner(r.walk);\n return (\n <tr\n key={i}\n className={winner ? s.winner : undefined}\n data-walk={r.walk.kind}\n data-winner={winner || undefined}\n aria-current={winner || undefined}\n >\n <td className={s.num}>{i + 1}</td>\n <td><Code size=\"sm\">{c.actionId}</Code></td>\n <td>\n <Badge size=\"xs\" variant=\"outline\" status={r.placedBy.step === 'first' ? 'muted' : 'warn'}>\n {placedByLabel(r.placedBy)}\n </Badge>\n </td>\n <td className={s.walk} data-walk={r.walk.kind}>{walkLabel(r.walk)}</td>\n <td>{c.scope}</td>\n <td className={s.num} aria-label=\"target, modifiers, phase, exact\">{c.specificity.join(' ')}</td>\n <td>{c.eligible ? <Code size=\"sm\" variant=\"plain\">{c.eligible}</Code> : <span className={s.muted}>none</span>}</td>\n <td><Routes candidate={c} /></td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </Stage>\n );\n}\n\n/** Props for {@link FallthroughDiagram}. */\nexport interface FallthroughDiagramProps {\n record: DispatchRecord;\n className?: string;\n}\n\n/**\n * One dispatch drawn top to bottom: the input, every binding it matched, what\n * the claim and eligibility filters dropped, and the ranked survivors with the\n * step that placed each one and what the walk did with it. The winner's row\n * carries `data-winner`.\n */\nexport function FallthroughDiagram({ record, className }: FallthroughDiagramProps) {\n const cls = className ? `${s.diagram} ${className}` : s.diagram;\n return (\n <div className={cls} data-outcome={record.outcome} data-predicted={record.predicted || undefined}>\n <InputLine input={record.input} predicted={record.predicted} />\n {record.matched.length === 0 ? (\n <p className={s.nothing}>Nothing matched this input.</p>\n ) : (\n <>\n <MatchedStage matched={record.matched} />\n <FilterBand title=\"Claim\" filter=\"claim\" dropped={record.dropped} />\n <FilterBand title=\"Eligibility\" filter=\"ineligible\" dropped={record.dropped} />\n <RankedStage ranked={record.ranked} />\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"FallthroughDiagram-Cn6QLWKA.js","names":["_Fragment","s","Badge","Code","keySpecsFromMods","KeyCap","KeySequence","keySpecFromKey","GestureRoute","FallthroughDiagram_module_default","placedByLabel","p","walkLabel","w","Fragment","isWinner","MOD_ORDER","InputLine","mods","input","m","predicted","Routes","candidate","r","Stage","id","useId","filter","title","note","children","MatchedStage","matched","c","i","dropReason","d","FilterBand","mine","dropped","RankedStage","ranked","winner","FallthroughDiagram","cls","className","record"],"sources":["../../src/components/FallthroughDiagram/FallthroughDiagram.module.css","../../src/components/FallthroughDiagram/FallthroughDiagram.tsx"],"sourcesContent":[".diagram {\n display: grid;\n gap: var(--wzl-space-3);\n color: var(--wzl-fg);\n font-family: var(--wzl-font-ui);\n font-size: var(--wzl-font-size-sm);\n}\n\n.input {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--wzl-space-2) var(--wzl-space-4);\n margin: 0;\n color: var(--wzl-fg-muted);\n}\n.field { display: flex; align-items: baseline; gap: var(--wzl-space-1); }\n.field dt { margin: 0; }\n.field dd {\n margin: 0;\n color: var(--wzl-fg);\n font-weight: var(--wzl-font-weight-medium);\n}\n\n.stage {\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-md);\n background: var(--wzl-surface);\n padding: var(--wzl-space-2) var(--wzl-space-3);\n /* Ids and rules do not wrap mid-word; a narrow host scrolls the stage instead. */\n overflow-x: auto;\n}\n.stage[data-filter] { background: var(--wzl-surface-sunken); }\n\n.stageTitle {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: var(--wzl-space-2);\n margin: 0;\n font-size: var(--wzl-font-size-sm);\n font-weight: var(--wzl-font-weight-bold);\n}\n.stageTitle + .table { margin-top: var(--wzl-space-2); }\n.note { color: var(--wzl-fg-muted); font-weight: var(--wzl-font-weight-medium); }\n\n.table { width: 100%; border-collapse: collapse; }\n.table th {\n text-align: start;\n color: var(--wzl-fg-muted);\n font-size: var(--wzl-font-size-xs);\n font-weight: var(--wzl-font-weight-bold);\n padding: var(--wzl-space-1) var(--wzl-space-2);\n}\n.table td {\n padding: var(--wzl-space-1) var(--wzl-space-2);\n border-top: 1px solid var(--wzl-border);\n vertical-align: top;\n white-space: nowrap;\n}\n\n.table code { white-space: nowrap; overflow-wrap: normal; }\n\n.routes { display: inline-flex; flex-direction: column; align-items: flex-start; gap: var(--wzl-space-1); }\n\n.num {\n composes: numeric from '@weasel-js/theme/numeric.module.css';\n text-align: end;\n white-space: nowrap;\n}\n\n.muted { color: var(--wzl-fg-muted); }\n\n.walk { color: var(--wzl-fg-muted); }\n.walk[data-walk='fired'],\n.walk[data-walk='would-fire'] { color: var(--wzl-success); font-weight: var(--wzl-font-weight-bold); }\n.walk[data-walk='declined'],\n.walk[data-walk='empty-handle'],\n.walk[data-walk='misbound'],\n.walk[data-walk='no-such-action'] { color: var(--wzl-fg); }\n\n.winner td { background: color-mix(in srgb, var(--wzl-success) 12%, transparent); }\n.winner td:first-child { box-shadow: inset 3px 0 var(--wzl-success); }\n\n.nothing { margin: 0; color: var(--wzl-fg-muted); }\n","import { useId, type ReactNode } from 'react';\nimport type {\n DispatchRecord,\n DispatchRecordInput,\n DroppedCandidate,\n PlacedBy,\n RankedCandidate,\n RecordCandidate,\n WalkStep,\n} from '@weasel-js/core/routing';\nimport { Badge } from '../Badge';\nimport { Code } from '../Code';\nimport { GestureRoute } from '../GestureRoute';\nimport { KeyCap, KeySequence, keySpecFromKey, keySpecsFromMods, type LogicalModSpec } from '../Keycaps';\nimport s from './FallthroughDiagram.module.css';\n\nfunction placedByLabel(p: PlacedBy): string {\n return p.step === 'specificity' ? `specificity · ${p.part}` : p.step;\n}\n\nfunction walkLabel(w: WalkStep): ReactNode {\n switch (w.kind) {\n case 'fired': return 'fired';\n case 'would-fire': return 'would fire';\n case 'declined': return <>declined: {w.reason}</>;\n case 'empty-handle': return 'empty handle';\n case 'misbound': return 'misbound';\n case 'duplicate': return 'duplicate';\n case 'no-such-action': return 'no such action';\n case 'not-asked': return 'not asked';\n case 'outranked': return 'outranked';\n }\n}\n\nconst isWinner = (w: WalkStep) => w.kind === 'fired' || w.kind === 'would-fire';\n\nconst MOD_ORDER = ['shift', 'alt', 'ctrl', 'meta'] as const;\n\nfunction InputLine({ input, predicted }: { input: DispatchRecordInput; predicted: boolean }) {\n const mods: LogicalModSpec[] = MOD_ORDER.filter((m) => input.modifiers[m]).map((name) => ({ name }));\n return (\n <dl className={s.input} aria-label=\"Input\">\n <div className={s.field}>\n <dt>Event</dt>\n <dd>{input.eventKind}</dd>\n </div>\n {input.key !== undefined && (\n <div className={s.field}>\n <dt>Key</dt>\n <dd data-key={input.key}>\n <KeyCap label={keySpecFromKey(input.key, { legend: 'text' }).label} variant=\"minimal\" />\n </dd>\n </div>\n )}\n <div className={s.field}>\n <dt>Modifiers</dt>\n <dd>{mods.length > 0 ? <KeySequence keys={keySpecsFromMods(mods)} variant=\"minimal\" /> : 'none'}</dd>\n </div>\n {input.viewId !== null && (\n <div className={s.field}>\n <dt>View</dt>\n <dd>{input.viewId}</dd>\n </div>\n )}\n {input.mode !== undefined && (\n <div className={s.field}>\n <dt>Mode</dt>\n <dd>{input.mode}</dd>\n </div>\n )}\n {predicted && (\n <div className={s.field}>\n <dd><Badge size=\"xs\" status=\"accent\" variant=\"subtle\">predicted</Badge></dd>\n </div>\n )}\n </dl>\n );\n}\n\nfunction Routes({ candidate }: { candidate: RecordCandidate }) {\n return (\n <span className={s.routes}>\n {candidate.routes.map((r) => <GestureRoute key={r} route={r} size=\"sm\" />)}\n </span>\n );\n}\n\nfunction Stage({ title, note, filter, children }: {\n title: string;\n note?: ReactNode;\n filter?: DroppedCandidate['filter'];\n children?: ReactNode;\n}) {\n const id = useId();\n return (\n <section className={s.stage} aria-labelledby={id} data-filter={filter}>\n <h3 className={s.stageTitle}>\n <span id={id}>{title}</span>\n {note && <span className={s.note}>{note}</span>}\n </h3>\n {children}\n </section>\n );\n}\n\nfunction MatchedStage({ matched }: { matched: readonly RecordCandidate[] }) {\n return (\n <Stage title=\"Matched\" note=\"every binding that fits the input\">\n <table className={s.table}>\n <thead>\n <tr><th>Action</th><th>Route</th><th>Owner</th><th>Tier</th></tr>\n </thead>\n <tbody>\n {matched.map((c, i) => (\n <tr key={i}>\n <td><Code size=\"sm\">{c.actionId}</Code></td>\n <td><Routes candidate={c} /></td>\n <td className={c.ownerId === null ? s.muted : undefined}>{c.ownerId ?? 'action'}</td>\n <td>{c.scope}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </Stage>\n );\n}\n\nfunction dropReason(d: DroppedCandidate): ReactNode {\n if (d.filter === 'ineligible') return <><Code size=\"sm\" variant=\"plain\">{d.rule}</Code> does not hold</>;\n return d.owner !== undefined\n ? <>barred by the exclusive claim of <Code size=\"sm\" variant=\"plain\">{d.owner}</Code></>\n : 'barred by an exclusive claim';\n}\n\nfunction FilterBand({ title, filter, dropped }: {\n title: string;\n filter: DroppedCandidate['filter'];\n dropped: readonly DroppedCandidate[];\n}) {\n const mine = dropped.filter((d) => d.filter === filter);\n if (mine.length === 0) return <Stage title={title} filter={filter} note=\"dropped nothing\" />;\n return (\n <Stage title={title} filter={filter} note=\"dropped\">\n <table className={s.table}>\n <thead>\n <tr><th>Action</th><th>Route</th><th>Why</th></tr>\n </thead>\n <tbody>\n {mine.map((d, i) => (\n <tr key={i}>\n <td><Code size=\"sm\">{d.candidate.actionId}</Code></td>\n <td><Routes candidate={d.candidate} /></td>\n <td>{dropReason(d)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </Stage>\n );\n}\n\nfunction RankedStage({ ranked }: { ranked: readonly RankedCandidate[] }) {\n if (ranked.length === 0) return <Stage title=\"Ranked\" note=\"nothing survived the filters\" />;\n return (\n <Stage title=\"Ranked\" note=\"highest first, with what the walk did at each\">\n <table className={s.table}>\n <thead>\n <tr>\n <th className={s.num}>#</th><th>Action</th><th>Placed by</th><th>Walk</th>\n <th>Tier</th><th>Specificity</th><th>Rule</th><th>Route</th>\n </tr>\n </thead>\n <tbody>\n {ranked.map((r, i) => {\n const c = r.candidate;\n const winner = isWinner(r.walk);\n return (\n <tr\n key={i}\n className={winner ? s.winner : undefined}\n data-walk={r.walk.kind}\n data-winner={winner || undefined}\n aria-current={winner || undefined}\n >\n <td className={s.num}>{i + 1}</td>\n <td><Code size=\"sm\">{c.actionId}</Code></td>\n <td>\n <Badge size=\"xs\" variant=\"outline\" status={r.placedBy.step === 'first' ? 'muted' : 'warn'}>\n {placedByLabel(r.placedBy)}\n </Badge>\n </td>\n <td className={s.walk} data-walk={r.walk.kind}>{walkLabel(r.walk)}</td>\n <td>{c.scope}</td>\n <td className={s.num} aria-label=\"target, modifiers, phase, exact\">{c.specificity.join(' ')}</td>\n <td>{c.eligible ? <Code size=\"sm\" variant=\"plain\">{c.eligible}</Code> : <span className={s.muted}>none</span>}</td>\n <td><Routes candidate={c} /></td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </Stage>\n );\n}\n\n/** Props for {@link FallthroughDiagram}. */\nexport interface FallthroughDiagramProps {\n record: DispatchRecord;\n className?: string;\n}\n\n/**\n * One dispatch drawn top to bottom: the input, every binding it matched, what\n * the claim and eligibility filters dropped, and the ranked survivors with the\n * step that placed each one and what the walk did with it. The winner's row\n * carries `data-winner`.\n */\nexport function FallthroughDiagram({ record, className }: FallthroughDiagramProps) {\n const cls = className ? `${s.diagram} ${className}` : s.diagram;\n return (\n <div className={cls} data-outcome={record.outcome} data-predicted={record.predicted || undefined}>\n <InputLine input={record.input} predicted={record.predicted} />\n {record.matched.length === 0 ? (\n <p className={s.nothing}>Nothing matched this input.</p>\n ) : (\n <>\n <MatchedStage matched={record.matched} />\n <FilterBand title=\"Claim\" filter=\"claim\" dropped={record.dropped} />\n <FilterBand title=\"Eligibility\" filter=\"ineligible\" dropped={record.dropped} />\n <RankedStage ranked={record.ranked} />\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { n as e } from "./paintValue-BpaGPjV3.js";
2
- import { t } from "./Button-XB-T75YL.js";
2
+ import { t } from "./Button-CQA-K1g9.js";
3
3
  import { i as n } from "./RecentColorsProvider-DHcfk25u.js";
4
4
  import { t as r } from "./paintPreview-CuSZiHpR.js";
5
5
  import { jsx as i, jsxs as a } from "react/jsx-runtime";
@@ -109,4 +109,4 @@ function d(e) {
109
109
  //#endregion
110
110
  export { d as t };
111
111
 
112
- //# sourceMappingURL=FillStrokeSwatch-C4h3rUFM.js.map
112
+ //# sourceMappingURL=FillStrokeSwatch-BkUhlpQL.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FillStrokeSwatch-C4h3rUFM.js","names":["s","solidColorOf","useRecordRecentColor","paintPreviewCss","FillStrokeSwatch_module_default","SLOT_LABEL","PICKER_FALLBACK","Chip","props","draft","setDraft","useState","record","none","paint","preview","pickerValue","slot","focused","color","FillStrokeSwatch","focusedPaint","fill","stroke","chip","anyButton","onToggleNone","onSwap","onReset","className","Button","shortcuts"],"sources":["../../src/components/FillStrokeSwatch/FillStrokeSwatch.module.css","../../src/components/FillStrokeSwatch/FillStrokeSwatch.tsx"],"sourcesContent":[".root {\n display: inline-flex;\n flex-direction: column;\n align-items: center;\n gap: var(--wzl-space-2);\n}\n\n/* The two chips overlap on a diagonal: fill up-left, behind; stroke\n down-right, in front, so the fill shows through the stroke's hole. */\n.pair {\n --chip: 32px;\n --offset: 12px;\n --ring: 8px;\n position: relative;\n inline-size: calc(var(--chip) + var(--offset));\n block-size: calc(var(--chip) + var(--offset));\n}\n\n.chip {\n position: absolute;\n inline-size: var(--chip);\n block-size: var(--chip);\n box-sizing: border-box;\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n}\n\n.chip[data-slot='fill'] {\n inset-block-start: 0;\n inset-inline-start: 0;\n}\n\n.chip[data-slot='stroke'] {\n inset-block-end: 0;\n inset-inline-end: 0;\n}\n\n.chip[data-focused] {\n border-color: var(--wzl-fg);\n box-shadow: 0 0 0 1px var(--wzl-fg);\n}\n\n.chip:has(.input:focus-visible) {\n outline: 2px solid var(--wzl-focus-ring);\n outline-offset: 1px;\n}\n\n/* The paint sits over the checker, so a translucent one reads as such. */\n.well {\n position: absolute;\n inset: 0;\n overflow: hidden;\n border-radius: calc(var(--wzl-radius-sm) - var(--wzl-border-w));\n background: repeating-conic-gradient(\n var(--wzl-checker-a) 0% 25%,\n var(--wzl-checker-b) 0% 50%\n )\n 50% / var(--wzl-checker-size) var(--wzl-checker-size);\n}\n\n/* The stroke well is a ring: the mask subtracts the content box from the\n padding box, so whatever is behind the chip shows through its hole. */\n.chip[data-slot='stroke'] .well {\n box-sizing: border-box;\n padding: var(--ring);\n mask:\n linear-gradient(#000, #000) padding-box,\n linear-gradient(#000, #000) content-box;\n mask-composite: exclude;\n}\n\n/* The hole's edge, so a focused stroke reads as focused on both sides. */\n.chip[data-slot='stroke'][data-focused]::before {\n content: '';\n position: absolute;\n inset: calc(var(--ring) - var(--wzl-border-w));\n border: 1px solid var(--wzl-fg);\n border-radius: 0;\n pointer-events: none;\n}\n\n.paint {\n position: absolute;\n inset: 0;\n}\n\n/* No paint: the surface crossed by the danger diagonal. */\n.chip[data-none] .well {\n background:\n linear-gradient(\n 45deg,\n transparent calc(50% - 1px),\n var(--wzl-danger) calc(50% - 1px),\n var(--wzl-danger) calc(50% + 1px),\n transparent calc(50% + 1px)\n ),\n var(--wzl-surface);\n}\n\n/* The native picker covers its chip, invisible: it takes the click and the\n keyboard focus, and the well draws the paint. */\n.input {\n position: absolute;\n inset: 0;\n inline-size: 100%;\n block-size: 100%;\n margin: 0;\n padding: 0;\n border: 0;\n opacity: 0;\n cursor: pointer;\n}\n\n.buttons {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n gap: var(--wzl-space-1);\n}\n","import { useState, type ReactElement } from 'react';\nimport type { FillStyle } from '@weasel-js/core';\nimport { paintPreviewCss } from '../../paintPreview';\nimport { solidColorOf } from '../paintValue';\nimport { Button } from '../Button';\nimport { useRecordRecentColor } from '../RecentColors/RecentColorsProvider';\nimport type { PaintSlot } from '../GradientEditor';\nimport s from './FillStrokeSwatch.module.css';\n\n/** Props for {@link FillStrokeSwatch}. */\nexport interface FillStrokeSwatchProps {\n /** `null` is an explicit \"no paint\". */\n fill: FillStyle | null | undefined;\n stroke: FillStyle | null | undefined;\n /** The slot that edits aimed at \"the current paint\" land on — the None\n * button, a palette click, a keyboard shortcut the app binds. */\n focused: PaintSlot;\n onFocusChange: (slot: PaintSlot) => void;\n /** Every tick of the color picker, as `#rrggbb`. */\n onInput?: (slot: PaintSlot, color: string) => void;\n /** Once when the picker closes, with its last color, if it moved. Return\n * the color actually applied when it differs from the picker's — the picker\n * is opaque, so an app keeping the paint's own alpha returns that — and the\n * recent colors record it instead. */\n onChange: (slot: PaintSlot, color: string) => string | void;\n /** Toggle a slot between no paint and a paint. Given, it adds the None\n * button (acting on `focused`) and shift-click on a chip. */\n onToggleNone?: (slot: PaintSlot) => void;\n /** Given, adds a Swap button that exchanges the two paints. */\n onSwap?: () => void;\n /** Given, adds a Default button that restores the app's default pair. */\n onReset?: () => void;\n /** Key hints for the buttons' tooltips. The app binds the keys itself. */\n shortcuts?: { none?: string; swap?: string; reset?: string };\n 'aria-label'?: string;\n className?: string;\n}\n\nconst SLOT_LABEL: Record<PaintSlot, string> = { fill: 'Fill', stroke: 'Stroke' };\nconst PICKER_FALLBACK: Record<PaintSlot, string> = { fill: '#ffffff', stroke: '#000000' };\n\nfunction Chip(props: {\n slot: PaintSlot;\n paint: FillStyle | null | undefined;\n focused: boolean;\n onFocusChange: (slot: PaintSlot) => void;\n onInput?: (slot: PaintSlot, color: string) => void;\n onChange: (slot: PaintSlot, color: string) => string | void;\n onToggleNone?: (slot: PaintSlot) => void;\n}): ReactElement {\n const { slot, paint, focused } = props;\n // Tracks the picker through a pick; the prop only catches up on commit.\n const [draft, setDraft] = useState<string | null>(null);\n const record = useRecordRecentColor();\n const none = paint === null;\n const preview = paintPreviewCss(paint);\n const pickerValue = (solidColorOf(paint) ?? PICKER_FALLBACK[slot]).slice(0, 7);\n\n return (\n <span\n className={s.chip}\n data-slot={slot}\n {...(focused ? { 'data-focused': '' } : {})}\n {...(none ? { 'data-none': '' } : {})}\n >\n <span className={s.well} aria-hidden=\"true\">\n {preview && <span className={s.paint} style={{ background: preview }} />}\n </span>\n <input\n type=\"color\"\n className={s.input}\n value={draft ?? pickerValue}\n aria-label={SLOT_LABEL[slot]}\n aria-current={focused ? 'true' : undefined}\n onFocus={() => props.onFocusChange(slot)}\n onClick={(e) => {\n props.onFocusChange(slot);\n // Shift-click is the none toggle; preventing it keeps the OS picker shut.\n if (e.shiftKey && props.onToggleNone) {\n e.preventDefault();\n props.onToggleNone(slot);\n }\n }}\n onInput={(e) => {\n const color = (e.target as HTMLInputElement).value;\n setDraft(color);\n props.onInput?.(slot, color);\n }}\n onBlur={() => {\n if (draft === null) return;\n setDraft(null);\n record(props.onChange(slot, draft) ?? draft);\n }}\n />\n </span>\n );\n}\n\n/**\n * The active fill and stroke as one overlapping pair, the way drawing apps\n * show the paint the next shape gets: fill a solid square behind, stroke a\n * frame in front of it. Each chip is a native color picker; the focused one\n * is what the None button — and whatever the app binds — acts on.\n *\n * Holds no state of its own: the paints and the focused slot are props, and\n * the chips report picks through `onInput`/`onChange` for the app to route —\n * into its own active-paint state, into the selection through\n * `useOngoingAction`, or both.\n */\nexport function FillStrokeSwatch(props: FillStrokeSwatchProps): ReactElement {\n const {\n fill, stroke, focused, onFocusChange, onInput, onChange,\n onToggleNone, onSwap, onReset, shortcuts, className,\n } = props;\n const focusedPaint = focused === 'fill' ? fill : stroke;\n const chip = { onFocusChange, onInput, onChange, onToggleNone };\n const anyButton = onToggleNone || onSwap || onReset;\n\n return (\n <div\n className={[s.root, className].filter(Boolean).join(' ')}\n role=\"group\"\n aria-label={props['aria-label'] ?? 'Fill and stroke'}\n >\n <div className={s.pair}>\n <Chip slot=\"fill\" paint={fill} focused={focused === 'fill'} {...chip} />\n <Chip slot=\"stroke\" paint={stroke} focused={focused === 'stroke'} {...chip} />\n </div>\n {anyButton && (\n <div className={s.buttons}>\n {onToggleNone && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n pressed={focusedPaint === null}\n onClick={() => onToggleNone(focused)}\n shortcut={shortcuts?.none}\n >\n None\n </Button>\n )}\n {onSwap && (\n <Button variant=\"ghost\" size=\"sm\" onClick={onSwap} shortcut={shortcuts?.swap}>\n Swap\n </Button>\n )}\n {onReset && (\n <Button variant=\"ghost\" size=\"sm\" onClick={onReset} shortcut={shortcuts?.reset}>\n Default\n </Button>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;GCsCMK,IAAwC;CAAE,MAAM;CAAQ,QAAQ;AAAS,GACzEC,IAA6C;CAAE,MAAM;CAAW,QAAQ;AAAU;AAExF,SAASC,EAAK,GAQG;CACf,IAAM,EAAE,SAAM,UAAO,eAAYC,GAE3B,CAACC,GAAOC,KAAYC,EAAwB,IAAI,GAChDC,IAASV,EAAqB,GAC9BW,IAAOC,MAAU,MACjBC,IAAUZ,EAAgBW,CAAK,GAC/BE,KAAef,EAAaa,CAAK,KAAKR,EAAgBW,GAAA,CAAO,MAAM,GAAG,CAAC;CAE7E,OACE,kBAAC,QAAD;EACE,WAAWjB,EAAE;EACb,aAAWiB;EACX,GAAKC,IAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;EACzC,GAAKL,IAAO,EAAE,aAAa,GAAG,IAAI,CAAC;EAJrC,UAAA,CAME,kBAAC,QAAD;GAAM,WAAWb,EAAE;GAAM,eAAY;GAClC,UAAAe,KAAW,kBAAC,QAAD;IAAM,WAAWf,EAAE;IAAO,OAAO,EAAE,YAAYe,EAAQ;GAAI,CAAA;EACnE,CAAA,GACN,kBAAC,SAAD;GACE,MAAK;GACL,WAAWf,EAAE;GACb,OAAOS,KAASO;GAChB,cAAYX,EAAWY;GACvB,gBAAcC,IAAU,SAAS,KAAA;GACjC,eAAeV,EAAM,cAAcS,CAAI;GACvC,UAAU,MAAM;IAGd,AAFAT,EAAM,cAAcS,CAAI,GAEpB,EAAE,YAAYT,EAAM,iBACtB,EAAE,eAAe,GACjBA,EAAM,aAAaS,CAAI;GAE3B;GACA,UAAU,MAAM;IACd,IAAME,IAAS,EAAE,OAA4B;IAE7C,AADAT,EAASS,CAAK,GACdX,EAAM,UAAUS,GAAME,CAAK;GAC7B;GACA,cAAc;IACZ,AAAIV,MAAU,SACdC,EAAS,IAAI,GACbE,EAAOJ,EAAM,SAASS,GAAMR,CAAK,KAAKA,CAAK;GAC7C;EACD,CAAA,CACG;;AAEV;AAaA,SAAgBW,EAAiB,GAA4C;CAC3E,IAAM,EACJ,SAAM,WAAQ,YAAS,kBAAe,YAAS,aAC/C,iBAAc,WAAQ,YAAS,cAAW,iBACxCZ,GACEa,IAAeH,MAAY,SAASI,IAAOC,GAC3CC,IAAO;EAAE;EAAe;EAAS;EAAU;CAAa,GACxDC,IAAYC,KAAgBC,KAAUC;CAE5C,OACE,kBAAC,OAAD;EACE,WAAW,CAAC5B,EAAE,MAAM6B,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;EACvD,MAAK;EACL,cAAYrB,EAAM,iBAAiB;EAHrC,UAAA,CAKE,kBAAC,OAAD;GAAK,WAAWR,EAAE;GAAlB,UAAA,CACE,kBAACO,GAAD;IAAM,MAAK;IAAO,OAAOe;IAAM,SAASJ,MAAY;IAAQ,GAAIM;GAAO,CAAA,GACvE,kBAACjB,GAAD;IAAM,MAAK;IAAS,OAAOgB;IAAQ,SAASL,MAAY;IAAU,GAAIM;GAAO,CAAA,CAC1E;EACJ,CAAA,GAAAC,KACC,kBAAC,OAAD;GAAK,WAAWzB,EAAE;GAAlB,UAAA;IACG0B,KACC,kBAACI,GAAD;KACE,SAAQ;KACR,MAAK;KACL,SAAST,MAAiB;KAC1B,eAAeK,EAAaR,CAAO;KACnC,UAAUa,GAAW;KACtB,UAAA;IAEO,CAAA;IAETJ,KACC,kBAACG,GAAD;KAAQ,SAAQ;KAAQ,MAAK;KAAK,SAASH;KAAQ,UAAUI,GAAW;KAAM,UAAA;IAEtE,CAAA;IAETH,KACC,kBAACE,GAAD;KAAQ,SAAQ;KAAQ,MAAK;KAAK,SAASF;KAAS,UAAUG,GAAW;KAAO,UAAA;IAExE,CAAA;GAEP;EAEJ,CAAA,CAAA;;AAET"}
1
+ {"version":3,"file":"FillStrokeSwatch-BkUhlpQL.js","names":["s","solidColorOf","useRecordRecentColor","paintPreviewCss","FillStrokeSwatch_module_default","SLOT_LABEL","PICKER_FALLBACK","Chip","props","draft","setDraft","useState","record","none","paint","preview","pickerValue","slot","focused","color","FillStrokeSwatch","focusedPaint","fill","stroke","chip","anyButton","onToggleNone","onSwap","onReset","className","Button","shortcuts"],"sources":["../../src/components/FillStrokeSwatch/FillStrokeSwatch.module.css","../../src/components/FillStrokeSwatch/FillStrokeSwatch.tsx"],"sourcesContent":[".root {\n display: inline-flex;\n flex-direction: column;\n align-items: center;\n gap: var(--wzl-space-2);\n}\n\n/* The two chips overlap on a diagonal: fill up-left, behind; stroke\n down-right, in front, so the fill shows through the stroke's hole. */\n.pair {\n --chip: 32px;\n --offset: 12px;\n --ring: 8px;\n position: relative;\n inline-size: calc(var(--chip) + var(--offset));\n block-size: calc(var(--chip) + var(--offset));\n}\n\n.chip {\n position: absolute;\n inline-size: var(--chip);\n block-size: var(--chip);\n box-sizing: border-box;\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n}\n\n.chip[data-slot='fill'] {\n inset-block-start: 0;\n inset-inline-start: 0;\n}\n\n.chip[data-slot='stroke'] {\n inset-block-end: 0;\n inset-inline-end: 0;\n}\n\n.chip[data-focused] {\n border-color: var(--wzl-fg);\n box-shadow: 0 0 0 1px var(--wzl-fg);\n}\n\n.chip:has(.input:focus-visible) {\n outline: 2px solid var(--wzl-focus-ring);\n outline-offset: 1px;\n}\n\n/* The paint sits over the checker, so a translucent one reads as such. */\n.well {\n position: absolute;\n inset: 0;\n overflow: hidden;\n border-radius: calc(var(--wzl-radius-sm) - var(--wzl-border-w));\n background: repeating-conic-gradient(\n var(--wzl-checker-a) 0% 25%,\n var(--wzl-checker-b) 0% 50%\n )\n 50% / var(--wzl-checker-size) var(--wzl-checker-size);\n}\n\n/* The stroke well is a ring: the mask subtracts the content box from the\n padding box, so whatever is behind the chip shows through its hole. */\n.chip[data-slot='stroke'] .well {\n box-sizing: border-box;\n padding: var(--ring);\n mask:\n linear-gradient(#000, #000) padding-box,\n linear-gradient(#000, #000) content-box;\n mask-composite: exclude;\n}\n\n/* The hole's edge, so a focused stroke reads as focused on both sides. */\n.chip[data-slot='stroke'][data-focused]::before {\n content: '';\n position: absolute;\n inset: calc(var(--ring) - var(--wzl-border-w));\n border: 1px solid var(--wzl-fg);\n border-radius: 0;\n pointer-events: none;\n}\n\n.paint {\n position: absolute;\n inset: 0;\n}\n\n/* No paint: the surface crossed by the danger diagonal. */\n.chip[data-none] .well {\n background:\n linear-gradient(\n 45deg,\n transparent calc(50% - 1px),\n var(--wzl-danger) calc(50% - 1px),\n var(--wzl-danger) calc(50% + 1px),\n transparent calc(50% + 1px)\n ),\n var(--wzl-surface);\n}\n\n/* The native picker covers its chip, invisible: it takes the click and the\n keyboard focus, and the well draws the paint. */\n.input {\n position: absolute;\n inset: 0;\n inline-size: 100%;\n block-size: 100%;\n margin: 0;\n padding: 0;\n border: 0;\n opacity: 0;\n cursor: pointer;\n}\n\n.buttons {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n gap: var(--wzl-space-1);\n}\n","import { useState, type ReactElement } from 'react';\nimport type { FillStyle } from '@weasel-js/core';\nimport { paintPreviewCss } from '../../paintPreview';\nimport { solidColorOf } from '../paintValue';\nimport { Button } from '../Button';\nimport { useRecordRecentColor } from '../RecentColors/RecentColorsProvider';\nimport type { PaintSlot } from '../GradientEditor';\nimport s from './FillStrokeSwatch.module.css';\n\n/** Props for {@link FillStrokeSwatch}. */\nexport interface FillStrokeSwatchProps {\n /** `null` is an explicit \"no paint\". */\n fill: FillStyle | null | undefined;\n stroke: FillStyle | null | undefined;\n /** The slot that edits aimed at \"the current paint\" land on — the None\n * button, a palette click, a keyboard shortcut the app binds. */\n focused: PaintSlot;\n onFocusChange: (slot: PaintSlot) => void;\n /** Every tick of the color picker, as `#rrggbb`. */\n onInput?: (slot: PaintSlot, color: string) => void;\n /** Once when the picker closes, with its last color, if it moved. Return\n * the color actually applied when it differs from the picker's — the picker\n * is opaque, so an app keeping the paint's own alpha returns that — and the\n * recent colors record it instead. */\n onChange: (slot: PaintSlot, color: string) => string | void;\n /** Toggle a slot between no paint and a paint. Given, it adds the None\n * button (acting on `focused`) and shift-click on a chip. */\n onToggleNone?: (slot: PaintSlot) => void;\n /** Given, adds a Swap button that exchanges the two paints. */\n onSwap?: () => void;\n /** Given, adds a Default button that restores the app's default pair. */\n onReset?: () => void;\n /** Key hints for the buttons' tooltips. The app binds the keys itself. */\n shortcuts?: { none?: string; swap?: string; reset?: string };\n 'aria-label'?: string;\n className?: string;\n}\n\nconst SLOT_LABEL: Record<PaintSlot, string> = { fill: 'Fill', stroke: 'Stroke' };\nconst PICKER_FALLBACK: Record<PaintSlot, string> = { fill: '#ffffff', stroke: '#000000' };\n\nfunction Chip(props: {\n slot: PaintSlot;\n paint: FillStyle | null | undefined;\n focused: boolean;\n onFocusChange: (slot: PaintSlot) => void;\n onInput?: (slot: PaintSlot, color: string) => void;\n onChange: (slot: PaintSlot, color: string) => string | void;\n onToggleNone?: (slot: PaintSlot) => void;\n}): ReactElement {\n const { slot, paint, focused } = props;\n // Tracks the picker through a pick; the prop only catches up on commit.\n const [draft, setDraft] = useState<string | null>(null);\n const record = useRecordRecentColor();\n const none = paint === null;\n const preview = paintPreviewCss(paint);\n const pickerValue = (solidColorOf(paint) ?? PICKER_FALLBACK[slot]).slice(0, 7);\n\n return (\n <span\n className={s.chip}\n data-slot={slot}\n {...(focused ? { 'data-focused': '' } : {})}\n {...(none ? { 'data-none': '' } : {})}\n >\n <span className={s.well} aria-hidden=\"true\">\n {preview && <span className={s.paint} style={{ background: preview }} />}\n </span>\n <input\n type=\"color\"\n className={s.input}\n value={draft ?? pickerValue}\n aria-label={SLOT_LABEL[slot]}\n aria-current={focused ? 'true' : undefined}\n onFocus={() => props.onFocusChange(slot)}\n onClick={(e) => {\n props.onFocusChange(slot);\n // Shift-click is the none toggle; preventing it keeps the OS picker shut.\n if (e.shiftKey && props.onToggleNone) {\n e.preventDefault();\n props.onToggleNone(slot);\n }\n }}\n onInput={(e) => {\n const color = (e.target as HTMLInputElement).value;\n setDraft(color);\n props.onInput?.(slot, color);\n }}\n onBlur={() => {\n if (draft === null) return;\n setDraft(null);\n record(props.onChange(slot, draft) ?? draft);\n }}\n />\n </span>\n );\n}\n\n/**\n * The active fill and stroke as one overlapping pair, the way drawing apps\n * show the paint the next shape gets: fill a solid square behind, stroke a\n * frame in front of it. Each chip is a native color picker; the focused one\n * is what the None button — and whatever the app binds — acts on.\n *\n * Holds no state of its own: the paints and the focused slot are props, and\n * the chips report picks through `onInput`/`onChange` for the app to route —\n * into its own active-paint state, into the selection through\n * `useOngoingAction`, or both.\n */\nexport function FillStrokeSwatch(props: FillStrokeSwatchProps): ReactElement {\n const {\n fill, stroke, focused, onFocusChange, onInput, onChange,\n onToggleNone, onSwap, onReset, shortcuts, className,\n } = props;\n const focusedPaint = focused === 'fill' ? fill : stroke;\n const chip = { onFocusChange, onInput, onChange, onToggleNone };\n const anyButton = onToggleNone || onSwap || onReset;\n\n return (\n <div\n className={[s.root, className].filter(Boolean).join(' ')}\n role=\"group\"\n aria-label={props['aria-label'] ?? 'Fill and stroke'}\n >\n <div className={s.pair}>\n <Chip slot=\"fill\" paint={fill} focused={focused === 'fill'} {...chip} />\n <Chip slot=\"stroke\" paint={stroke} focused={focused === 'stroke'} {...chip} />\n </div>\n {anyButton && (\n <div className={s.buttons}>\n {onToggleNone && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n pressed={focusedPaint === null}\n onClick={() => onToggleNone(focused)}\n shortcut={shortcuts?.none}\n >\n None\n </Button>\n )}\n {onSwap && (\n <Button variant=\"ghost\" size=\"sm\" onClick={onSwap} shortcut={shortcuts?.swap}>\n Swap\n </Button>\n )}\n {onReset && (\n <Button variant=\"ghost\" size=\"sm\" onClick={onReset} shortcut={shortcuts?.reset}>\n Default\n </Button>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;GCsCMK,IAAwC;CAAE,MAAM;CAAQ,QAAQ;AAAS,GACzEC,IAA6C;CAAE,MAAM;CAAW,QAAQ;AAAU;AAExF,SAASC,EAAK,GAQG;CACf,IAAM,EAAE,SAAM,UAAO,eAAYC,GAE3B,CAACC,GAAOC,KAAYC,EAAwB,IAAI,GAChDC,IAASV,EAAqB,GAC9BW,IAAOC,MAAU,MACjBC,IAAUZ,EAAgBW,CAAK,GAC/BE,KAAef,EAAaa,CAAK,KAAKR,EAAgBW,GAAA,CAAO,MAAM,GAAG,CAAC;CAE7E,OACE,kBAAC,QAAD;EACE,WAAWjB,EAAE;EACb,aAAWiB;EACX,GAAKC,IAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;EACzC,GAAKL,IAAO,EAAE,aAAa,GAAG,IAAI,CAAC;EAJrC,UAAA,CAME,kBAAC,QAAD;GAAM,WAAWb,EAAE;GAAM,eAAY;GAClC,UAAAe,KAAW,kBAAC,QAAD;IAAM,WAAWf,EAAE;IAAO,OAAO,EAAE,YAAYe,EAAQ;GAAI,CAAA;EACnE,CAAA,GACN,kBAAC,SAAD;GACE,MAAK;GACL,WAAWf,EAAE;GACb,OAAOS,KAASO;GAChB,cAAYX,EAAWY;GACvB,gBAAcC,IAAU,SAAS,KAAA;GACjC,eAAeV,EAAM,cAAcS,CAAI;GACvC,UAAU,MAAM;IAGd,AAFAT,EAAM,cAAcS,CAAI,GAEpB,EAAE,YAAYT,EAAM,iBACtB,EAAE,eAAe,GACjBA,EAAM,aAAaS,CAAI;GAE3B;GACA,UAAU,MAAM;IACd,IAAME,IAAS,EAAE,OAA4B;IAE7C,AADAT,EAASS,CAAK,GACdX,EAAM,UAAUS,GAAME,CAAK;GAC7B;GACA,cAAc;IACZ,AAAIV,MAAU,SACdC,EAAS,IAAI,GACbE,EAAOJ,EAAM,SAASS,GAAMR,CAAK,KAAKA,CAAK;GAC7C;EACD,CAAA,CACG;;AAEV;AAaA,SAAgBW,EAAiB,GAA4C;CAC3E,IAAM,EACJ,SAAM,WAAQ,YAAS,kBAAe,YAAS,aAC/C,iBAAc,WAAQ,YAAS,cAAW,iBACxCZ,GACEa,IAAeH,MAAY,SAASI,IAAOC,GAC3CC,IAAO;EAAE;EAAe;EAAS;EAAU;CAAa,GACxDC,IAAYC,KAAgBC,KAAUC;CAE5C,OACE,kBAAC,OAAD;EACE,WAAW,CAAC5B,EAAE,MAAM6B,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;EACvD,MAAK;EACL,cAAYrB,EAAM,iBAAiB;EAHrC,UAAA,CAKE,kBAAC,OAAD;GAAK,WAAWR,EAAE;GAAlB,UAAA,CACE,kBAACO,GAAD;IAAM,MAAK;IAAO,OAAOe;IAAM,SAASJ,MAAY;IAAQ,GAAIM;GAAO,CAAA,GACvE,kBAACjB,GAAD;IAAM,MAAK;IAAS,OAAOgB;IAAQ,SAASL,MAAY;IAAU,GAAIM;GAAO,CAAA,CAC1E;EACJ,CAAA,GAAAC,KACC,kBAAC,OAAD;GAAK,WAAWzB,EAAE;GAAlB,UAAA;IACG0B,KACC,kBAACI,GAAD;KACE,SAAQ;KACR,MAAK;KACL,SAAST,MAAiB;KAC1B,eAAeK,EAAaR,CAAO;KACnC,UAAUa,GAAW;KACtB,UAAA;IAEO,CAAA;IAETJ,KACC,kBAACG,GAAD;KAAQ,SAAQ;KAAQ,MAAK;KAAK,SAASH;KAAQ,UAAUI,GAAW;KAAM,UAAA;IAEtE,CAAA;IAETH,KACC,kBAACE,GAAD;KAAQ,SAAQ;KAAQ,MAAK;KAAK,SAASF;KAAS,UAAUG,GAAW;KAAO,UAAA;IAExE,CAAA;GAEP;EAEJ,CAAA,CAAA;;AAET"}
@@ -1,4 +1,4 @@
1
- import { t as e } from "./Select-ouV8O6cp.js";
1
+ import { t as e } from "./Select-BhHwjNsb.js";
2
2
  import { jsx as t } from "react/jsx-runtime";
3
3
  import { listCanvasFonts as n, listFonts as r, resolveFontVariant as i } from "@weasel-js/font";
4
4
  //#region src/components/FontFamilySelect/FontFamilySelect.tsx
@@ -26,4 +26,4 @@ function o(i) {
26
26
  //#endregion
27
27
  export { o as t };
28
28
 
29
- //# sourceMappingURL=FontFamilySelect-DEvMXyZd.js.map
29
+ //# sourceMappingURL=FontFamilySelect-CXN7O2-p.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FontFamilySelect-DEvMXyZd.js","names":["Select","unregisteredLabel","resolveFontVariant","family","weight","style","substituted","FontFamilySelect","props","baked","listFonts","canvas","listCanvasFonts","f","c","available","options","value","fontStyle","mixed","onChange"],"sources":["../../src/components/FontFamilySelect/FontFamilySelect.tsx"],"sourcesContent":["import { listCanvasFonts, listFonts, resolveFontVariant } from '@weasel-js/font';\nimport { Select } from '../Select';\n\n/** Props for {@link FontFamilySelect}. */\nexport interface FontFamilySelectProps {\n /** Current family, or `undefined` when the sources disagree (`mixed`). */\n value?: string;\n /** Indeterminate presentation — the aggregated sources name different families. */\n mixed?: boolean;\n onChange: (family: string) => void;\n /** The weight / style the text actually renders at, so the substitution\n * probe reports the variant that paints rather than a nominal 400/normal. */\n weight?: number;\n fontStyle?: 'normal' | 'italic';\n 'aria-label'?: string;\n className?: string;\n}\n\n/** Label for a value that isn't in the registry: name the substitute when the\n * fallback chain found one, otherwise say only what is known. */\nfunction unregisteredLabel(family: string, weight: number, style: 'normal' | 'italic'): string {\n const { substituted } = resolveFontVariant(family, weight, style);\n return substituted\n ? `${family} — not loaded, showing ${substituted.resolved}`\n : `${family} — not loaded`;\n}\n\n/**\n * Font-family picker driven by the live font registry.\n *\n * **What it offers.** Both tiers that can actually paint: `listFonts()` for\n * families with a baked atlas, and `listCanvasFonts()` for families the\n * dynamic canvas-SDF tier will serve. Offer what will render, invent\n * nothing — and `listCanvasFonts` reports service rather than membership,\n * so a family auto-enrolled under a policy that is no longer in force\n * doesn't appear.\n *\n * **What it does with a value it can't offer.** Nothing about the model\n * stops a node from naming a family that was never registered — a pasted\n * node, an imported SVG, a document from a session that loaded more fonts.\n * The control keeps that value visible as its own entry and names what is\n * actually painting instead, read structurally off\n * `resolveFontVariant().substituted`. Dropping the value would silently\n * rewrite the document on the next edit; showing it bare would claim a font\n * is in use that isn't.\n */\nexport function FontFamilySelect(props: FontFamilySelectProps) {\n const { value, mixed = false, onChange, weight = 400, fontStyle = 'normal', className } = props;\n // Both tiers that actually render: families with a baked atlas, and\n // families the dynamic canvas-SDF tier will serve. Neither list is a guess\n // — `listCanvasFonts` reports what the registry will really route, so a\n // family reaches this menu only if it can paint.\n const baked = listFonts();\n const canvas = listCanvasFonts().filter((c) => !baked.some((f) => f.family === c.family));\n const available = [...baked.map((f) => f.family), ...canvas.map((c) => c.family)];\n const options = available.map((family) => ({ value: family, label: family }));\n if (value !== undefined && !available.includes(value)) {\n options.push({ value, label: unregisteredLabel(value, weight, fontStyle) });\n }\n return (\n <Select<string>\n className={className}\n options={options}\n selectedKey={mixed ? null : (value ?? null)}\n placeholder={mixed ? 'Mixed' : undefined}\n onSelectionChange={onChange}\n aria-label={props['aria-label'] ?? 'Font'}\n />\n );\n}\n"],"mappings":";;;;AAoBA,SAASC,EAAkB,GAAgB,GAAgB,GAAoC;CAC7F,IAAM,EAAE,mBAAgBC,EAAmBC,GAAQC,GAAQC,CAAK;CAChE,OAAOC,IACH,GAAGH,EAAO,yBAAyBG,EAAY,aAC/C,GAAGH,EAAO;AAChB;AAqBA,SAAgBI,EAAiB,GAA8B;CAC7D,IAAM,EAAE,UAAO,WAAQ,IAAO,aAAU,YAAS,KAAK,eAAY,UAAU,iBAAcC,GAKpFC,IAAQC,EAAU,GAClBC,IAASC,EAAgB,CAAC,CAAC,QAAQ,MAAM,CAACH,EAAM,MAAM,MAAMI,EAAE,WAAWC,EAAE,MAAM,CAAC,GAClFC,IAAY,CAAC,GAAGN,EAAM,KAAK,MAAMI,EAAE,MAAM,GAAG,GAAGF,EAAO,KAAK,MAAMG,EAAE,MAAM,CAAC,GAC1EE,IAAUD,EAAU,KAAK,OAAY;EAAE,OAAOZ;EAAQ,OAAOA;CAAO,EAAE;CAI5E,OAHIc,MAAU,KAAA,KAAa,CAACF,EAAU,SAASE,CAAK,KAClDD,EAAQ,KAAK;EAAE;EAAO,OAAOf,EAAkBgB,GAAOb,GAAQc,CAAS;CAAE,CAAC,GAG1E,kBAAClB,GAAD;EACa;EACF;EACT,aAAamB,IAAQ,OAAQF,KAAS;EACtC,aAAaE,IAAQ,UAAU,KAAA;EAC/B,mBAAmBC;EACnB,cAAYZ,EAAM,iBAAiB;CACpC,CAAA;AAEL"}
1
+ {"version":3,"file":"FontFamilySelect-CXN7O2-p.js","names":["Select","unregisteredLabel","resolveFontVariant","family","weight","style","substituted","FontFamilySelect","props","baked","listFonts","canvas","listCanvasFonts","f","c","available","options","value","fontStyle","mixed","onChange"],"sources":["../../src/components/FontFamilySelect/FontFamilySelect.tsx"],"sourcesContent":["import { listCanvasFonts, listFonts, resolveFontVariant } from '@weasel-js/font';\nimport { Select } from '../Select';\n\n/** Props for {@link FontFamilySelect}. */\nexport interface FontFamilySelectProps {\n /** Current family, or `undefined` when the sources disagree (`mixed`). */\n value?: string;\n /** Indeterminate presentation — the aggregated sources name different families. */\n mixed?: boolean;\n onChange: (family: string) => void;\n /** The weight / style the text actually renders at, so the substitution\n * probe reports the variant that paints rather than a nominal 400/normal. */\n weight?: number;\n fontStyle?: 'normal' | 'italic';\n 'aria-label'?: string;\n className?: string;\n}\n\n/** Label for a value that isn't in the registry: name the substitute when the\n * fallback chain found one, otherwise say only what is known. */\nfunction unregisteredLabel(family: string, weight: number, style: 'normal' | 'italic'): string {\n const { substituted } = resolveFontVariant(family, weight, style);\n return substituted\n ? `${family} — not loaded, showing ${substituted.resolved}`\n : `${family} — not loaded`;\n}\n\n/**\n * Font-family picker driven by the live font registry.\n *\n * **What it offers.** Both tiers that can actually paint: `listFonts()` for\n * families with a baked atlas, and `listCanvasFonts()` for families the\n * dynamic canvas-SDF tier will serve. Offer what will render, invent\n * nothing — and `listCanvasFonts` reports service rather than membership,\n * so a family auto-enrolled under a policy that is no longer in force\n * doesn't appear.\n *\n * **What it does with a value it can't offer.** Nothing about the model\n * stops a node from naming a family that was never registered — a pasted\n * node, an imported SVG, a document from a session that loaded more fonts.\n * The control keeps that value visible as its own entry and names what is\n * actually painting instead, read structurally off\n * `resolveFontVariant().substituted`. Dropping the value would silently\n * rewrite the document on the next edit; showing it bare would claim a font\n * is in use that isn't.\n */\nexport function FontFamilySelect(props: FontFamilySelectProps) {\n const { value, mixed = false, onChange, weight = 400, fontStyle = 'normal', className } = props;\n // Both tiers that actually render: families with a baked atlas, and\n // families the dynamic canvas-SDF tier will serve. Neither list is a guess\n // — `listCanvasFonts` reports what the registry will really route, so a\n // family reaches this menu only if it can paint.\n const baked = listFonts();\n const canvas = listCanvasFonts().filter((c) => !baked.some((f) => f.family === c.family));\n const available = [...baked.map((f) => f.family), ...canvas.map((c) => c.family)];\n const options = available.map((family) => ({ value: family, label: family }));\n if (value !== undefined && !available.includes(value)) {\n options.push({ value, label: unregisteredLabel(value, weight, fontStyle) });\n }\n return (\n <Select<string>\n className={className}\n options={options}\n selectedKey={mixed ? null : (value ?? null)}\n placeholder={mixed ? 'Mixed' : undefined}\n onSelectionChange={onChange}\n aria-label={props['aria-label'] ?? 'Font'}\n />\n );\n}\n"],"mappings":";;;;AAoBA,SAASC,EAAkB,GAAgB,GAAgB,GAAoC;CAC7F,IAAM,EAAE,mBAAgBC,EAAmBC,GAAQC,GAAQC,CAAK;CAChE,OAAOC,IACH,GAAGH,EAAO,yBAAyBG,EAAY,aAC/C,GAAGH,EAAO;AAChB;AAqBA,SAAgBI,EAAiB,GAA8B;CAC7D,IAAM,EAAE,UAAO,WAAQ,IAAO,aAAU,YAAS,KAAK,eAAY,UAAU,iBAAcC,GAKpFC,IAAQC,EAAU,GAClBC,IAASC,EAAgB,CAAC,CAAC,QAAQ,MAAM,CAACH,EAAM,MAAM,MAAMI,EAAE,WAAWC,EAAE,MAAM,CAAC,GAClFC,IAAY,CAAC,GAAGN,EAAM,KAAK,MAAMI,EAAE,MAAM,GAAG,GAAGF,EAAO,KAAK,MAAMG,EAAE,MAAM,CAAC,GAC1EE,IAAUD,EAAU,KAAK,OAAY;EAAE,OAAOZ;EAAQ,OAAOA;CAAO,EAAE;CAI5E,OAHIc,MAAU,KAAA,KAAa,CAACF,EAAU,SAASE,CAAK,KAClDD,EAAQ,KAAK;EAAE;EAAO,OAAOf,EAAkBgB,GAAOb,GAAQc,CAAS;CAAE,CAAC,GAG1E,kBAAClB,GAAD;EACa;EACF;EACT,aAAamB,IAAQ,OAAQF,KAAS;EACtC,aAAaE,IAAQ,UAAU,KAAA;EAC/B,mBAAmBC;EACnB,cAAYZ,EAAM,iBAAiB;CACpC,CAAA;AAEL"}
@@ -1,4 +1,4 @@
1
- import { t as e } from "./Select-ouV8O6cp.js";
1
+ import { t as e } from "./Select-BhHwjNsb.js";
2
2
  import { jsx as t } from "react/jsx-runtime";
3
3
  import { listFontWeights as n } from "@weasel-js/font";
4
4
  //#region src/components/FontWeightSelect/FontWeightSelect.tsx
@@ -46,4 +46,4 @@ function o(i) {
46
46
  //#endregion
47
47
  export { a as n, o as t };
48
48
 
49
- //# sourceMappingURL=FontWeightSelect-BLQ_LMJF.js.map
49
+ //# sourceMappingURL=FontWeightSelect-BBuJGe_2.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FontWeightSelect-BLQ_LMJF.js","names":["Select","CSS_WEIGHTS","NAMES","fontWeightLabel","name","weight","FontWeightSelect","props","registered","family","listFontWeights","weights","value","mixed","options","a","b","w","onChange","key"],"sources":["../../src/components/FontWeightSelect/FontWeightSelect.tsx"],"sourcesContent":["import { listFontWeights } from '@weasel-js/font';\nimport { Select } from '../Select';\n\n/** Props for {@link FontWeightSelect}. */\nexport interface FontWeightSelectProps {\n /** Current weight, or `undefined` when the sources disagree (`mixed`). */\n value?: number;\n /** Indeterminate presentation — the aggregated sources differ in weight. */\n mixed?: boolean;\n /** The family whose weights to offer. Absent offers the CSS weights. */\n family?: string;\n onChange: (weight: number) => void;\n 'aria-label'?: string;\n className?: string;\n}\n\nconst CSS_WEIGHTS = [100, 200, 300, 400, 500, 600, 700, 800, 900] as const;\n\nconst NAMES: Readonly<Record<number, string>> = {\n 100: 'Thin',\n 200: 'Extra Light',\n 300: 'Light',\n 400: 'Regular',\n 500: 'Medium',\n 600: 'Semibold',\n 700: 'Bold',\n 800: 'Extra Bold',\n 900: 'Black',\n};\n\n/** A weight as the picker lists it: the number, then its common name. */\nexport function fontWeightLabel(weight: number): string {\n const name = NAMES[weight];\n return name ? `${weight} ${name}` : String(weight);\n}\n\n/**\n * Font-weight picker for one family, driven by the live font registry.\n *\n * It offers the weights `listFontWeights` reports for `family` — the faces\n * that will actually paint rather than be faked. A family with none on file\n * (the canvas tier, which rasterizes any weight it is asked for, or no family\n * at all because the selection spans several) gets the nine CSS weights. A\n * value outside the list is kept as its own entry, as `FontFamilySelect` keeps\n * an unregistered family: dropping it would rewrite the text on the next edit.\n */\nexport function FontWeightSelect(props: FontWeightSelectProps) {\n const { value, mixed = false, family, onChange, className } = props;\n const registered = family === undefined ? [] : listFontWeights(family);\n const weights = new Set<number>(registered.length > 0 ? registered : CSS_WEIGHTS);\n if (value !== undefined && !mixed) weights.add(value);\n const options = [...weights]\n .sort((a, b) => a - b)\n .map((w) => ({ value: String(w), label: fontWeightLabel(w) }));\n return (\n <Select<string>\n className={className}\n options={options}\n selectedKey={mixed || value === undefined ? null : String(value)}\n placeholder={mixed ? 'Mixed' : undefined}\n onSelectionChange={(key) => onChange(Number(key))}\n aria-label={props['aria-label'] ?? 'Weight'}\n />\n );\n}\n"],"mappings":";;;;AAgBA,IAAMC,IAAc;CAAC;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;AAAG,GAE1DC,IAA0C;CAC9C,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;AACP;AAGA,SAAgBC,EAAgB,GAAwB;CACtD,IAAMC,IAAOF,EAAMG;CACnB,OAAOD,IAAO,GAAGC,EAAO,GAAGD,MAAS,OAAOC,CAAM;AACnD;AAYA,SAAgBC,EAAiB,GAA8B;CAC7D,IAAM,EAAE,UAAO,WAAQ,IAAO,WAAQ,aAAU,iBAAcC,GACxDC,IAAaC,MAAW,KAAA,IAAY,CAAC,IAAIC,EAAgBD,CAAM,GAC/DE,IAAU,IAAI,IAAYH,EAAW,SAAS,IAAIA,IAAaP,CAAW;CAChF,AAAIW,MAAU,KAAA,KAAa,CAACC,KAAOF,EAAQ,IAAIC,CAAK;CACpD,IAAME,IAAU,CAAC,GAAGH,CAAO,CAAC,CACzB,MAAM,GAAG,MAAMI,IAAIC,CAAC,CAAC,CACrB,KAAK,OAAO;EAAE,OAAO,OAAOC,CAAC;EAAG,OAAOd,EAAgBc,CAAC;CAAE,EAAE;CAC/D,OACE,kBAACjB,GAAD;EACa;EACF;EACT,aAAaa,KAASD,MAAU,KAAA,IAAY,OAAO,OAAOA,CAAK;EAC/D,aAAaC,IAAQ,UAAU,KAAA;EAC/B,oBAAoB,MAAQK,EAAS,OAAOC,CAAG,CAAC;EAChD,cAAYZ,EAAM,iBAAiB;CACpC,CAAA;AAEL"}
1
+ {"version":3,"file":"FontWeightSelect-BBuJGe_2.js","names":["Select","CSS_WEIGHTS","NAMES","fontWeightLabel","name","weight","FontWeightSelect","props","registered","family","listFontWeights","weights","value","mixed","options","a","b","w","onChange","key"],"sources":["../../src/components/FontWeightSelect/FontWeightSelect.tsx"],"sourcesContent":["import { listFontWeights } from '@weasel-js/font';\nimport { Select } from '../Select';\n\n/** Props for {@link FontWeightSelect}. */\nexport interface FontWeightSelectProps {\n /** Current weight, or `undefined` when the sources disagree (`mixed`). */\n value?: number;\n /** Indeterminate presentation — the aggregated sources differ in weight. */\n mixed?: boolean;\n /** The family whose weights to offer. Absent offers the CSS weights. */\n family?: string;\n onChange: (weight: number) => void;\n 'aria-label'?: string;\n className?: string;\n}\n\nconst CSS_WEIGHTS = [100, 200, 300, 400, 500, 600, 700, 800, 900] as const;\n\nconst NAMES: Readonly<Record<number, string>> = {\n 100: 'Thin',\n 200: 'Extra Light',\n 300: 'Light',\n 400: 'Regular',\n 500: 'Medium',\n 600: 'Semibold',\n 700: 'Bold',\n 800: 'Extra Bold',\n 900: 'Black',\n};\n\n/** A weight as the picker lists it: the number, then its common name. */\nexport function fontWeightLabel(weight: number): string {\n const name = NAMES[weight];\n return name ? `${weight} ${name}` : String(weight);\n}\n\n/**\n * Font-weight picker for one family, driven by the live font registry.\n *\n * It offers the weights `listFontWeights` reports for `family` — the faces\n * that will actually paint rather than be faked. A family with none on file\n * (the canvas tier, which rasterizes any weight it is asked for, or no family\n * at all because the selection spans several) gets the nine CSS weights. A\n * value outside the list is kept as its own entry, as `FontFamilySelect` keeps\n * an unregistered family: dropping it would rewrite the text on the next edit.\n */\nexport function FontWeightSelect(props: FontWeightSelectProps) {\n const { value, mixed = false, family, onChange, className } = props;\n const registered = family === undefined ? [] : listFontWeights(family);\n const weights = new Set<number>(registered.length > 0 ? registered : CSS_WEIGHTS);\n if (value !== undefined && !mixed) weights.add(value);\n const options = [...weights]\n .sort((a, b) => a - b)\n .map((w) => ({ value: String(w), label: fontWeightLabel(w) }));\n return (\n <Select<string>\n className={className}\n options={options}\n selectedKey={mixed || value === undefined ? null : String(value)}\n placeholder={mixed ? 'Mixed' : undefined}\n onSelectionChange={(key) => onChange(Number(key))}\n aria-label={props['aria-label'] ?? 'Weight'}\n />\n );\n}\n"],"mappings":";;;;AAgBA,IAAMC,IAAc;CAAC;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;AAAG,GAE1DC,IAA0C;CAC9C,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;AACP;AAGA,SAAgBC,EAAgB,GAAwB;CACtD,IAAMC,IAAOF,EAAMG;CACnB,OAAOD,IAAO,GAAGC,EAAO,GAAGD,MAAS,OAAOC,CAAM;AACnD;AAYA,SAAgBC,EAAiB,GAA8B;CAC7D,IAAM,EAAE,UAAO,WAAQ,IAAO,WAAQ,aAAU,iBAAcC,GACxDC,IAAaC,MAAW,KAAA,IAAY,CAAC,IAAIC,EAAgBD,CAAM,GAC/DE,IAAU,IAAI,IAAYH,EAAW,SAAS,IAAIA,IAAaP,CAAW;CAChF,AAAIW,MAAU,KAAA,KAAa,CAACC,KAAOF,EAAQ,IAAIC,CAAK;CACpD,IAAME,IAAU,CAAC,GAAGH,CAAO,CAAC,CACzB,MAAM,GAAG,MAAMI,IAAIC,CAAC,CAAC,CACrB,KAAK,OAAO;EAAE,OAAO,OAAOC,CAAC;EAAG,OAAOd,EAAgBc,CAAC;CAAE,EAAE;CAC/D,OACE,kBAACjB,GAAD;EACa;EACF;EACT,aAAaa,KAASD,MAAU,KAAA,IAAY,OAAO,OAAOA,CAAK;EAC/D,aAAaC,IAAQ,UAAU,KAAA;EAC/B,oBAAoB,MAAQK,EAAS,OAAOC,CAAG,CAAC;EAChD,cAAYZ,EAAM,iBAAiB;CACpC,CAAA;AAEL"}
@@ -1,5 +1,5 @@
1
- import { o as e, r as t, s as n } from "./Keycaps-C-FJyG8k.js";
2
- import { t as r } from "./Powerline-6wIFmDik.js";
1
+ import { c as e, i as t, s as n } from "./Keycaps-CM-gOj0e.js";
2
+ import { t as r } from "./Powerline-CogJv9VU.js";
3
3
  import { Fragment as i, jsx as a, jsxs as o } from "react/jsx-runtime";
4
4
  import { getGestureDescriptor as s, parseRoute as c } from "@weasel-js/core/routing";
5
5
  var l = {
@@ -12,19 +12,19 @@ var l = {
12
12
  "ctrl",
13
13
  "meta"
14
14
  ];
15
- function d(e) {
16
- let t = [];
17
- for (let n of u) {
18
- let r = e[n];
19
- r !== void 0 && t.push({
20
- name: n,
15
+ function d(t) {
16
+ let n = [];
17
+ for (let e of u) {
18
+ let r = t[e];
19
+ r !== void 0 && n.push({
20
+ name: e,
21
21
  optional: r === "optional"
22
22
  });
23
23
  }
24
- if (t.length !== 0) return n(t);
24
+ if (n.length !== 0) return e(n);
25
25
  }
26
- function f(n) {
27
- let r = c(n), u = s(r.gesture), f = !!u.arg && r.arg !== void 0, p = f && u.arg?.name === "key", m = f && u.arg?.default !== void 0 && r.arg === u.arg.default, h = u.hasTarget && r.target !== void 0, g = h && r.target === "*", _ = d(r.modifiers), v = [];
26
+ function f(e) {
27
+ let r = c(e), u = s(r.gesture), f = !!u.arg && r.arg !== void 0, p = f && u.arg?.name === "key", m = f && u.arg?.default !== void 0 && r.arg === u.arg.default, h = u.hasTarget && r.target !== void 0, g = h && r.target === "*", _ = d(r.modifiers), v = [];
28
28
  for (let e of r.phases) e.channel !== "&" && v.push({
29
29
  text: e.channel,
30
30
  status: "neutral",
@@ -39,7 +39,7 @@ function f(n) {
39
39
  r.gesture,
40
40
  " ",
41
41
  /* @__PURE__ */ a(t, {
42
- label: e(r.arg).label,
42
+ label: n(r.arg).label,
43
43
  variant: "minimal",
44
44
  className: l.keyCap
45
45
  })
@@ -79,4 +79,4 @@ function p({ route: e, "aria-label": t, ...n }) {
79
79
  //#endregion
80
80
  export { f as n, p as t };
81
81
 
82
- //# sourceMappingURL=GestureRoute-BXESrHI0.js.map
82
+ //# sourceMappingURL=GestureRoute-BqHUrGeQ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"GestureRoute-BXESrHI0.js","names":["_Fragment","s","keySpecFromKey","KeyCap","keySpecsFromMods","Powerline","GestureRoute_module_default","MOD_DISPLAY_ORDER","modifierKeys","specs","name","req","modifiers","gestureRouteSegments","parsed","parseRoute","route","desc","getGestureDescriptor","hasArg","argIsKey","argIsDefault","hasTarget","targetIsWildcard","modKeys","segments","p","Fragment","text","k","i","GestureRoute","ariaLabel","rest"],"sources":["../../src/components/GestureRoute/GestureRoute.module.css","../../src/components/GestureRoute/GestureRoute.tsx"],"sourcesContent":[".muted { opacity: 0.55; }\n/* A gesture segment baseline-aligns its text; a KeyCap inside it has its own box and sits off-center without this. */\n.keyCap { align-self: center; }\n","import {\n getGestureDescriptor,\n parseRoute,\n type GestureName,\n type ModifierKey,\n type ParsedModifiers,\n} from '@weasel-js/core/routing';\nimport { Powerline, type PowerlineProps, type PowerlineSegment } from '../Powerline';\nimport { KeyCap, keySpecFromKey, keySpecsFromMods, type KeySpec, type LogicalModSpec } from '../Keycaps';\nimport s from './GestureRoute.module.css';\n\nconst MOD_DISPLAY_ORDER: readonly ModifierKey[] = ['mod', 'shift', 'alt', 'ctrl', 'meta'];\n\nfunction modifierKeys(modifiers: ParsedModifiers): readonly KeySpec[] | undefined {\n const specs: LogicalModSpec[] = [];\n for (const name of MOD_DISPLAY_ORDER) {\n const req = modifiers[name];\n if (req !== undefined) specs.push({ name, optional: req === 'optional' });\n }\n if (specs.length === 0) return undefined;\n return keySpecsFromMods(specs);\n}\n\n/**\n * The segments {@link GestureRoute} draws for a route string: phase atoms, then\n * `gesture(arg)`, then the held modifiers as one chord, then the target.\n * A descriptor's default arg and a wildcard target stay visible, muted, so\n * the strip shows the whole binding.\n */\nexport function gestureRouteSegments(route: string): PowerlineSegment[] {\n const parsed = parseRoute(route);\n const desc = getGestureDescriptor(parsed.gesture as GestureName);\n const hasArg = !!desc.arg && parsed.arg !== undefined;\n const argIsKey = hasArg && desc.arg?.name === 'key';\n const argIsDefault = hasArg && desc.arg?.default !== undefined && parsed.arg === desc.arg.default;\n const hasTarget = desc.hasTarget && parsed.target !== undefined;\n const targetIsWildcard = hasTarget && parsed.target === '*';\n const modKeys = modifierKeys(parsed.modifiers);\n\n const segments: PowerlineSegment[] = [];\n for (const p of parsed.phases) {\n if (p.channel !== '&') segments.push({ text: p.channel, status: 'neutral', variant: 'subtle' });\n segments.push({ text: p.phase, status: 'accent', variant: 'subtle' });\n }\n segments.push({\n text: hasArg ? (\n argIsKey ? (\n <>\n {parsed.gesture}{' '}\n <KeyCap label={keySpecFromKey(parsed.arg!).label} variant=\"minimal\" className={s.keyCap} />\n </>\n ) : (\n <>\n {parsed.gesture}\n <span className={argIsDefault ? s.muted : undefined}>({parsed.arg})</span>\n </>\n )\n ) : parsed.gesture,\n status: 'info',\n variant: 'outline',\n });\n if (modKeys) {\n // `+` marks a required modifier and `?` an optional one, so `+mod ?shift` reads `+⌘?⇧`.\n const text = modKeys.map((k) => `${k.optional ? '?' : '+'}${k.label}`).join('');\n segments.push({ text, status: 'muted', variant: 'solid' });\n }\n if (hasTarget) {\n segments.push({\n text: parsed.target,\n status: 'muted',\n variant: targetIsWildcard ? 'subtle' : 'outline',\n });\n }\n for (let i = 0; i < segments.length - 1; i++) segments[i].endCap = 'chevron';\n return segments;\n}\n\n/** Props for {@link GestureRoute}. */\nexport interface GestureRouteProps extends Omit<PowerlineProps, 'segments'> {\n /** A route in the dispatcher's grammar, e.g. `[initial] drag => node +shift`. */\n route: string;\n}\n\n/**\n * A dispatcher route drawn as a {@link Powerline}: phase, gesture, modifiers\n * and target as one chevron-linked chain.\n */\nexport function GestureRoute({ route, 'aria-label': ariaLabel, ...rest }: GestureRouteProps) {\n return <Powerline {...rest} segments={gestureRouteSegments(route)} aria-label={ariaLabel ?? route} />;\n}\n"],"mappings":";;;;;;;GCWMO,IAA4C;CAAC;CAAO;CAAS;CAAO;CAAQ;AAAM;AAExF,SAASC,EAAa,GAA4D;CAChF,IAAMC,IAA0B,CAAC;CACjC,KAAK,IAAMC,KAAQH,GAAmB;EACpC,IAAMI,IAAMC,EAAUF;EACtB,AAAIC,MAAQ,KAAA,KAAWF,EAAM,KAAK;GAAE;GAAM,UAAUE,MAAQ;EAAW,CAAC;CAC1E;CACA,IAAIF,EAAM,WAAW,GACrB,OAAOL,EAAiBK,CAAK;AAC/B;AAQA,SAAgBI,EAAqB,GAAmC;CACtE,IAAMC,IAASC,EAAWC,CAAK,GACzBC,IAAOC,EAAqBJ,EAAO,OAAsB,GACzDK,IAAS,CAAC,CAACF,EAAK,OAAOH,EAAO,QAAQ,KAAA,GACtCM,IAAWD,KAAUF,EAAK,KAAK,SAAS,OACxCI,IAAeF,KAAUF,EAAK,KAAK,YAAY,KAAA,KAAaH,EAAO,QAAQG,EAAK,IAAI,SACpFK,IAAYL,EAAK,aAAaH,EAAO,WAAW,KAAA,GAChDS,IAAmBD,KAAaR,EAAO,WAAW,KAClDU,IAAUhB,EAAaM,EAAO,SAAS,GAEvCW,IAA+B,CAAC;CACtC,KAAK,IAAMC,KAAKZ,EAAO,QAErB,AADIY,EAAE,YAAY,OAAKD,EAAS,KAAK;EAAE,MAAMC,EAAE;EAAS,QAAQ;EAAW,SAAS;CAAS,CAAC,GAC9FD,EAAS,KAAK;EAAE,MAAMC,EAAE;EAAO,QAAQ;EAAU,SAAS;CAAS,CAAC;CAmBtE,IAjBAD,EAAS,KAAK;EACZ,MAAMN,IACJC,IACE,kBAAApB,GAAA,EAAA,UAAA;GACGc,EAAO;GAAS;GACjB,kBAACX,GAAD;IAAQ,OAAOD,EAAeY,EAAO,GAAI,CAAC,CAAC;IAAO,SAAQ;IAAU,WAAWb,EAAE;GAAS,CAAA;EAC1F,EAAA,CAAA,IAEF,kBAAAD,GAAA,EAAA,UAAA,CACGc,EAAO,SACR,kBAAC,QAAD;GAAM,WAAWO,IAAepB,EAAE,QAAQ,KAAA;GAA1C,UAAA;IAAqD;IAAEa,EAAO;IAAI;GAAO;EACzE,CAAA,CAAA,EAAA,CAAA,IAEFA,EAAO;EACX,QAAQ;EACR,SAAS;CACX,CAAC,GACGU,GAAS;EAEX,IAAMI,IAAOJ,EAAQ,KAAK,MAAM,GAAGK,EAAE,WAAW,MAAM,MAAMA,EAAE,OAAO,CAAC,CAAC,KAAK,EAAE;EAC9E,EAAS,KAAK;GAAE;GAAM,QAAQ;GAAS,SAAS;EAAQ,CAAC;CAC3D;CACA,AAAIP,KACFG,EAAS,KAAK;EACZ,MAAMX,EAAO;EACb,QAAQ;EACR,SAASS,IAAmB,WAAW;CACzC,CAAC;CAEH,KAAK,IAAIO,IAAI,GAAGA,IAAIL,EAAS,SAAS,GAAG,KAAK,EAASK,EAAE,CAAC,SAAS;CACnE,OAAOL;AACT;AAYA,SAAgBM,EAAa,EAAE,UAAO,cAAcC,GAAW,GAAGC,KAA2B;CAC3F,OAAO,kBAAC5B,GAAD;EAAW,GAAI4B;EAAM,UAAUpB,EAAqBG,CAAK;EAAG,cAAYgB,KAAahB;CAAQ,CAAA;AACtG"}
1
+ {"version":3,"file":"GestureRoute-BqHUrGeQ.js","names":["_Fragment","s","keySpecsFromMods","KeyCap","keySpecFromKey","Powerline","GestureRoute_module_default","MOD_DISPLAY_ORDER","modifierKeys","specs","name","req","modifiers","gestureRouteSegments","parsed","parseRoute","route","desc","getGestureDescriptor","hasArg","argIsKey","argIsDefault","hasTarget","targetIsWildcard","modKeys","segments","p","Fragment","text","k","i","GestureRoute","ariaLabel","rest"],"sources":["../../src/components/GestureRoute/GestureRoute.module.css","../../src/components/GestureRoute/GestureRoute.tsx"],"sourcesContent":[".muted { opacity: 0.55; }\n/* A gesture segment baseline-aligns its text; a KeyCap inside it has its own box and sits off-center without this. */\n.keyCap { align-self: center; }\n","import {\n getGestureDescriptor,\n parseRoute,\n type GestureName,\n type ModifierKey,\n type ParsedModifiers,\n} from '@weasel-js/core/routing';\nimport { Powerline, type PowerlineProps, type PowerlineSegment } from '../Powerline';\nimport { KeyCap, keySpecFromKey, keySpecsFromMods, type KeySpec, type LogicalModSpec } from '../Keycaps';\nimport s from './GestureRoute.module.css';\n\nconst MOD_DISPLAY_ORDER: readonly ModifierKey[] = ['mod', 'shift', 'alt', 'ctrl', 'meta'];\n\nfunction modifierKeys(modifiers: ParsedModifiers): readonly KeySpec[] | undefined {\n const specs: LogicalModSpec[] = [];\n for (const name of MOD_DISPLAY_ORDER) {\n const req = modifiers[name];\n if (req !== undefined) specs.push({ name, optional: req === 'optional' });\n }\n if (specs.length === 0) return undefined;\n return keySpecsFromMods(specs);\n}\n\n/**\n * The segments {@link GestureRoute} draws for a route string: phase atoms, then\n * `gesture(arg)`, then the held modifiers as one chord, then the target.\n * A descriptor's default arg and a wildcard target stay visible, muted, so\n * the strip shows the whole binding.\n */\nexport function gestureRouteSegments(route: string): PowerlineSegment[] {\n const parsed = parseRoute(route);\n const desc = getGestureDescriptor(parsed.gesture as GestureName);\n const hasArg = !!desc.arg && parsed.arg !== undefined;\n const argIsKey = hasArg && desc.arg?.name === 'key';\n const argIsDefault = hasArg && desc.arg?.default !== undefined && parsed.arg === desc.arg.default;\n const hasTarget = desc.hasTarget && parsed.target !== undefined;\n const targetIsWildcard = hasTarget && parsed.target === '*';\n const modKeys = modifierKeys(parsed.modifiers);\n\n const segments: PowerlineSegment[] = [];\n for (const p of parsed.phases) {\n if (p.channel !== '&') segments.push({ text: p.channel, status: 'neutral', variant: 'subtle' });\n segments.push({ text: p.phase, status: 'accent', variant: 'subtle' });\n }\n segments.push({\n text: hasArg ? (\n argIsKey ? (\n <>\n {parsed.gesture}{' '}\n <KeyCap label={keySpecFromKey(parsed.arg!).label} variant=\"minimal\" className={s.keyCap} />\n </>\n ) : (\n <>\n {parsed.gesture}\n <span className={argIsDefault ? s.muted : undefined}>({parsed.arg})</span>\n </>\n )\n ) : parsed.gesture,\n status: 'info',\n variant: 'outline',\n });\n if (modKeys) {\n // `+` marks a required modifier and `?` an optional one, so `+mod ?shift` reads `+⌘?⇧`.\n const text = modKeys.map((k) => `${k.optional ? '?' : '+'}${k.label}`).join('');\n segments.push({ text, status: 'muted', variant: 'solid' });\n }\n if (hasTarget) {\n segments.push({\n text: parsed.target,\n status: 'muted',\n variant: targetIsWildcard ? 'subtle' : 'outline',\n });\n }\n for (let i = 0; i < segments.length - 1; i++) segments[i].endCap = 'chevron';\n return segments;\n}\n\n/** Props for {@link GestureRoute}. */\nexport interface GestureRouteProps extends Omit<PowerlineProps, 'segments'> {\n /** A route in the dispatcher's grammar, e.g. `[initial] drag => node +shift`. */\n route: string;\n}\n\n/**\n * A dispatcher route drawn as a {@link Powerline}: phase, gesture, modifiers\n * and target as one chevron-linked chain.\n */\nexport function GestureRoute({ route, 'aria-label': ariaLabel, ...rest }: GestureRouteProps) {\n return <Powerline {...rest} segments={gestureRouteSegments(route)} aria-label={ariaLabel ?? route} />;\n}\n"],"mappings":";;;;;;;GCWMO,IAA4C;CAAC;CAAO;CAAS;CAAO;CAAQ;AAAM;AAExF,SAASC,EAAa,GAA4D;CAChF,IAAMC,IAA0B,CAAC;CACjC,KAAK,IAAMC,KAAQH,GAAmB;EACpC,IAAMI,IAAMC,EAAUF;EACtB,AAAIC,MAAQ,KAAA,KAAWF,EAAM,KAAK;GAAE;GAAM,UAAUE,MAAQ;EAAW,CAAC;CAC1E;CACA,IAAIF,EAAM,WAAW,GACrB,OAAOP,EAAiBO,CAAK;AAC/B;AAQA,SAAgBI,EAAqB,GAAmC;CACtE,IAAMC,IAASC,EAAWC,CAAK,GACzBC,IAAOC,EAAqBJ,EAAO,OAAsB,GACzDK,IAAS,CAAC,CAACF,EAAK,OAAOH,EAAO,QAAQ,KAAA,GACtCM,IAAWD,KAAUF,EAAK,KAAK,SAAS,OACxCI,IAAeF,KAAUF,EAAK,KAAK,YAAY,KAAA,KAAaH,EAAO,QAAQG,EAAK,IAAI,SACpFK,IAAYL,EAAK,aAAaH,EAAO,WAAW,KAAA,GAChDS,IAAmBD,KAAaR,EAAO,WAAW,KAClDU,IAAUhB,EAAaM,EAAO,SAAS,GAEvCW,IAA+B,CAAC;CACtC,KAAK,IAAMC,KAAKZ,EAAO,QAErB,AADIY,EAAE,YAAY,OAAKD,EAAS,KAAK;EAAE,MAAMC,EAAE;EAAS,QAAQ;EAAW,SAAS;CAAS,CAAC,GAC9FD,EAAS,KAAK;EAAE,MAAMC,EAAE;EAAO,QAAQ;EAAU,SAAS;CAAS,CAAC;CAmBtE,IAjBAD,EAAS,KAAK;EACZ,MAAMN,IACJC,IACE,kBAAApB,GAAA,EAAA,UAAA;GACGc,EAAO;GAAS;GACjB,kBAACX,GAAD;IAAQ,OAAOC,EAAeU,EAAO,GAAI,CAAC,CAAC;IAAO,SAAQ;IAAU,WAAWb,EAAE;GAAS,CAAA;EAC1F,EAAA,CAAA,IAEF,kBAAAD,GAAA,EAAA,UAAA,CACGc,EAAO,SACR,kBAAC,QAAD;GAAM,WAAWO,IAAepB,EAAE,QAAQ,KAAA;GAA1C,UAAA;IAAqD;IAAEa,EAAO;IAAI;GAAO;EACzE,CAAA,CAAA,EAAA,CAAA,IAEFA,EAAO;EACX,QAAQ;EACR,SAAS;CACX,CAAC,GACGU,GAAS;EAEX,IAAMI,IAAOJ,EAAQ,KAAK,MAAM,GAAGK,EAAE,WAAW,MAAM,MAAMA,EAAE,OAAO,CAAC,CAAC,KAAK,EAAE;EAC9E,EAAS,KAAK;GAAE;GAAM,QAAQ;GAAS,SAAS;EAAQ,CAAC;CAC3D;CACA,AAAIP,KACFG,EAAS,KAAK;EACZ,MAAMX,EAAO;EACb,QAAQ;EACR,SAASS,IAAmB,WAAW;CACzC,CAAC;CAEH,KAAK,IAAIO,IAAI,GAAGA,IAAIL,EAAS,SAAS,GAAG,KAAK,EAASK,EAAE,CAAC,SAAS;CACnE,OAAOL;AACT;AAYA,SAAgBM,EAAa,EAAE,UAAO,cAAcC,GAAW,GAAGC,KAA2B;CAC3F,OAAO,kBAAC5B,GAAD;EAAW,GAAI4B;EAAM,UAAUpB,EAAqBG,CAAK;EAAG,cAAYgB,KAAahB;CAAQ,CAAA;AACtG"}
@@ -1,7 +1,7 @@
1
1
  import { t as e } from "./ColorField-BwlelY26.js";
2
2
  import { t } from "./Slider-CHBQ8uUC.js";
3
- import { t as n } from "./ToggleBar-BZ4Gk1IU.js";
4
- import { a as r, i, n as a, o, r as s, s as c, t as l } from "./MeshEditor-8CRsu-Td.js";
3
+ import { t as n } from "./ToggleBar-CfYbMK3x.js";
4
+ import { a as r, i, n as a, o, r as s, s as c, t as l } from "./MeshEditor-rhf7cXfG.js";
5
5
  import { fillInPoseFrame as u, fillToBoundsFrame as d, gradientGeometry as f, isGradientFill as p, sampleGradientStops as m, switchGradientKind as h, useGradientKinds as g, useNodeOverlayFrame as _, useOngoingAction as v, usePaintKind as y } from "@weasel-js/core";
6
6
  import { Fragment as b, jsx as x, jsxs as S } from "react/jsx-runtime";
7
7
  import "react";
@@ -322,4 +322,4 @@ function R(e, t, n) {
322
322
  //#endregion
323
323
  export { D as i, N as n, j as r, L as t };
324
324
 
325
- //# sourceMappingURL=GradientEditor-vz771M-N.js.map
325
+ //# sourceMappingURL=GradientEditor-DX4sb1Hf.js.map