@weasel-js/ui 1.2.0 → 1.3.0-pre.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/dist/chunks/{ActionBar-BvPEd_GW.js → ActionBar-BTev_ZZl.js} +21 -21
  2. package/dist/chunks/ActionBar-BTev_ZZl.js.map +1 -0
  3. package/dist/chunks/{ActionsBar-9aR5N3lU.js → ActionsBar-gTuCtkv8.js} +4 -4
  4. package/dist/chunks/{ActionsBar-9aR5N3lU.js.map → ActionsBar-gTuCtkv8.js.map} +1 -1
  5. package/dist/chunks/{Badge-dQzbkk-C.js → Badge-9dPfvYOk.js} +672 -670
  6. package/dist/chunks/Badge-9dPfvYOk.js.map +1 -0
  7. package/dist/chunks/{BandEditor-D3i6l299.js → BandEditor-DKbcHG2_.js} +46 -46
  8. package/dist/chunks/BandEditor-DKbcHG2_.js.map +1 -0
  9. package/dist/chunks/{Button-9QNIYugK.js → Button-CIu40Epu.js} +28 -28
  10. package/dist/chunks/Button-CIu40Epu.js.map +1 -0
  11. package/dist/chunks/{Callout-cJHJ4kIN.js → Callout-CHs98djt.js} +35 -35
  12. package/dist/chunks/Callout-CHs98djt.js.map +1 -0
  13. package/dist/chunks/Checkbox-BLEw-wEb.js +47 -0
  14. package/dist/chunks/Checkbox-BLEw-wEb.js.map +1 -0
  15. package/dist/chunks/ColorField-D1t-BdSa.js +60 -0
  16. package/dist/chunks/ColorField-D1t-BdSa.js.map +1 -0
  17. package/dist/chunks/{ComboBox-ONQISwtI.js → ComboBox-cvdKolWv.js} +28 -28
  18. package/dist/chunks/ComboBox-cvdKolWv.js.map +1 -0
  19. package/dist/chunks/{CurveEditor-SmwwvrsA.js → CurveEditor-BaA-PabB.js} +116 -116
  20. package/dist/chunks/{CurveEditor-SmwwvrsA.js.map → CurveEditor-BaA-PabB.js.map} +1 -1
  21. package/dist/chunks/DataGrid-D15I779H.js +210 -0
  22. package/dist/chunks/DataGrid-D15I779H.js.map +1 -0
  23. package/dist/chunks/Dialog-DC1x46Sq.js +59 -0
  24. package/dist/chunks/Dialog-DC1x46Sq.js.map +1 -0
  25. package/dist/chunks/{Field-VQwmrIna.js → Field-BjFl_yng.js} +6 -6
  26. package/dist/chunks/Field-BjFl_yng.js.map +1 -0
  27. package/dist/chunks/GradientEditor-BJRD0n1g.js +308 -0
  28. package/dist/chunks/GradientEditor-BJRD0n1g.js.map +1 -0
  29. package/dist/chunks/InlineRange-C6UYKxLD.js +24 -0
  30. package/dist/chunks/InlineRange-C6UYKxLD.js.map +1 -0
  31. package/dist/chunks/Input-DhWBgpRx.js +55 -0
  32. package/dist/chunks/Input-DhWBgpRx.js.map +1 -0
  33. package/dist/chunks/{Keycaps-CFX7UAj8.js → Keycaps-BhCKdyws.js} +15 -15
  34. package/dist/chunks/Keycaps-BhCKdyws.js.map +1 -0
  35. package/dist/chunks/NumberField-ZdVa4bpJ.js +59 -0
  36. package/dist/chunks/NumberField-ZdVa4bpJ.js.map +1 -0
  37. package/dist/chunks/{OptionsBar-CHvH4n4u.js → OptionsBar-DahCI4Aq.js} +4 -4
  38. package/dist/chunks/{OptionsBar-CHvH4n4u.js.map → OptionsBar-DahCI4Aq.js.map} +1 -1
  39. package/dist/chunks/PaintInput-Cggm9OIl.js +205 -0
  40. package/dist/chunks/PaintInput-Cggm9OIl.js.map +1 -0
  41. package/dist/chunks/PatternPicker-B63SlDoF.js +93 -0
  42. package/dist/chunks/PatternPicker-B63SlDoF.js.map +1 -0
  43. package/dist/chunks/{Plot2D-CEnhzodm.js → Plot2D-sPXTw_-l.js} +27 -27
  44. package/dist/chunks/{Plot2D-CEnhzodm.js.map → Plot2D-sPXTw_-l.js.map} +1 -1
  45. package/dist/chunks/{PointPlotter-BUIse9c4.js → PointPlotter-CLd9u5Bq.js} +2 -2
  46. package/dist/chunks/{PointPlotter-BUIse9c4.js.map → PointPlotter-CLd9u5Bq.js.map} +1 -1
  47. package/dist/chunks/{Powerline-BYWdK8QH.js → Powerline-DhQJYaUg.js} +2 -2
  48. package/dist/chunks/{Powerline-BYWdK8QH.js.map → Powerline-DhQJYaUg.js.map} +1 -1
  49. package/dist/chunks/Prefs-BSJ0lMmJ.js +300 -0
  50. package/dist/chunks/Prefs-BSJ0lMmJ.js.map +1 -0
  51. package/dist/chunks/RadioGroup-vtwjRiUm.js +49 -0
  52. package/dist/chunks/RadioGroup-vtwjRiUm.js.map +1 -0
  53. package/dist/chunks/RangeSlider-B-n_7AgJ.js +52 -0
  54. package/dist/chunks/RangeSlider-B-n_7AgJ.js.map +1 -0
  55. package/dist/chunks/{ResizeHandle-BOpmtOOj.js → ResizeHandle--ZExnMfa.js} +8 -8
  56. package/dist/chunks/{ResizeHandle-BOpmtOOj.js.map → ResizeHandle--ZExnMfa.js.map} +1 -1
  57. package/dist/chunks/{Select-CHsuQCp0.js → Select--BVTi-j0.js} +32 -32
  58. package/dist/chunks/Select--BVTi-j0.js.map +1 -0
  59. package/dist/chunks/SelectionPanel-C7kygTFE.js +446 -0
  60. package/dist/chunks/SelectionPanel-C7kygTFE.js.map +1 -0
  61. package/dist/chunks/{Sidebar-DP_nyJze.js → Sidebar-CtwBdr4n.js} +1 -1
  62. package/dist/chunks/{Sidebar-DP_nyJze.js.map → Sidebar-CtwBdr4n.js.map} +1 -1
  63. package/dist/chunks/SidebarPanel-AeSc7wNR.js +59 -0
  64. package/dist/chunks/SidebarPanel-AeSc7wNR.js.map +1 -0
  65. package/dist/chunks/{Slider-tGbqoAZH.js → Slider-cxWR47gi.js} +39 -39
  66. package/dist/chunks/Slider-cxWR47gi.js.map +1 -0
  67. package/dist/chunks/{StatusBar-DBO0E0Yd.js → StatusBar-D_wu-SZh.js} +6 -6
  68. package/dist/chunks/StatusBar-D_wu-SZh.js.map +1 -0
  69. package/dist/chunks/Switch-CtZwp5zm.js +27 -0
  70. package/dist/chunks/Switch-CtZwp5zm.js.map +1 -0
  71. package/dist/chunks/Tabs-lB0IB4TX.js +41 -0
  72. package/dist/chunks/Tabs-lB0IB4TX.js.map +1 -0
  73. package/dist/chunks/{Toast-DFw4IrD_.js → Toast-hr9BYsho.js} +31 -31
  74. package/dist/chunks/{Toast-DFw4IrD_.js.map → Toast-hr9BYsho.js.map} +1 -1
  75. package/dist/chunks/{ToggleBar-BP1zFcvg.js → ToggleBar-BxgWq-wO.js} +11 -10
  76. package/dist/chunks/ToggleBar-BxgWq-wO.js.map +1 -0
  77. package/dist/chunks/{ToolButton-ClPz5L6j.js → ToolButton-CJrPjbsj.js} +16 -16
  78. package/dist/chunks/ToolButton-CJrPjbsj.js.map +1 -0
  79. package/dist/chunks/{ToolGroup-BTIwd4U4.js → ToolGroup-D_lDzkvU.js} +1 -1
  80. package/dist/chunks/{ToolGroup-BTIwd4U4.js.map → ToolGroup-D_lDzkvU.js.map} +1 -1
  81. package/dist/chunks/{ToolOptionsBar-JvmY7iNe.js → ToolOptionsBar-BA6Sb3U2.js} +4 -4
  82. package/dist/chunks/ToolOptionsBar-BA6Sb3U2.js.map +1 -0
  83. package/dist/chunks/{ToolPalette-DAiVhdtV.js → ToolPalette-TMvkaJwr.js} +24 -24
  84. package/dist/chunks/ToolPalette-TMvkaJwr.js.map +1 -0
  85. package/dist/chunks/Tooltip-Di7vxbFa.js +40 -0
  86. package/dist/chunks/Tooltip-Di7vxbFa.js.map +1 -0
  87. package/dist/chunks/{keyGlyph-BDH5fqkO.js → keyGlyph-DNpLxF8u.js} +1 -1
  88. package/dist/chunks/{keyGlyph-BDH5fqkO.js.map → keyGlyph-DNpLxF8u.js.map} +1 -1
  89. package/dist/chunks/segmentedControl.module-D4b7x19O.js +11 -0
  90. package/dist/chunks/segmentedControl.module-D4b7x19O.js.map +1 -0
  91. package/dist/chunks/{useRovingTabIndex-DBZtq4IH.js → useRovingTabIndex-CQz8EiKh.js} +1 -1
  92. package/dist/chunks/{useRovingTabIndex-DBZtq4IH.js.map → useRovingTabIndex-CQz8EiKh.js.map} +1 -1
  93. package/dist/components/ActionBar/index.js +1 -1
  94. package/dist/components/ActionsBar/index.js +1 -1
  95. package/dist/components/Badge/Badge.d.ts.map +1 -1
  96. package/dist/components/Badge/index.js +1 -1
  97. package/dist/components/BandEditor/index.js +1 -1
  98. package/dist/components/Button/index.js +1 -1
  99. package/dist/components/Callout/index.js +1 -1
  100. package/dist/components/Checkbox/Checkbox.d.ts +1 -1
  101. package/dist/components/Checkbox/index.js +1 -1
  102. package/dist/components/ColorField/ColorField.d.ts.map +1 -1
  103. package/dist/components/ColorField/index.js +1 -1
  104. package/dist/components/ComboBox/index.js +1 -1
  105. package/dist/components/CurveEditor/index.js +1 -1
  106. package/dist/components/DataGrid/index.js +1 -1
  107. package/dist/components/Dialog/index.js +1 -1
  108. package/dist/components/Field/index.js +1 -1
  109. package/dist/components/GradientEditor/SceneGradientHandles.d.ts +41 -0
  110. package/dist/components/GradientEditor/SceneGradientHandles.d.ts.map +1 -0
  111. package/dist/components/GradientEditor/index.d.ts +1 -0
  112. package/dist/components/GradientEditor/index.d.ts.map +1 -1
  113. package/dist/components/GradientEditor/index.js +2 -2
  114. package/dist/components/InlineRange/InlineRange.d.ts +17 -0
  115. package/dist/components/InlineRange/InlineRange.d.ts.map +1 -0
  116. package/dist/components/InlineRange/index.d.ts +2 -0
  117. package/dist/components/InlineRange/index.d.ts.map +1 -0
  118. package/dist/components/InlineRange/index.js +2 -0
  119. package/dist/components/Input/Input.d.ts +1 -1
  120. package/dist/components/Input/index.js +1 -1
  121. package/dist/components/Keycaps/index.js +2 -2
  122. package/dist/components/NumberField/NumberField.d.ts +1 -1
  123. package/dist/components/NumberField/index.js +1 -1
  124. package/dist/components/OptionsBar/index.js +1 -1
  125. package/dist/components/PaintInput/PaintInput.d.ts +40 -0
  126. package/dist/components/PaintInput/PaintInput.d.ts.map +1 -0
  127. package/dist/components/PaintInput/index.d.ts +2 -0
  128. package/dist/components/PaintInput/index.d.ts.map +1 -0
  129. package/dist/components/PaintInput/index.js +2 -0
  130. package/dist/components/PatternPicker/PatternPicker.d.ts +31 -0
  131. package/dist/components/PatternPicker/PatternPicker.d.ts.map +1 -0
  132. package/dist/components/PatternPicker/index.d.ts +2 -0
  133. package/dist/components/PatternPicker/index.d.ts.map +1 -0
  134. package/dist/components/PatternPicker/index.js +2 -0
  135. package/dist/components/Plot2D/index.js +1 -1
  136. package/dist/components/PointPlotter/index.js +1 -1
  137. package/dist/components/Powerline/index.js +2 -2
  138. package/dist/components/Prefs/PrefsForm.d.ts.map +1 -1
  139. package/dist/components/Prefs/index.d.ts +1 -1
  140. package/dist/components/Prefs/index.d.ts.map +1 -1
  141. package/dist/components/Prefs/index.js +1 -1
  142. package/dist/components/Prefs/schema.d.ts +4 -92
  143. package/dist/components/Prefs/schema.d.ts.map +1 -1
  144. package/dist/components/RadioGroup/index.js +1 -1
  145. package/dist/components/RangeSlider/index.js +1 -1
  146. package/dist/components/ResizeHandle/index.js +1 -1
  147. package/dist/components/Select/index.js +1 -1
  148. package/dist/components/SelectionPanel/SelectionPanel.d.ts +32 -1
  149. package/dist/components/SelectionPanel/SelectionPanel.d.ts.map +1 -1
  150. package/dist/components/SelectionPanel/index.js +1 -1
  151. package/dist/components/Sidebar/index.js +1 -1
  152. package/dist/components/SidebarPanel/index.js +1 -1
  153. package/dist/components/Slider/index.js +1 -1
  154. package/dist/components/StatusBar/index.js +1 -1
  155. package/dist/components/Switch/Switch.d.ts +1 -1
  156. package/dist/components/Switch/index.js +1 -1
  157. package/dist/components/Tabs/index.js +1 -1
  158. package/dist/components/Toast/index.js +1 -1
  159. package/dist/components/ToggleBar/ToggleBar.d.ts +1 -1
  160. package/dist/components/ToggleBar/ToggleBar.d.ts.map +1 -1
  161. package/dist/components/ToggleBar/index.js +1 -1
  162. package/dist/components/ToolButton/index.js +1 -1
  163. package/dist/components/ToolGroup/index.js +1 -1
  164. package/dist/components/ToolOptionsBar/index.js +1 -1
  165. package/dist/components/ToolPalette/index.js +1 -1
  166. package/dist/components/Tooltip/index.js +1 -1
  167. package/dist/components/paintValue.d.ts +8 -0
  168. package/dist/components/paintValue.d.ts.map +1 -0
  169. package/dist/icons/Icon.d.ts +17 -0
  170. package/dist/icons/Icon.d.ts.map +1 -0
  171. package/dist/icons/index.d.ts +50 -0
  172. package/dist/icons/index.d.ts.map +1 -0
  173. package/dist/icons/paths.d.ts +71 -0
  174. package/dist/icons/paths.d.ts.map +1 -0
  175. package/dist/index.d.ts +5 -0
  176. package/dist/index.d.ts.map +1 -1
  177. package/dist/index.js +184 -48
  178. package/dist/index.js.map +1 -1
  179. package/dist/style.css +1 -1
  180. package/package.json +4 -3
  181. package/dist/chunks/ActionBar-BvPEd_GW.js.map +0 -1
  182. package/dist/chunks/Badge-dQzbkk-C.js.map +0 -1
  183. package/dist/chunks/BandEditor-D3i6l299.js.map +0 -1
  184. package/dist/chunks/Button-9QNIYugK.js.map +0 -1
  185. package/dist/chunks/Callout-cJHJ4kIN.js.map +0 -1
  186. package/dist/chunks/Checkbox-KQ5L8APP.js +0 -47
  187. package/dist/chunks/Checkbox-KQ5L8APP.js.map +0 -1
  188. package/dist/chunks/ColorField-ykpiD3XU.js +0 -60
  189. package/dist/chunks/ColorField-ykpiD3XU.js.map +0 -1
  190. package/dist/chunks/ComboBox-ONQISwtI.js.map +0 -1
  191. package/dist/chunks/DataGrid-DZQ4Chbd.js +0 -210
  192. package/dist/chunks/DataGrid-DZQ4Chbd.js.map +0 -1
  193. package/dist/chunks/Dialog-CqwdJv_t.js +0 -59
  194. package/dist/chunks/Dialog-CqwdJv_t.js.map +0 -1
  195. package/dist/chunks/Field-VQwmrIna.js.map +0 -1
  196. package/dist/chunks/GradientEditor-Bg6SX64V.js +0 -280
  197. package/dist/chunks/GradientEditor-Bg6SX64V.js.map +0 -1
  198. package/dist/chunks/Input-DpkwYtUY.js +0 -55
  199. package/dist/chunks/Input-DpkwYtUY.js.map +0 -1
  200. package/dist/chunks/Keycaps-CFX7UAj8.js.map +0 -1
  201. package/dist/chunks/NumberField-BbzI0GtU.js +0 -59
  202. package/dist/chunks/NumberField-BbzI0GtU.js.map +0 -1
  203. package/dist/chunks/Prefs-BMR9wMiZ.js +0 -238
  204. package/dist/chunks/Prefs-BMR9wMiZ.js.map +0 -1
  205. package/dist/chunks/RadioGroup-CIpHr3g6.js +0 -49
  206. package/dist/chunks/RadioGroup-CIpHr3g6.js.map +0 -1
  207. package/dist/chunks/RangeSlider-CaDogGTj.js +0 -52
  208. package/dist/chunks/RangeSlider-CaDogGTj.js.map +0 -1
  209. package/dist/chunks/Select-CHsuQCp0.js.map +0 -1
  210. package/dist/chunks/SelectionPanel-D_wk_8ZN.js +0 -303
  211. package/dist/chunks/SelectionPanel-D_wk_8ZN.js.map +0 -1
  212. package/dist/chunks/SidebarPanel-BVTQ1ZyM.js +0 -59
  213. package/dist/chunks/SidebarPanel-BVTQ1ZyM.js.map +0 -1
  214. package/dist/chunks/Slider-tGbqoAZH.js.map +0 -1
  215. package/dist/chunks/StatusBar-DBO0E0Yd.js.map +0 -1
  216. package/dist/chunks/Switch-JyVSSAzC.js +0 -27
  217. package/dist/chunks/Switch-JyVSSAzC.js.map +0 -1
  218. package/dist/chunks/Tabs-Df0sDomx.js +0 -41
  219. package/dist/chunks/Tabs-Df0sDomx.js.map +0 -1
  220. package/dist/chunks/ToggleBar-BP1zFcvg.js.map +0 -1
  221. package/dist/chunks/ToolButton-ClPz5L6j.js.map +0 -1
  222. package/dist/chunks/ToolOptionsBar-JvmY7iNe.js.map +0 -1
  223. package/dist/chunks/ToolPalette-DAiVhdtV.js.map +0 -1
  224. package/dist/chunks/Tooltip-DXJbdR5B.js +0 -40
  225. package/dist/chunks/Tooltip-DXJbdR5B.js.map +0 -1
  226. package/dist/chunks/segmentedControl.module-DCyuDbj1.js +0 -11
  227. package/dist/chunks/segmentedControl.module-DCyuDbj1.js.map +0 -1
@@ -0,0 +1,446 @@
1
+ import { n as e, r as t, t as n } from "./PaintInput-Cggm9OIl.js";
2
+ import { t as r } from "./ToggleBar-BxgWq-wO.js";
3
+ import { t as i } from "./InlineRange-C6UYKxLD.js";
4
+ import { t as a } from "./ColorField-D1t-BdSa.js";
5
+ import { t as o } from "./Input-DhWBgpRx.js";
6
+ import { t as s } from "./NumberField-ZdVa4bpJ.js";
7
+ import { t as c } from "./Select--BVTi-j0.js";
8
+ import { t as l } from "./Switch-CtZwp5zm.js";
9
+ import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
10
+ import { MIXED as p, asNodeId as m, isBuiltinToolPref as h } from "@weasel-js/core";
11
+ import { Fragment as g, useState as _, useSyncExternalStore as v } from "react";
12
+ //#region src/components/SelectionPanel/model.ts
13
+ function y(e, t) {
14
+ if (e.kind === "container") return "group";
15
+ for (let n of t) if (n.matches(e.data)) return n.name;
16
+ return "unknown";
17
+ }
18
+ function b(e) {
19
+ return !("kind" in e);
20
+ }
21
+ function x(e) {
22
+ let t = [], n = [], r = (e, t) => {
23
+ for (let [n, i] of Object.entries(e.children)) b(i) ? r(i, t) : t.push({
24
+ path: n,
25
+ leaf: i
26
+ });
27
+ };
28
+ for (let [i, a] of Object.entries(e.children)) if (b(a)) {
29
+ let e = [];
30
+ r(a, e), t.push({
31
+ key: i,
32
+ name: a.name,
33
+ rows: S(e)
34
+ });
35
+ } else n.push({
36
+ path: i,
37
+ leaf: a
38
+ });
39
+ return n.length > 0 && t.unshift({
40
+ key: "",
41
+ name: "",
42
+ rows: S(n)
43
+ }), t;
44
+ }
45
+ function S(e) {
46
+ let t = [];
47
+ for (let n of e) {
48
+ let e = n.leaf.pair, r = t[t.length - 1];
49
+ e !== void 0 && r !== void 0 && r.label === e ? r.leaves.push(n) : t.push({
50
+ label: e ?? n.leaf.name,
51
+ leaves: [n]
52
+ });
53
+ }
54
+ return t;
55
+ }
56
+ function C(e, t) {
57
+ let n = [...new Set(e)];
58
+ if (n.length === 0) return [];
59
+ let r = new Map(t.map((e) => [e.name, e])), i = n.map((e) => r.get(e)?.schema);
60
+ if (i.some((e) => e === void 0)) return [];
61
+ let [a, ...o] = i, s = x(a);
62
+ if (o.length === 0) return s;
63
+ let c = (e) => `${e.path}\0${e.leaf.kind}`, l = o.map((e) => new Set(x(e).flatMap((e) => e.rows).flatMap((e) => e.leaves).map(c))), u = (e) => l.every((t) => t.has(c(e)));
64
+ return s.map((e) => ({
65
+ ...e,
66
+ rows: e.rows.map((e) => ({
67
+ ...e,
68
+ leaves: e.leaves.filter(u)
69
+ })).filter((e) => e.leaves.length > 0)
70
+ })).filter((e) => e.rows.length > 0);
71
+ }
72
+ function w(e, t) {
73
+ let n = t.split(".");
74
+ if (n.length < 2) return;
75
+ let r = n[0], i = r === "pose" ? e.pose : r === "data" ? e.data : void 0;
76
+ for (let e = 1; e < n.length; e++) {
77
+ if (typeof i != "object" || !i) return;
78
+ i = i[n[e]];
79
+ }
80
+ return i;
81
+ }
82
+ function T(e, t, n) {
83
+ let [r, ...i] = t;
84
+ if (i.length === 0) return Array.isArray(e) ? Object.assign([...e], { [r]: n }) : {
85
+ ...e,
86
+ [r]: n
87
+ };
88
+ let a = e[r], o = T(Array.isArray(a) ? [...a] : typeof a == "object" && a ? { ...a } : {}, i, n);
89
+ return Array.isArray(e) ? Object.assign([...e], { [r]: o }) : {
90
+ ...e,
91
+ [r]: o
92
+ };
93
+ }
94
+ function E(e, t) {
95
+ let n, r = !0;
96
+ for (let i of e) {
97
+ let e = w(i, t);
98
+ if (r) n = e, r = !1;
99
+ else if (!Object.is(e, n)) return p;
100
+ }
101
+ return n;
102
+ }
103
+ function D(e) {
104
+ let t = /* @__PURE__ */ new Map();
105
+ for (let n of e) t.set(n, (t.get(n) ?? 0) + 1);
106
+ return [...t.entries()].map(([e, t]) => t > 1 ? `${e} ×${t}` : e).join(" · ");
107
+ }
108
+ var O = {
109
+ root: "_root_1c7nn_1",
110
+ header: "_header_1c7nn_8",
111
+ kind: "_kind_1c7nn_16",
112
+ breakdown: "_breakdown_1c7nn_21",
113
+ section: "_section_1c7nn_26",
114
+ sectionTitle: "_sectionTitle_1c7nn_35",
115
+ row: "_row_1c7nn_44",
116
+ rowLabel: "_rowLabel_1c7nn_52",
117
+ rowControls: "_rowControls_1c7nn_56",
118
+ namedField: "_namedField_1c7nn_66",
119
+ blockRow: "_blockRow_1c7nn_78",
120
+ toggle: "_toggle_1c7nn_89",
121
+ number: "_number_1c7nn_109",
122
+ select: "_select_1c7nn_110",
123
+ unitSuffix: "_unitSuffix_1c7nn_114",
124
+ mixedSwitch: "_mixedSwitch_1c7nn_120",
125
+ unrenderable: "_unrenderable_1c7nn_124",
126
+ objectLeaf: "_objectLeaf_1c7nn_131",
127
+ objectGroup: "_objectGroup_1c7nn_140",
128
+ slider: "_slider_1c7nn_147",
129
+ sliderReadout: "_sliderReadout_1c7nn_151"
130
+ }, k = (e) => e.length === 0 ? e : e[0].toUpperCase() + e.slice(1);
131
+ function A(e) {
132
+ let { scene: t, selection: n, properties: r, routing: i, renderers: a, kindLabel: o = k, emptyState: s = null, className: c } = e;
133
+ v(t.subscribe, t.getVersion, t.getVersion);
134
+ let l = n.current.map((e) => t.get(m(e))).filter((e) => e != null), p = l.map((e) => y(e, i)), h = l.map((e) => e.id).join(","), _ = C(p, r);
135
+ if (l.length === 0) return /* @__PURE__ */ d("div", {
136
+ className: [O.root, c].filter(Boolean).join(" "),
137
+ children: s
138
+ });
139
+ let b = (e, r) => {
140
+ let [i, ...a] = e.path.split(".");
141
+ if (a.length === 0) return;
142
+ let o = n.current.map(m);
143
+ t.batch(`Edit ${e.leaf.name}`, () => {
144
+ for (let e of o) {
145
+ let n = t.get(e);
146
+ n && (i === "pose" ? t.setPose(e, T(n.pose, a, r)) : i === "data" && t.update(e, { data: T(n.data, a, r) }));
147
+ }
148
+ });
149
+ };
150
+ return /* @__PURE__ */ f("div", {
151
+ className: [O.root, c].filter(Boolean).join(" "),
152
+ children: [/* @__PURE__ */ d("header", {
153
+ className: O.header,
154
+ children: l.length === 1 ? /* @__PURE__ */ d("span", {
155
+ className: O.kind,
156
+ children: o(p[0])
157
+ }) : /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ f("span", {
158
+ className: O.kind,
159
+ children: [l.length, " selected"]
160
+ }), /* @__PURE__ */ d("span", {
161
+ className: O.breakdown,
162
+ children: D(p)
163
+ })] })
164
+ }), _.map((e) => {
165
+ let t = e.rows.map((e) => ({
166
+ row: e,
167
+ controls: e.leaves.map((t) => ({
168
+ panelLeaf: t,
169
+ content: j(t, e.leaves.length > 1 ? `${e.label} ${t.leaf.name}` : t.leaf.name, l, a, b, h)
170
+ })).filter((e) => e.content != null)
171
+ })).filter(({ controls: e }) => e.length > 0);
172
+ return t.length === 0 ? null : /* @__PURE__ */ f("section", {
173
+ className: O.section,
174
+ children: [e.name !== "" && /* @__PURE__ */ d("h3", {
175
+ className: O.sectionTitle,
176
+ children: e.name
177
+ }), t.map(({ row: t, controls: n }) => t.leaves.length === 1 && t.leaves[0].leaf.block ? /* @__PURE__ */ f("div", { children: [e.name === "" && t.leaves[0].leaf.kind !== "object" && /* @__PURE__ */ d("h4", {
178
+ className: O.sectionTitle,
179
+ title: t.leaves[0].leaf.description,
180
+ children: t.leaves[0].leaf.name
181
+ }), n[0].content] }, t.leaves[0].path) : /* @__PURE__ */ f("div", {
182
+ className: O.row,
183
+ children: [/* @__PURE__ */ d("span", {
184
+ className: O.rowLabel,
185
+ title: t.leaves[0].leaf.description,
186
+ children: t.label
187
+ }), /* @__PURE__ */ d("span", {
188
+ className: O.rowControls,
189
+ children: n.map(({ panelLeaf: e, content: t }) => /* @__PURE__ */ d(g, { children: t }, e.path))
190
+ })]
191
+ }, t.leaves[0].path))]
192
+ }, e.key);
193
+ })]
194
+ });
195
+ }
196
+ function j(e, t, n, r, i, a) {
197
+ let { path: o, leaf: s } = e, c = E(n, o), l = c === p, u = {
198
+ path: o,
199
+ pref: s,
200
+ value: l ? void 0 : c,
201
+ mixed: l,
202
+ unset: !l && c === void 0,
203
+ setValue: (t) => i(e, t),
204
+ valueAt: (e) => {
205
+ let t = E(n, e);
206
+ return t === p ? {
207
+ value: void 0,
208
+ mixed: !0
209
+ } : {
210
+ value: t,
211
+ mixed: !1
212
+ };
213
+ }
214
+ }, d = r?.[o] ?? r?.[s.kind];
215
+ return d ? d(u) : M(u, t, r, a);
216
+ }
217
+ function M(o, p, m, g = "", _ = !1) {
218
+ let { pref: v, value: y, mixed: b, unset: x, setValue: S } = o;
219
+ if (!h(v)) return /* @__PURE__ */ f("span", {
220
+ className: O.unrenderable,
221
+ children: [
222
+ "(",
223
+ v.kind,
224
+ ": no renderer)"
225
+ ]
226
+ });
227
+ switch (v.kind) {
228
+ case "number": {
229
+ let e = v, t = typeof y == "number" && Number.isFinite(y) ? y : void 0, n = t === void 0 ? NaN : e.unit ? e.unit.toDisplay(t) : t;
230
+ if (e.control === "slider") {
231
+ let r = e.min ?? 0, a = e.max ?? 100, o = !b && t !== void 0;
232
+ return /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d(i, {
233
+ min: r,
234
+ max: a,
235
+ step: e.step ?? 1,
236
+ value: o ? Math.min(Math.max(n, r), a) : r,
237
+ disabled: b,
238
+ className: O.slider,
239
+ "aria-label": p,
240
+ onChange: (t) => {
241
+ let n = Number(t.target.value);
242
+ S(e.unit ? e.unit.fromDisplay(n) : n);
243
+ }
244
+ }), /* @__PURE__ */ d("span", {
245
+ className: O.sliderReadout,
246
+ "aria-hidden": "true",
247
+ children: o ? `${n}${e.unit?.suffix ?? ""}` : "—"
248
+ })] });
249
+ }
250
+ let r = /* @__PURE__ */ d(s, {
251
+ className: O.number,
252
+ value: b || t === void 0 ? NaN : n,
253
+ placeholder: b ? "Mixed" : void 0,
254
+ minValue: e.min,
255
+ maxValue: e.max,
256
+ step: e.step ?? 1,
257
+ hideSteppers: !0,
258
+ "aria-label": p,
259
+ onChange: (t) => {
260
+ Number.isNaN(t) || S(e.unit ? e.unit.fromDisplay(t) : t);
261
+ }
262
+ });
263
+ return e.unit?.suffix === void 0 ? r : /* @__PURE__ */ f(u, { children: [r, /* @__PURE__ */ d("span", {
264
+ className: O.unitSuffix,
265
+ "aria-hidden": "true",
266
+ children: e.unit.suffix
267
+ })] });
268
+ }
269
+ case "string": return /* @__PURE__ */ d(P, {
270
+ text: b ? void 0 : typeof y == "string" ? y : "",
271
+ placeholder: b ? "Mixed" : void 0,
272
+ ariaLabel: p,
273
+ onCommit: S
274
+ });
275
+ case "boolean": {
276
+ let e = /* @__PURE__ */ d(l, {
277
+ isSelected: !!y,
278
+ onChange: S,
279
+ "aria-label": p
280
+ });
281
+ return !b && !x ? e : /* @__PURE__ */ d("span", {
282
+ className: O.mixedSwitch,
283
+ title: b ? "Mixed" : "Not set",
284
+ children: e
285
+ });
286
+ }
287
+ case "enum": {
288
+ let n = v, i = n.encoding ? b ? void 0 : n.encoding.read(y, o.siblings) : b || x ? void 0 : typeof y == "string" ? y : n.default, a = (e) => S(n.encoding ? n.encoding.write(e, o.siblings) : e);
289
+ return n.control === "toggle" ? /* @__PURE__ */ d(r, {
290
+ size: "sm",
291
+ variant: "flat",
292
+ className: O.toggle,
293
+ ariaLabel: p,
294
+ items: n.options.map((n) => ({
295
+ value: n.value,
296
+ label: n.icon && n.icon in t ? /* @__PURE__ */ d(e, {
297
+ name: n.icon,
298
+ size: 14
299
+ }) : n.short ?? n.label,
300
+ ariaLabel: n.label,
301
+ disabled: n.disabled
302
+ })),
303
+ value: i ?? null,
304
+ onChange: (e) => {
305
+ e !== null && a(e);
306
+ }
307
+ }) : /* @__PURE__ */ d(c, {
308
+ className: O.select,
309
+ options: n.options.map((e) => ({
310
+ value: e.value,
311
+ label: e.label,
312
+ isDisabled: e.disabled
313
+ })),
314
+ selectedKey: i ?? null,
315
+ placeholder: b ? "Mixed" : x ? "—" : void 0,
316
+ onSelectionChange: a,
317
+ "aria-label": p
318
+ });
319
+ }
320
+ case "color": {
321
+ let e = v;
322
+ return /* @__PURE__ */ d(a, {
323
+ value: b ? void 0 : typeof y == "string" ? y : e.default,
324
+ mixed: b,
325
+ alpha: e.alpha,
326
+ onChange: S,
327
+ "aria-label": p
328
+ });
329
+ }
330
+ case "paint": return /* @__PURE__ */ d(n, {
331
+ value: y === null ? null : y ?? (b ? void 0 : v.default),
332
+ mixed: b,
333
+ unset: x,
334
+ allowNone: !_,
335
+ onChange: S,
336
+ "aria-label": p
337
+ }, g);
338
+ case "object": return /* @__PURE__ */ d(N, {
339
+ ctx: o,
340
+ renderers: m,
341
+ selectionKey: g
342
+ });
343
+ default: throw Error(`SelectionPanel: no control for built-in pref kind "${v.kind}"`);
344
+ }
345
+ }
346
+ function N({ ctx: n, renderers: r, selectionKey: i = "" }) {
347
+ let a = n.pref, o = typeof n.value == "object" && n.value !== null ? n.value : void 0, s = Object.values(a.children).every((e) => !("kind" in e)), c = (s, l) => {
348
+ let u = [];
349
+ for (let [p, m] of Object.entries(s)) {
350
+ if (!("kind" in m)) {
351
+ let e = m.name !== "", t = c(m.children, e);
352
+ if (t.length === 0) continue;
353
+ u.push(/* @__PURE__ */ f("div", {
354
+ className: e && l ? O.objectGroup : void 0,
355
+ children: [e && /* @__PURE__ */ d("h5", {
356
+ className: O.sectionTitle,
357
+ children: m.name
358
+ }), t]
359
+ }, `group:${p}`));
360
+ continue;
361
+ }
362
+ let s = `${n.path}.${p}`, h = {
363
+ path: s,
364
+ pref: m,
365
+ value: o?.[p],
366
+ mixed: n.mixed,
367
+ unset: !n.mixed && o?.[p] === void 0,
368
+ siblings: o,
369
+ valueAt: n.valueAt,
370
+ setValue: (e) => {
371
+ let t = o ?? a.fromScalar?.(n.value) ?? {};
372
+ if (e === void 0) {
373
+ let { [p]: e, ...r } = t;
374
+ n.setValue(r);
375
+ return;
376
+ }
377
+ n.setValue({
378
+ ...t,
379
+ [p]: e
380
+ });
381
+ }
382
+ }, _ = r?.[s] ?? r?.[m.kind], v = _ ? _(h) : M(h, m.name, r, i, !0);
383
+ if (v == null) continue;
384
+ let y = (m.pair !== void 0 || m.block === !0) && m.icon && m.icon in t ? /* @__PURE__ */ f("span", {
385
+ className: O.namedField,
386
+ children: [/* @__PURE__ */ d(e, {
387
+ name: m.icon,
388
+ size: 14
389
+ }), v]
390
+ }) : v, b = u[u.length - 1];
391
+ if (m.pair !== void 0 && ((e) => typeof e == "object" && !!e && "controls" in e)(b) && b.pair === m.pair) {
392
+ b.controls.push(/* @__PURE__ */ d(g, { children: y }, s)), b.block &&= m.block === !0;
393
+ continue;
394
+ }
395
+ u.push({
396
+ key: s,
397
+ pair: m.pair,
398
+ label: m.pair ?? m.name,
399
+ title: m.description,
400
+ block: m.block === !0,
401
+ controls: [/* @__PURE__ */ d(g, { children: y }, s)]
402
+ });
403
+ }
404
+ return u.map((e) => typeof e == "object" && e && "controls" in e ? e.block ? /* @__PURE__ */ d("div", {
405
+ className: `${O.rowControls} ${O.blockRow}`,
406
+ title: e.title,
407
+ children: e.controls
408
+ }, e.key) : /* @__PURE__ */ f("div", {
409
+ className: O.row,
410
+ children: [/* @__PURE__ */ d("span", {
411
+ className: O.rowLabel,
412
+ title: e.title,
413
+ children: e.label
414
+ }), /* @__PURE__ */ d("span", {
415
+ className: O.rowControls,
416
+ children: e.controls
417
+ })]
418
+ }, e.key) : e);
419
+ }, l = c(a.children, !s);
420
+ return l.length === 0 ? null : /* @__PURE__ */ f("div", {
421
+ className: O.objectLeaf,
422
+ children: [!s && /* @__PURE__ */ d("h4", {
423
+ className: O.sectionTitle,
424
+ children: a.name
425
+ }), l]
426
+ });
427
+ }
428
+ function P({ text: e, placeholder: t, ariaLabel: n, onCommit: r }) {
429
+ let [i, a] = _(null);
430
+ return /* @__PURE__ */ d(o, {
431
+ value: i ?? e ?? "",
432
+ placeholder: t,
433
+ "aria-label": n,
434
+ onChange: a,
435
+ onBlur: () => {
436
+ i !== null && i !== e && r(i), a(null);
437
+ },
438
+ onKeyDown: (e) => {
439
+ e.key === "Enter" && e.target.blur();
440
+ }
441
+ });
442
+ }
443
+ //#endregion
444
+ export { C as a, T as c, y as i, p as n, D as o, E as r, w as s, A as t };
445
+
446
+ //# sourceMappingURL=SelectionPanel-C7kygTFE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectionPanel-C7kygTFE.js","names":[],"sources":["../../src/components/SelectionPanel/model.ts","../../src/components/SelectionPanel/SelectionPanel.module.css","../../src/components/SelectionPanel/SelectionPanel.tsx"],"sourcesContent":["// Pure selection→panel derivations for SelectionPanel. Kept free of\n// React so intersection/aggregation semantics are unit-testable.\n//\n// Path convention (see core `NodePropertiesEntry`): a leaf's OWN KEY in\n// the schema is its node path — a dotted path of any depth rooted at\n// `pose` or `data` (`pose.x`, `data.fill`, `data.style.fontSize`). Group\n// keys are organizational only.\n\nimport {\n MIXED,\n type Mixed,\n type NodePropertiesEntry,\n type NodeRoutingEntry,\n type SceneNode,\n type ToolPrefGroup,\n type ToolPrefLeaf,\n} from '@weasel-js/core';\n\n// Re-exported under its existing public name here: the kit-wide \"these\n// values disagree\" sentinel (`@weasel-js/core`'s `MIXED`) also covers\n// multi-node aggregation, so this panel doesn't get its own symbol.\nexport { MIXED };\nexport type { Mixed };\n\nexport type AnyNode = SceneNode<unknown, string, unknown>;\n\n/**\n * One editable property in a {@link SelectionPanel}, paired with the dotted\n * node path it reads and writes.\n */\nexport interface PanelLeaf {\n /** Dotted node path — the leaf's key in the schema. */\n path: string;\n leaf: ToolPrefLeaf;\n}\n\n/**\n * One labeled row of a panel section. Holds several leaves when the schema\n * paired them.\n */\nexport interface PanelRow {\n label: string;\n leaves: PanelLeaf[];\n}\n\n/**\n * A titled block of rows, derived from one top-level group of a properties\n * schema. `key` is that group's schema key.\n */\nexport interface PanelSection {\n key: string;\n name: string;\n rows: PanelRow[];\n}\n\n/** Derive a node's kind: containers are `'group'`; leaves classify their\n * `data` through the routing entries (first match wins, `'unknown'`\n * otherwise) — same semantics as core's `NodeRouting.classify`. */\nexport function classifyKind(\n node: AnyNode,\n routing: readonly NodeRoutingEntry[],\n): string {\n if (node.kind === 'container') return 'group';\n for (const entry of routing) {\n if (entry.matches(node.data)) return entry.name;\n }\n return 'unknown';\n}\n\nfunction isGroup(n: ToolPrefLeaf | ToolPrefGroup): n is ToolPrefGroup {\n return !('kind' in n);\n}\n\n/** Flatten a schema to sections of leaves. Nested groups fold into their\n * top-level section; top-level leaves get an untitled leading section. */\nfunction flatten(schema: ToolPrefGroup): PanelSection[] {\n const sections: PanelSection[] = [];\n const untitled: PanelLeaf[] = [];\n\n const collect = (group: ToolPrefGroup, into: PanelLeaf[]): void => {\n for (const [key, child] of Object.entries(group.children)) {\n if (isGroup(child)) collect(child, into);\n else into.push({ path: key, leaf: child });\n }\n };\n\n for (const [key, child] of Object.entries(schema.children)) {\n if (isGroup(child)) {\n const leaves: PanelLeaf[] = [];\n collect(child, leaves);\n sections.push({ key, name: child.name, rows: pairRows(leaves) });\n } else {\n untitled.push({ path: key, leaf: child });\n }\n }\n if (untitled.length > 0) {\n sections.unshift({ key: '', name: '', rows: pairRows(untitled) });\n }\n return sections;\n}\n\n/** Merge consecutive leaves sharing a `pair` id into one labeled row. */\nfunction pairRows(leaves: readonly PanelLeaf[]): PanelRow[] {\n const rows: PanelRow[] = [];\n for (const item of leaves) {\n const pair = item.leaf.pair;\n const prev = rows[rows.length - 1];\n if (pair !== undefined && prev !== undefined && prev.label === pair) {\n prev.leaves.push(item);\n } else {\n rows.push({ label: pair ?? item.leaf.name, leaves: [item] });\n }\n }\n return rows;\n}\n\n/**\n * The schema the panel shows for a set of kinds: one kind → its full\n * schema; several → the intersection by (path, leaf kind), shaped by the\n * first kind's section/row layout. Kinds without a registered schema\n * contribute nothing, so their presence collapses the intersection.\n */\nexport function effectiveSections(\n kinds: readonly string[],\n entries: readonly NodePropertiesEntry[],\n): PanelSection[] {\n const uniq = [...new Set(kinds)];\n if (uniq.length === 0) return [];\n const byName = new Map(entries.map((e) => [e.name, e]));\n const schemas = uniq.map((k) => byName.get(k)?.schema);\n if (schemas.some((s) => s === undefined)) return [];\n const [first, ...rest] = schemas as ToolPrefGroup[];\n\n const flatFirst = flatten(first);\n if (rest.length === 0) return flatFirst;\n\n // '\\0' delimiter: path and kind strings are unconstrained, so a\n // printable separator could collide with real content.\n const leafKey = (l: PanelLeaf): string => `${l.path}\\0${l.leaf.kind}`;\n const restKeys = rest.map(\n (schema) =>\n new Set(\n flatten(schema)\n .flatMap((s) => s.rows)\n .flatMap((r) => r.leaves)\n .map(leafKey),\n ),\n );\n const keep = (l: PanelLeaf): boolean =>\n restKeys.every((set) => set.has(leafKey(l)));\n\n return flatFirst\n .map((section) => ({\n ...section,\n rows: section.rows\n .map((row) => ({ ...row, leaves: row.leaves.filter(keep) }))\n .filter((row) => row.leaves.length > 0),\n }))\n .filter((section) => section.rows.length > 0);\n}\n\n/** Read a node value at a dotted path of any depth (`pose.x`,\n * `data.style.fontSize`, `data.style.fill.color`). Returns `undefined` if\n * the path is dotless (mirrors `commit`'s no-op on a malformed schema\n * key) or if any intermediate segment is missing or not an object. */\nexport function nodeValueAt(node: AnyNode, path: string): unknown {\n const segments = path.split('.');\n if (segments.length < 2) return undefined;\n const head = segments[0];\n let cursor: unknown = head === 'pose' ? node.pose : head === 'data' ? node.data : undefined;\n for (let i = 1; i < segments.length; i++) {\n if (cursor == null || typeof cursor !== 'object') return undefined;\n cursor = (cursor as Record<string, unknown>)[segments[i]];\n }\n return cursor;\n}\n\n/** Immutably set `value` at a dotted path within `root`, cloning each level\n * on the way down so React sees new object identities. Arrays stay arrays\n * (an array intermediate is shallow-copied, not flattened into `{0: ...}`)\n * — this is the write-side twin of `nodeValueAt`, so a schema author who\n * gets a working read gets a working write. */\nexport function setAtPath(root: object, segments: readonly string[], value: unknown): object {\n const [head, ...rest] = segments;\n if (rest.length === 0) {\n return Array.isArray(root)\n ? Object.assign([...root], { [head]: value })\n : { ...root, [head]: value };\n }\n const child = (root as Record<string, unknown>)[head];\n const childObj = Array.isArray(child)\n ? [...child]\n : child != null && typeof child === 'object'\n ? { ...child }\n : {};\n const nested = setAtPath(childObj, rest, value);\n return Array.isArray(root)\n ? Object.assign([...root], { [head]: nested })\n : { ...root, [head]: nested };\n}\n\n/** Aggregate a path across nodes: the shared value, or `MIXED`. */\nexport function aggregateValue(\n nodes: readonly AnyNode[],\n path: string,\n): unknown | Mixed {\n let value: unknown;\n let first = true;\n for (const node of nodes) {\n const v = nodeValueAt(node, path);\n if (first) {\n value = v;\n first = false;\n } else if (!Object.is(v, value)) {\n return MIXED;\n }\n }\n return value;\n}\n\n/** `'rect ×2 · text'` — kinds in first-seen order with counts. */\nexport function kindBreakdown(kinds: readonly string[]): string {\n const counts = new Map<string, number>();\n for (const k of kinds) counts.set(k, (counts.get(k) ?? 0) + 1);\n return [...counts.entries()]\n .map(([k, n]) => (n > 1 ? `${k} ×${n}` : k))\n .join(' · ');\n}\n",".root {\n display: flex;\n flex-direction: column;\n font-size: var(--wzl-font-size-sm);\n color: var(--wzl-fg);\n}\n\n.header {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 8px 0;\n border-block-end: 1px solid var(--wzl-border);\n}\n\n.kind {\n font-weight: var(--wzl-font-weight-bold);\n font-size: var(--wzl-font-size);\n}\n\n.breakdown {\n font-size: var(--wzl-font-size-sm);\n opacity: 0.6;\n}\n\n.section {\n padding: 6px 0 8px;\n border-block-end: 1px solid var(--wzl-border);\n}\n\n.section:last-child {\n border-block-end: none;\n}\n\n.sectionTitle {\n margin: 0 0 6px;\n font-size: var(--wzl-font-size-xs);\n font-weight: var(--wzl-font-weight-bold);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n opacity: 0.55;\n}\n\n.row {\n display: grid;\n grid-template-columns: 64px 1fr;\n gap: 6px;\n align-items: center;\n margin-block-end: 5px;\n}\n\n.rowLabel {\n opacity: 0.75;\n}\n\n.rowControls {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n align-items: center;\n min-inline-size: 0;\n}\n\n/* Glyph plus its control, held together so the pair row wraps between\n fields rather than between a field and the icon naming it. */\n.namedField {\n display: flex;\n align-items: center;\n gap: 4px;\n min-inline-size: 0;\n}\n\n.namedField > svg {\n flex: none;\n opacity: 0.75;\n}\n\n.blockRow {\n margin-block-end: 5px;\n}\n\n/* Fields paired into one row split it evenly, the way Position's x/y do. */\n.rowControls > .namedField {\n flex: 1 1 0;\n}\n\n/* Except around a segmented bar, which is sized by its segments: an equal\n share of the row is wider than the buttons want to be. */\n.rowControls > .namedField:has(> .toggle) {\n flex: 0 1 auto;\n}\n\n.rowControls > * {\n flex: 1;\n min-inline-size: 0;\n}\n\n/* Bars sharing a row split the leftover between them, so the gap stays fixed\n and the width lands on the segments. A bar with the row to itself is sized\n by its segments instead — a third of 240px is a plate, not a button. */\n.rowControls > .toggle {\n flex: 1 1 auto;\n}\n\n.rowControls > .toggle:only-child {\n flex: 0 1 auto;\n}\n\n.number :global(input),\n.select {\n font-size: var(--wzl-font-size-sm);\n}\n\n.unitSuffix {\n flex: none;\n font-size: var(--wzl-font-size-sm);\n opacity: 0.6;\n}\n\n.mixedSwitch {\n opacity: 0.55;\n}\n\n.unrenderable {\n font-size: var(--wzl-font-size-sm);\n opacity: 0.5;\n font-style: italic;\n}\n\n/* A `block` leaf that renders its own rows — an object leaf's fields. */\n.objectLeaf {\n margin-block: 8px 4px;\n}\n\n.objectLeaf .sectionTitle {\n margin-block: 7px 5px;\n}\n\n/* Fields under a heading inside an object read as belonging to it. */\n.objectGroup {\n padding-inline-start: 8px;\n border-inline-start: 1px solid var(--wzl-border);\n margin-block-end: 4px;\n}\n\n/* `InlineRange` owns the track; the row only says how it shares the space. */\n.slider {\n flex: 1;\n}\n\n.sliderReadout {\n flex: none;\n min-inline-size: 3ch;\n text-align: end;\n font-size: var(--wzl-font-size-sm);\n opacity: 0.7;\n}\n","import { Fragment, useState, useSyncExternalStore, type ReactNode } from 'react';\nimport {\n asNodeId,\n isBuiltinToolPref,\n type FillStyle,\n type NodePropertiesEntry,\n type NodeRoutingEntry,\n type Scene,\n type SelectionApi,\n type ToolPrefColor,\n type ToolPrefEnum,\n type ToolPrefLeaf,\n type ToolPrefNumber,\n type ToolPrefPaint,\n type ToolPrefGroup,\n type ToolPrefObject,\n} from '@weasel-js/core';\nimport { ColorField } from '../ColorField';\nimport { PaintInput } from '../PaintInput';\nimport { InlineRange } from '../InlineRange';\nimport { Input } from '../Input';\nimport { NumberField } from '../NumberField';\nimport { Select } from '../Select';\nimport { Switch } from '../Switch';\nimport { ToggleBar } from '../ToggleBar';\nimport { Icon } from '../../icons/Icon';\nimport { ICON_PATHS, type IconName } from '../../icons/paths';\nimport {\n MIXED,\n aggregateValue,\n classifyKind,\n effectiveSections,\n kindBreakdown,\n setAtPath,\n type AnyNode,\n type PanelLeaf,\n} from './model';\nimport s from './SelectionPanel.module.css';\n\n/** What a {@link PropertyRenderer} is given for the property it is rendering. */\nexport interface PropertyRenderContext {\n /** Dotted node path of the leaf (`pose.x`, `data.fill`). */\n path: string;\n /** The schema leaf. App renderers narrow it to their own kind shape. */\n pref: ToolPrefLeaf;\n /** Aggregated value across the selection; `undefined` when mixed or unset. */\n value: unknown;\n /** True when selected nodes disagree at this path. */\n mixed: boolean;\n /**\n * True when the nodes agree and what they agree on is nothing — the field\n * is absent, and whatever paints comes from a fallback further down.\n *\n * Distinct from `mixed`, and both leave `value` undefined. A control must\n * not substitute its schema default here: doing so shows a value the node\n * does not hold, and the next edit writes that invention back.\n */\n unset: boolean;\n /**\n * The fields of the object this leaf is a field of — `undefined` for a\n * top-level leaf, and for a field of an object the node does not hold.\n *\n * What a control needs when its own field doesn't carry the whole answer:\n * a stroke's dash lengths are multiples of the stroke's width, so reading\n * the array as a style takes both.\n */\n siblings?: Record<string, unknown>;\n /** Commit a value — fans out to every selected node in one undo step. */\n setValue: (value: unknown) => void;\n /**\n * The aggregated value at another node path — what `value` and `mixed` are\n * for this leaf's own path, for any path.\n *\n * A control whose subject spans more than one field needs it: a font picker\n * reporting which variant will actually paint has to read the node's weight\n * and style. Reading it off `value` is impossible — a leaf is handed one\n * field.\n */\n valueAt: (path: string) => { value: unknown; mixed: boolean };\n}\n\n/**\n * Renders the control for one property across the whole selection.\n * Returning `null` collapses the leaf.\n */\nexport type PropertyRenderer = (ctx: PropertyRenderContext) => ReactNode;\n\n/** Props for {@link SelectionPanel}. */\nexport interface SelectionPanelProps<TData, TLayer extends string, TPose> {\n /** The scene handle (`useScene`). The panel subscribes itself, so it\n * re-renders on scene mutations regardless of parent renders. */\n scene: Scene<TData, TLayer, TPose>;\n /** Selection handle (`useSelection`). Only `current` is read. */\n selection: Pick<SelectionApi, 'current'>;\n /** Properties-trait entries, e.g. core's `defaultNodeProperties` /\n * `inferredNodeProperties` (+ consumer extras). Memoize or hoist. */\n properties: readonly NodePropertiesEntry[];\n /** Routing-trait classifiers used to derive each node's kind — pass\n * the same list the canvas uses. Memoize or hoist. */\n routing: readonly NodeRoutingEntry[];\n /** Control overrides / app-defined kinds (PrefsForm-style). Keys are\n * leaf paths (`data.fill`, checked first) or leaf kinds (`color`).\n * A renderer returning `null` collapses its leaf (and the row, when\n * every leaf in it collapses). */\n renderers?: Record<string, PropertyRenderer>;\n /** Kind → header label. Default: capitalized kind name. */\n kindLabel?: (kind: string) => string;\n /** Rendered when the selection is empty. */\n emptyState?: ReactNode;\n className?: string;\n}\n\nconst defaultKindLabel = (kind: string): string =>\n kind.length === 0 ? kind : kind[0].toUpperCase() + kind.slice(1);\n\n/**\n * Pre-baked selection properties panel. Shows the selected nodes' kind\n * and the properties-trait schema for that kind; multi-selections show\n * the intersection of the kinds' schemas with per-field Mixed state.\n * Edits commit as one labeled `scene.batch` fan-out per gesture.\n */\nexport function SelectionPanel<TData, TLayer extends string, TPose>(\n props: SelectionPanelProps<TData, TLayer, TPose>,\n) {\n const {\n scene,\n selection,\n properties,\n routing,\n renderers,\n kindLabel = defaultKindLabel,\n emptyState = null,\n className,\n } = props;\n\n useSyncExternalStore(scene.subscribe, scene.getVersion, scene.getVersion);\n\n const nodes = selection.current\n .map((id) => scene.get(asNodeId(id)))\n .filter((n): n is NonNullable<typeof n> => n != null) as readonly AnyNode[];\n\n const kinds = nodes.map((n) => classifyKind(n, routing));\n const selectionKey = nodes.map((n) => n.id).join(',');\n const sections = effectiveSections(kinds, properties);\n\n if (nodes.length === 0) {\n return <div className={[s.root, className].filter(Boolean).join(' ')}>{emptyState}</div>;\n }\n\n const commit = (leaf: PanelLeaf, value: unknown): void => {\n const [head, ...rest] = leaf.path.split('.');\n if (rest.length === 0) return;\n const ids = selection.current.map(asNodeId);\n scene.batch(`Edit ${leaf.leaf.name}`, () => {\n for (const id of ids) {\n const node = scene.get(id);\n if (!node) continue;\n // `setAtPath` spreads plain objects/arrays down the schema path;\n // consumer `pose`/`data` are assumed plain-object-shaped along\n // that path (a class instance's prototype would be dropped by\n // the spread) — the `as TPose`/`as TData` casts below rely on it.\n if (head === 'pose') {\n scene.setPose(id, setAtPath(node.pose as object, rest, value) as TPose);\n } else if (head === 'data') {\n scene.update(id, { data: setAtPath(node.data as object, rest, value) as TData });\n }\n }\n });\n };\n\n return (\n <div className={[s.root, className].filter(Boolean).join(' ')}>\n <header className={s.header}>\n {nodes.length === 1 ? (\n <span className={s.kind}>{kindLabel(kinds[0])}</span>\n ) : (\n <>\n <span className={s.kind}>{nodes.length} selected</span>\n <span className={s.breakdown}>{kindBreakdown(kinds)}</span>\n </>\n )}\n </header>\n {sections.map((section) => {\n // Render controls before emitting row chrome so a null-rendering\n // leaf (custom renderer opting out) collapses its cell — and the\n // whole row / section when nothing survives. PrefsForm precedent.\n const rows = section.rows\n .map((row) => {\n const controls = row.leaves\n .map((panelLeaf) => ({\n panelLeaf,\n content: renderLeafControl(\n panelLeaf,\n row.leaves.length > 1\n ? `${row.label} ${panelLeaf.leaf.name}`\n : panelLeaf.leaf.name,\n nodes,\n renderers,\n commit,\n selectionKey,\n ),\n }))\n .filter((c) => c.content != null);\n return { row, controls };\n })\n .filter(({ controls }) => controls.length > 0);\n if (rows.length === 0) return null;\n return (\n <section key={section.key} className={s.section}>\n {section.name !== '' && <h3 className={s.sectionTitle}>{section.name}</h3>}\n {rows.map(({ row, controls }) => (\n // A `block` leaf brings its own chrome — it spans the section\n // instead of sitting in a labeled row's control cell. Under a\n // headless section nothing else names it, so it heads itself;\n // an object leaf already renders its own heading.\n row.leaves.length === 1 && row.leaves[0].leaf.block ? (\n <div key={row.leaves[0].path}>\n {section.name === '' && row.leaves[0].leaf.kind !== 'object' && (\n <h4 className={s.sectionTitle} title={row.leaves[0].leaf.description}>\n {row.leaves[0].leaf.name}\n </h4>\n )}\n {controls[0].content}\n </div>\n ) : (\n <div key={row.leaves[0].path} className={s.row}>\n <span className={s.rowLabel} title={row.leaves[0].leaf.description}>\n {row.label}\n </span>\n <span className={s.rowControls}>\n {controls.map(({ panelLeaf, content }) => (\n <Fragment key={panelLeaf.path}>{content}</Fragment>\n ))}\n </span>\n </div>\n )\n ))}\n </section>\n );\n })}\n </div>\n );\n}\n\nfunction renderLeafControl(\n panelLeaf: PanelLeaf,\n ariaLabel: string,\n nodes: readonly AnyNode[],\n renderers: Record<string, PropertyRenderer> | undefined,\n commit: (leaf: PanelLeaf, value: unknown) => void,\n selectionKey: string,\n): ReactNode {\n const { path, leaf } = panelLeaf;\n const aggregated = aggregateValue(nodes, path);\n const mixed = aggregated === MIXED;\n const value = mixed ? undefined : aggregated;\n\n const ctx: PropertyRenderContext = {\n path,\n pref: leaf,\n value,\n mixed,\n unset: !mixed && aggregated === undefined,\n setValue: (v) => commit(panelLeaf, v),\n valueAt: (p) => {\n const at = aggregateValue(nodes, p);\n return at === MIXED ? { value: undefined, mixed: true } : { value: at, mixed: false };\n },\n };\n\n const custom = renderers?.[path] ?? renderers?.[leaf.kind];\n if (custom) return custom(ctx);\n return renderBuiltin(ctx, ariaLabel, renderers, selectionKey);\n}\n\nfunction renderBuiltin(\n ctx: PropertyRenderContext,\n ariaLabel: string,\n renderers?: Record<string, PropertyRenderer>,\n /** Identifies the current selection, so a control holding per-selection\n * scratch (the paint kind memory) is remounted rather than carried over. */\n selectionKey = '',\n /** True for a field of an object leaf. Such a field writes itself into its\n * parent, so it can only hold values its parent's type permits — which is\n * what rules \"none\" out for a nested paint. */\n nested = false,\n): ReactNode {\n const { pref, value, mixed, unset, setValue } = ctx;\n if (!isBuiltinToolPref(pref)) {\n return <span className={s.unrenderable}>({pref.kind}: no renderer)</span>;\n }\n switch (pref.kind) {\n case 'number': {\n const p = pref as ToolPrefNumber;\n const stored = typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n const display = stored !== undefined ? (p.unit ? p.unit.toDisplay(stored) : stored) : NaN;\n if (p.control === 'slider') {\n const min = p.min ?? 0;\n const max = p.max ?? 100;\n const known = !mixed && stored !== undefined;\n return (\n <>\n <InlineRange\n min={min}\n max={max}\n step={p.step ?? 1}\n // The thumb clamps to the track; the readout beside it does not,\n // so a value past `max` is still reported as what it is.\n value={known ? Math.min(Math.max(display, min), max) : min}\n disabled={mixed}\n className={s.slider}\n aria-label={ariaLabel}\n onChange={(e) => {\n const next = Number(e.target.value);\n setValue(p.unit ? p.unit.fromDisplay(next) : next);\n }}\n />\n <span className={s.sliderReadout} aria-hidden=\"true\">\n {known ? `${display}${p.unit?.suffix ?? ''}` : '—'}\n </span>\n </>\n );\n }\n const field = (\n <NumberField\n className={s.number}\n value={mixed || stored === undefined ? NaN : display}\n placeholder={mixed ? 'Mixed' : undefined}\n minValue={p.min}\n maxValue={p.max}\n step={p.step ?? 1}\n hideSteppers\n aria-label={ariaLabel}\n onChange={(n) => {\n if (Number.isNaN(n)) return;\n setValue(p.unit ? p.unit.fromDisplay(n) : n);\n }}\n />\n );\n if (p.unit?.suffix === undefined) return field;\n return (\n <>\n {field}\n <span className={s.unitSuffix} aria-hidden=\"true\">\n {p.unit.suffix}\n </span>\n </>\n );\n }\n case 'string': {\n return (\n <DraftInput\n text={mixed ? undefined : typeof value === 'string' ? value : ''}\n placeholder={mixed ? 'Mixed' : undefined}\n ariaLabel={ariaLabel}\n onCommit={setValue}\n />\n );\n }\n case 'boolean': {\n const control = (\n <Switch isSelected={Boolean(value)} onChange={setValue} aria-label={ariaLabel} />\n );\n // Switch has no indeterminate state; a reduced-opacity wrapper with\n // a title is the cheap honest cue for a mixed selection, and for a\n // field the node leaves to its fallback.\n if (!mixed && !unset) return control;\n return (\n <span className={s.mixedSwitch} title={mixed ? 'Mixed' : 'Not set'}>\n {control}\n </span>\n );\n }\n case 'enum': {\n const p = pref as ToolPrefEnum;\n // An encoded leaf stores something other than the option string, so the\n // option comes from the encoding rather than from the value — and an\n // absent field is one of the things it reads (no dash is `solid`), which\n // is why `unset` doesn't blank the control here.\n const option = p.encoding\n ? (mixed ? undefined : p.encoding.read(value, ctx.siblings))\n : mixed || unset\n ? undefined\n : typeof value === 'string'\n ? value\n : p.default;\n const choose = (next: string): void =>\n setValue(p.encoding ? p.encoding.write(next, ctx.siblings) : next);\n if (p.control === 'toggle') {\n // Every option visible at once, which is the point of a segmented\n // control: a glyph, else `short`, keeps it to the width a property row\n // has, and the full label stays the accessible name. A mixed selection\n // selects nothing rather than picking a winner.\n return (\n <ToggleBar<string>\n size=\"sm\"\n variant=\"flat\"\n className={s.toggle}\n ariaLabel={ariaLabel}\n items={p.options.map((o) => ({\n value: o.value,\n label:\n o.icon && o.icon in ICON_PATHS ? (\n <Icon name={o.icon as IconName} size={14} />\n ) : (\n (o.short ?? o.label)\n ),\n ariaLabel: o.label,\n disabled: o.disabled,\n }))}\n value={option ?? null}\n onChange={(next) => { if (next !== null) choose(next); }}\n />\n );\n }\n return (\n <Select<string>\n className={s.select}\n options={p.options.map((o) => ({ value: o.value, label: o.label, isDisabled: o.disabled }))}\n selectedKey={option ?? null}\n placeholder={mixed ? 'Mixed' : unset ? '—' : undefined}\n onSelectionChange={choose}\n aria-label={ariaLabel}\n />\n );\n }\n case 'color': {\n const p = pref as ToolPrefColor;\n return (\n <ColorField\n value={mixed ? undefined : typeof value === 'string' ? value : p.default}\n mixed={mixed}\n alpha={p.alpha}\n onChange={setValue}\n aria-label={ariaLabel}\n />\n );\n }\n case 'paint': {\n // The value is a whole `FillStyle`, and `PaintInput` edits it as one —\n // previewing a gradient as a gradient rather than degrading it to the\n // indeterminate chip. That leaves the checkerboard meaning `mixed` and\n // nothing else.\n const p = pref as ToolPrefPaint;\n // `??` would read an explicit `null` — \"no paint\" — as absent and show\n // the schema default over it, so the None segment could never stay lit.\n // Unset shows the fallback that is actually on the canvas, dimmed: true,\n // but not chosen, which is what the dimming says.\n const paint = (value === null\n ? null\n : (value ?? (mixed ? undefined : p.default))) as FillStyle | null | undefined;\n return (\n <PaintInput\n // Per-kind switch memory is scratch for one selection; carrying it\n // across would recall the previous node's gradient.\n key={selectionKey}\n value={paint}\n mixed={mixed}\n unset={unset}\n // A nested paint writes one field of its parent, and no kit paint\n // field is optional — `Stroke.paint` is required. Removing the\n // whole parent is a different edit than repainting it, so the\n // control must not offer one as the other. A consumer whose\n // renderer owns the parent key (WeaselDraw's `setStroke`) can.\n allowNone={!nested}\n onChange={setValue}\n aria-label={ariaLabel}\n />\n );\n }\n case 'object': {\n // One value with fields hanging off it. Each child writes the parent\n // whole, so a field is never set on a half-built object.\n return <ObjectLeaf ctx={ctx} renderers={renderers} selectionKey={selectionKey} />;\n }\n default: {\n // Not reachable while every built-in kind has an arm — and a new kind\n // that lacks one is a compile error here, never a blank cell.\n const _exhaustive: never = pref;\n throw new Error(\n `SelectionPanel: no control for built-in pref kind \"${(_exhaustive as { kind: string }).kind}\"`,\n );\n }\n }\n}\n\n/**\n * Renders an object leaf: a titled block whose rows are the object's own\n * fields. A child's edit commits the parent object, never the child's path —\n * writing into a path whose value is not an object yet would corrupt it.\n */\n/** One labeled row inside an object leaf; `pair` merges adjacent fields into\n * it, exactly as the section rows merge theirs. */\ninterface ObjectRow {\n key: string;\n pair?: string;\n label: string;\n title?: string;\n /** Every field in the row brought its own chrome, so the row drops the\n * label column and spans the block. One unlabeled field in an otherwise\n * labeled pair would leave the row named after half of itself. */\n block: boolean;\n controls: ReactNode[];\n}\n\n/**\n * Renders an object leaf: the object's own fields, as rows. A child's edit\n * commits the parent object, never the child's path — writing into a path\n * whose value is not an object yet would corrupt it.\n */\nfunction ObjectLeaf({\n ctx,\n renderers,\n selectionKey = '',\n}: {\n ctx: PropertyRenderContext;\n renderers?: Record<string, PropertyRenderer>;\n selectionKey?: string;\n}): ReactNode {\n const pref = ctx.pref as ToolPrefObject;\n const held = typeof ctx.value === 'object' && ctx.value !== null\n ? (ctx.value as Record<string, unknown>)\n : undefined;\n\n // A value whose fields are entirely grouped is titled by those groups — its\n // own heading would stack onto the first one and name nothing new.\n const allGrouped = Object.values(pref.children).every((child) => !('kind' in child));\n\n // `indent` is false when nothing visible sits above these rows: depth is\n // drawn only where a label marks it.\n const rowsOf = (\n children: Record<string, ToolPrefLeaf | ToolPrefGroup>,\n indent: boolean,\n ): ReactNode[] => {\n const out: (ReactNode | ObjectRow)[] = [];\n for (const [key, child] of Object.entries(children)) {\n // A group among the children organises the fields under a heading\n // without contributing to the path — the rule group keys follow at the\n // top level.\n if (!('kind' in child)) {\n const labeled = child.name !== '';\n const inner = rowsOf(child.children, labeled);\n if (inner.length === 0) continue;\n out.push(\n <div key={`group:${key}`} className={labeled && indent ? s.objectGroup : undefined}>\n {labeled && <h5 className={s.sectionTitle}>{child.name}</h5>}\n {inner}\n </div>,\n );\n continue;\n }\n const childPath = `${ctx.path}.${key}`;\n const childCtx: PropertyRenderContext = {\n path: childPath,\n pref: child,\n value: held?.[key],\n mixed: ctx.mixed,\n // A field of an object the node does not hold is unset, and so is one\n // the object omits — `data.stroke` absent leaves every stroke field\n // with nothing behind it, not with the schema's defaults.\n unset: !ctx.mixed && held?.[key] === undefined,\n siblings: held,\n valueAt: ctx.valueAt,\n setValue: (v) => {\n const base = held ?? pref.fromScalar?.(ctx.value) ?? {};\n if (v === undefined) {\n // A field written as absent is absent — leaving the key holding\n // `undefined` says the object has a dash of nothing.\n const { [key]: _dropped, ...rest } = base;\n ctx.setValue(rest);\n return;\n }\n ctx.setValue({ ...base, [key]: v });\n },\n };\n const custom = renderers?.[childPath] ?? renderers?.[child.kind];\n const rendered = custom\n ? custom(childCtx)\n : renderBuiltin(childCtx, child.name, renderers, selectionKey, true);\n if (rendered == null) continue;\n // Neither a paired row nor a block one gives a field its own label\n // column, so the glyph is all that names it on screen. The control\n // already carries `name` as its accessible name, which leaves the\n // glyph decorative.\n const unlabeled = child.pair !== undefined || child.block === true;\n const glyph = unlabeled && child.icon && child.icon in ICON_PATHS;\n const content = glyph ? (\n <span className={s.namedField}>\n <Icon name={child.icon as IconName} size={14} />\n {rendered}\n </span>\n ) : (\n rendered\n );\n const prev = out[out.length - 1];\n const isRow = (v: ReactNode | ObjectRow): v is ObjectRow =>\n typeof v === 'object' && v !== null && 'controls' in v;\n if (child.pair !== undefined && isRow(prev) && prev.pair === child.pair) {\n prev.controls.push(<Fragment key={childPath}>{content}</Fragment>);\n prev.block &&= child.block === true;\n continue;\n }\n out.push({\n key: childPath,\n pair: child.pair,\n label: child.pair ?? child.name,\n title: child.description,\n block: child.block === true,\n controls: [<Fragment key={childPath}>{content}</Fragment>],\n });\n }\n return out.map((entry) =>\n typeof entry === 'object' && entry !== null && 'controls' in entry ? (\n entry.block ? (\n <div key={entry.key} className={`${s.rowControls} ${s.blockRow}`} title={entry.title}>\n {entry.controls}\n </div>\n ) : (\n <div key={entry.key} className={s.row}>\n <span className={s.rowLabel} title={entry.title}>{entry.label}</span>\n <span className={s.rowControls}>{entry.controls}</span>\n </div>\n )\n ) : (\n entry\n ),\n );\n };\n\n const rows = rowsOf(pref.children, !allGrouped);\n if (rows.length === 0) return null;\n return (\n <div className={s.objectLeaf}>\n {!allGrouped && <h4 className={s.sectionTitle}>{pref.name}</h4>}\n {rows}\n </div>\n );\n}\n\n/** Text input with commit-on-blur/Enter semantics — live-per-keystroke\n * writes would emit one undo step per character. */\nfunction DraftInput({\n text,\n placeholder,\n ariaLabel,\n onCommit,\n}: {\n text: string | undefined;\n placeholder?: string;\n ariaLabel: string;\n onCommit: (v: string) => void;\n}) {\n const [draft, setDraft] = useState<string | null>(null);\n return (\n <Input\n value={draft ?? text ?? ''}\n placeholder={placeholder}\n aria-label={ariaLabel}\n onChange={setDraft}\n onBlur={() => {\n if (draft !== null && draft !== text) onCommit(draft);\n setDraft(null);\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter') (e.target as HTMLInputElement).blur();\n }}\n />\n );\n}\n"],"mappings":"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"}
@@ -23,4 +23,4 @@ function n(n) {
23
23
  //#endregion
24
24
  export { n as t };
25
25
 
26
- //# sourceMappingURL=Sidebar-DP_nyJze.js.map
26
+ //# sourceMappingURL=Sidebar-CtwBdr4n.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Sidebar-DP_nyJze.js","names":[],"sources":["../../src/components/Sidebar/Sidebar.module.css","../../src/components/Sidebar/Sidebar.tsx"],"sourcesContent":[".sidebar {\n display: flex;\n flex-direction: column;\n gap: 4px;\n background: var(--wzl-surface);\n color: var(--wzl-fg);\n}\n\n.sidebar.left {\n border-right: 1px solid var(--wzl-border);\n}\n.sidebar.right {\n border-left: 1px solid var(--wzl-border);\n}\n","import type { CSSProperties, ReactNode } from 'react';\nimport s from './Sidebar.module.css';\n\n/** Props for {@link Sidebar}. */\nexport interface SidebarProps {\n /**\n * Edge the sidebar docks to. Adds a class hook (`s.left` / `s.right`)\n * so consumers can target either edge for borders, shadows, etc.\n * Purely cosmetic — layout is the parent's job.\n */\n side?: 'left' | 'right';\n /**\n * Accessible label — rendered onto the `<aside>`. Without it, screen\n * readers fall back to \"complementary landmark,\" which is fine for\n * apps with a single sidebar but ambiguous when there's both a left\n * and a right.\n */\n ariaLabel?: string;\n children?: ReactNode;\n className?: string;\n /** Inline style — useful for width-as-custom-property handles or any\n * other one-off CSS property the consumer wants to set dynamically. */\n style?: CSSProperties;\n}\n\n/**\n * Generic sidebar shell — `<aside>` with column layout and token-driven\n * surface treatment. Width, sticky positioning, and resize handles stay\n * the consumer's concern (every app has its own layout grid); the kit\n * just provides the chrome.\n *\n * Pair with `<SidebarPanel>` for collapsible sections.\n */\nexport function Sidebar(props: SidebarProps) {\n const { side, ariaLabel, children, className, style } = props;\n const cls = [s.sidebar, side === 'left' && s.left, side === 'right' && s.right, className]\n .filter(Boolean).join(' ');\n return (\n <aside className={cls} aria-label={ariaLabel} style={style}>\n {children}\n </aside>\n );\n}\n"],"mappings":";;;;;;;;ACiCA,SAAgB,EAAQ,GAAqB;CAC3C,IAAM,EAAE,SAAM,cAAW,aAAU,cAAW,aAAU;AAGxD,QACE,kBAAC,SAAD;EAAO,WAHG;GAAC,EAAE;GAAS,MAAS,UAAU,EAAE;GAAM,MAAS,WAAW,EAAE;GAAO;GAAU,CACvF,OAAO,QAAQ,CAAC,KAAK,IAEJ;EAAK,cAAY;EAAkB;EAClD;EACK,CAAA"}
1
+ {"version":3,"file":"Sidebar-CtwBdr4n.js","names":[],"sources":["../../src/components/Sidebar/Sidebar.module.css","../../src/components/Sidebar/Sidebar.tsx"],"sourcesContent":[".sidebar {\n display: flex;\n flex-direction: column;\n gap: 4px;\n background: var(--wzl-surface);\n color: var(--wzl-fg);\n}\n\n.sidebar.left {\n border-right: 1px solid var(--wzl-border);\n}\n.sidebar.right {\n border-left: 1px solid var(--wzl-border);\n}\n","import type { CSSProperties, ReactNode } from 'react';\nimport s from './Sidebar.module.css';\n\n/** Props for {@link Sidebar}. */\nexport interface SidebarProps {\n /**\n * Edge the sidebar docks to. Adds a class hook (`s.left` / `s.right`)\n * so consumers can target either edge for borders, shadows, etc.\n * Purely cosmetic — layout is the parent's job.\n */\n side?: 'left' | 'right';\n /**\n * Accessible label — rendered onto the `<aside>`. Without it, screen\n * readers fall back to \"complementary landmark,\" which is fine for\n * apps with a single sidebar but ambiguous when there's both a left\n * and a right.\n */\n ariaLabel?: string;\n children?: ReactNode;\n className?: string;\n /** Inline style — useful for width-as-custom-property handles or any\n * other one-off CSS property the consumer wants to set dynamically. */\n style?: CSSProperties;\n}\n\n/**\n * Generic sidebar shell — `<aside>` with column layout and token-driven\n * surface treatment. Width, sticky positioning, and resize handles stay\n * the consumer's concern (every app has its own layout grid); the kit\n * just provides the chrome.\n *\n * Pair with `<SidebarPanel>` for collapsible sections.\n */\nexport function Sidebar(props: SidebarProps) {\n const { side, ariaLabel, children, className, style } = props;\n const cls = [s.sidebar, side === 'left' && s.left, side === 'right' && s.right, className]\n .filter(Boolean).join(' ');\n return (\n <aside className={cls} aria-label={ariaLabel} style={style}>\n {children}\n </aside>\n );\n}\n"],"mappings":";;;;;;;;ACiCA,SAAgB,EAAQ,GAAqB;CAC3C,IAAM,EAAE,SAAM,cAAW,aAAU,cAAW,aAAU;AAGxD,QACE,kBAAC,SAAD;EAAO,WAHG;GAAC,EAAE;GAAS,MAAS,UAAU,EAAE;GAAM,MAAS,WAAW,EAAE;GAAO;GAAU,CACvF,OAAO,QAAQ,CAAC,KAAK,IAEJ;EAAK,cAAY;EAAkB;EAClD;EACK,CAAA"}
@@ -0,0 +1,59 @@
1
+ import { n as e, t } from "./Tooltip-Di7vxbFa.js";
2
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
3
+ import { Focusable as i } from "react-aria-components";
4
+ var a = {
5
+ panel: "_panel_1uzcs_1",
6
+ title: "_title_1uzcs_9",
7
+ titleRow: "_titleRow_1uzcs_17",
8
+ titleButton: "_titleButton_1uzcs_25",
9
+ chevron: "_chevron_1uzcs_42",
10
+ chevronCollapsed: "_chevronCollapsed_1uzcs_49",
11
+ hideButton: "_hideButton_1uzcs_53",
12
+ collapsed: "_collapsed_1uzcs_66",
13
+ body: "_body_1uzcs_70"
14
+ };
15
+ //#endregion
16
+ //#region src/components/SidebarPanel/SidebarPanel.tsx
17
+ function o(o) {
18
+ let { title: s, collapsed: c, onToggleCollapse: l, onHide: u, children: d, className: f } = o, p = [
19
+ a.panel,
20
+ c && a.collapsed,
21
+ f
22
+ ].filter(Boolean).join(" "), m = l !== void 0;
23
+ return /* @__PURE__ */ r("div", {
24
+ className: p,
25
+ children: [s !== void 0 && /* @__PURE__ */ r("div", {
26
+ className: a.titleRow,
27
+ children: [m ? /* @__PURE__ */ r("button", {
28
+ type: "button",
29
+ className: a.titleButton,
30
+ onClick: l,
31
+ "aria-expanded": !c,
32
+ children: [/* @__PURE__ */ n("span", {
33
+ className: [a.chevron, c && a.chevronCollapsed].filter(Boolean).join(" "),
34
+ "aria-hidden": "true",
35
+ children: "▾"
36
+ }), /* @__PURE__ */ n("span", {
37
+ className: a.title,
38
+ children: s
39
+ })]
40
+ }) : /* @__PURE__ */ n("span", {
41
+ className: a.title,
42
+ children: s
43
+ }), u !== void 0 && /* @__PURE__ */ r(e, { children: [/* @__PURE__ */ n(i, { children: /* @__PURE__ */ n("button", {
44
+ type: "button",
45
+ className: a.hideButton,
46
+ onClick: u,
47
+ "aria-label": "Hide panel",
48
+ children: "×"
49
+ }) }), /* @__PURE__ */ n(t, { children: "Hide panel" })] })]
50
+ }), !c && /* @__PURE__ */ n("div", {
51
+ className: a.body,
52
+ children: d
53
+ })]
54
+ });
55
+ }
56
+ //#endregion
57
+ export { o as t };
58
+
59
+ //# sourceMappingURL=SidebarPanel-AeSc7wNR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SidebarPanel-AeSc7wNR.js","names":[],"sources":["../../src/components/SidebarPanel/SidebarPanel.module.css","../../src/components/SidebarPanel/SidebarPanel.tsx"],"sourcesContent":[".panel {\n /* Vertical only: the body spans the panel's full width, and the title row's\n own controls carry the horizontal inset. */\n padding: 8px 0;\n background: var(--wzl-surface);\n color: var(--wzl-fg);\n}\n\n.title {\n font-size: var(--wzl-font-size-sm);\n font-weight: var(--wzl-font-weight-bold);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--wzl-fg-muted);\n}\n\n.titleRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 4px;\n padding: 0;\n}\n\n.titleButton {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n flex: 1 1 auto;\n background: transparent;\n border: 0;\n padding: 4px 8px;\n cursor: pointer;\n color: inherit;\n font: inherit;\n text-align: left;\n}\n.titleButton:hover {\n color: var(--wzl-fg);\n}\n\n.chevron {\n display: inline-block;\n font-size: var(--wzl-font-size-xs);\n line-height: 1;\n color: var(--wzl-fg-muted);\n transition: transform var(--wzl-motion-fast) ease;\n}\n.chevronCollapsed {\n transform: rotate(-90deg);\n}\n\n.hideButton {\n background: transparent;\n border: 0;\n padding: 0 8px;\n font-size: var(--wzl-font-size);\n line-height: 1;\n color: var(--wzl-fg-muted);\n cursor: pointer;\n}\n.hideButton:hover {\n color: var(--wzl-fg);\n}\n\n.panel.collapsed {\n padding-bottom: 4px;\n}\n\n.body {\n padding: 0;\n}\n","import type { ReactNode } from 'react';\nimport { Focusable } from 'react-aria-components';\nimport { Tooltip, TooltipTrigger } from '../Tooltip';\nimport s from './SidebarPanel.module.css';\n\n/** Props for {@link SidebarPanel}. */\nexport interface SidebarPanelProps {\n /** Title rendered in the panel's header row. */\n title?: ReactNode;\n /**\n * Collapsed state — when true, only the header paints; the body is\n * omitted. Caller owns the state (typically via prefs) and flips it\n * back via `onToggleCollapse`.\n */\n collapsed?: boolean;\n /**\n * Click handler for the title row. When provided, the header renders\n * as a button (chevron + label) and acts as the collapse toggle.\n * When omitted, the title is static text and the panel can't collapse.\n */\n onToggleCollapse?(): void;\n /**\n * Optional close handler — when provided, the header gains a `×`\n * button on the trailing edge. Caller flips its \"hidden\" state and\n * conditionally unmounts the panel from the parent.\n */\n onHide?(): void;\n children?: ReactNode;\n className?: string;\n}\n\n/**\n * Generic collapsible panel section for sidebars. Pure presentation —\n * caller manages `collapsed`/`hidden` state externally (the pattern is\n * to drive it from app prefs so the Preferences modal and inline\n * chevrons toggle the same map).\n *\n * Pair with `<Sidebar>` for the docked column layout.\n */\nexport function SidebarPanel(props: SidebarPanelProps) {\n const { title, collapsed, onToggleCollapse, onHide, children, className } = props;\n const cls = [s.panel, collapsed && s.collapsed, className].filter(Boolean).join(' ');\n const headerInteractive = onToggleCollapse !== undefined;\n return (\n <div className={cls}>\n {title !== undefined && (\n <div className={s.titleRow}>\n {headerInteractive ? (\n <button\n type=\"button\"\n className={s.titleButton}\n onClick={onToggleCollapse}\n aria-expanded={!collapsed}\n >\n <span className={[s.chevron, collapsed && s.chevronCollapsed].filter(Boolean).join(' ')} aria-hidden=\"true\">▾</span>\n <span className={s.title}>{title}</span>\n </button>\n ) : (\n <span className={s.title}>{title}</span>\n )}\n {onHide !== undefined && (\n <TooltipTrigger>\n <Focusable>\n <button\n type=\"button\"\n className={s.hideButton}\n onClick={onHide}\n aria-label=\"Hide panel\"\n >\n ×\n </button>\n </Focusable>\n <Tooltip>Hide panel</Tooltip>\n </TooltipTrigger>\n )}\n </div>\n )}\n {!collapsed && <div className={s.body}>{children}</div>}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;ACuCA,SAAgB,EAAa,GAA0B;CACrD,IAAM,EAAE,UAAO,cAAW,qBAAkB,WAAQ,aAAU,iBAAc,GACtE,IAAM;EAAC,EAAE;EAAO,KAAa,EAAE;EAAW;EAAU,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI,EAC9E,IAAoB,MAAqB,KAAA;AAC/C,QACE,kBAAC,OAAD;EAAK,WAAW;YAAhB,CACG,MAAU,KAAA,KACT,kBAAC,OAAD;GAAK,WAAW,EAAE;aAAlB,CACG,IACC,kBAAC,UAAD;IACE,MAAK;IACL,WAAW,EAAE;IACb,SAAS;IACT,iBAAe,CAAC;cAJlB,CAME,kBAAC,QAAD;KAAM,WAAW,CAAC,EAAE,SAAS,KAAa,EAAE,iBAAiB,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;KAAE,eAAY;eAAO;KAAQ,CAAA,EACpH,kBAAC,QAAD;KAAM,WAAW,EAAE;eAAQ;KAAa,CAAA,CACjC;QAET,kBAAC,QAAD;IAAM,WAAW,EAAE;cAAQ;IAAa,CAAA,EAEzC,MAAW,KAAA,KACV,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,UAAD;IACE,MAAK;IACL,WAAW,EAAE;IACb,SAAS;IACT,cAAW;cACZ;IAEQ,CAAA,EACC,CAAA,EACZ,kBAAC,GAAD,EAAA,UAAS,cAAoB,CAAA,CACd,EAAA,CAAA,CAEf;MAEP,CAAC,KAAa,kBAAC,OAAD;GAAK,WAAW,EAAE;GAAO;GAAe,CAAA,CACnD"}