@weasel-js/ui 1.4.3 → 1.4.4

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 (53) hide show
  1. package/dist/chunks/Button-CExdkQcU.js +90 -0
  2. package/dist/chunks/{Button-Cq5LBUMD.js.map → Button-CExdkQcU.js.map} +1 -1
  3. package/dist/chunks/{ColorField-D0OxUlvu.js → ColorField-C20aRvCg.js} +2 -2
  4. package/dist/chunks/{ColorField-D0OxUlvu.js.map → ColorField-C20aRvCg.js.map} +1 -1
  5. package/dist/chunks/{DetentSlider-BVWKdRyC.js → DetentSlider-BpD7bgjN.js} +2 -2
  6. package/dist/chunks/{DetentSlider-BVWKdRyC.js.map → DetentSlider-BpD7bgjN.js.map} +1 -1
  7. package/dist/chunks/{GradientEditor-C5NVILLk.js → GradientEditor-BLpAtMDK.js} +3 -3
  8. package/dist/chunks/{GradientEditor-C5NVILLk.js.map → GradientEditor-BLpAtMDK.js.map} +1 -1
  9. package/dist/chunks/{InlineRange-50ulExCL.js → InlineRange-CcfzY80y.js} +2 -2
  10. package/dist/chunks/{InlineRange-50ulExCL.js.map → InlineRange-CcfzY80y.js.map} +1 -1
  11. package/dist/chunks/{PaintInput-67U-COE4.js → PaintInput-B7AScmHN.js} +3 -3
  12. package/dist/chunks/{PaintInput-67U-COE4.js.map → PaintInput-B7AScmHN.js.map} +1 -1
  13. package/dist/chunks/{Prefs-D0l0PXLF.js → Prefs-4JSEtuU-.js} +3 -3
  14. package/dist/chunks/{Prefs-D0l0PXLF.js.map → Prefs-4JSEtuU-.js.map} +1 -1
  15. package/dist/chunks/{Properties-CMLua9mU.js → Properties-BpUYKi7-.js} +125 -113
  16. package/dist/chunks/Properties-BpUYKi7-.js.map +1 -0
  17. package/dist/chunks/{RangeSlider-C3to8A52.js → RangeSlider-S2R3zA8N.js} +8 -8
  18. package/dist/chunks/RangeSlider-S2R3zA8N.js.map +1 -0
  19. package/dist/chunks/{SelectionPanel-BWXU8BqW.js → SelectionPanel-C59wavdA.js} +4 -4
  20. package/dist/chunks/{SelectionPanel-BWXU8BqW.js.map → SelectionPanel-C59wavdA.js.map} +1 -1
  21. package/dist/chunks/{Slider-Bk_G5sSW.js → Slider-Bs-KSYgQ.js} +14 -14
  22. package/dist/chunks/Slider-Bs-KSYgQ.js.map +1 -0
  23. package/dist/chunks/{Timeline-BmYwYjXd.js → Timeline-Bz806JbE.js} +2 -2
  24. package/dist/chunks/{Timeline-BmYwYjXd.js.map → Timeline-Bz806JbE.js.map} +1 -1
  25. package/dist/chunks/range.module-CvSfABmu.js +8 -0
  26. package/dist/chunks/range.module-CvSfABmu.js.map +1 -0
  27. package/dist/components/Button/Button.d.ts +5 -0
  28. package/dist/components/Button/Button.d.ts.map +1 -1
  29. package/dist/components/Button/index.js +1 -1
  30. package/dist/components/ColorField/index.js +1 -1
  31. package/dist/components/DetentSlider/index.js +1 -1
  32. package/dist/components/GradientEditor/index.js +1 -1
  33. package/dist/components/InlineRange/index.js +1 -1
  34. package/dist/components/PaintInput/index.js +1 -1
  35. package/dist/components/Prefs/index.js +1 -1
  36. package/dist/components/Properties/PropertyPanel.d.ts +27 -2
  37. package/dist/components/Properties/PropertyPanel.d.ts.map +1 -1
  38. package/dist/components/Properties/index.js +1 -1
  39. package/dist/components/RangeSlider/RangeSlider.d.ts +5 -0
  40. package/dist/components/RangeSlider/RangeSlider.d.ts.map +1 -1
  41. package/dist/components/RangeSlider/index.js +1 -1
  42. package/dist/components/SelectionPanel/index.js +1 -1
  43. package/dist/components/Slider/index.js +1 -1
  44. package/dist/components/Timeline/index.js +1 -1
  45. package/dist/index.js +12 -12
  46. package/dist/style.css +1 -1
  47. package/package.json +6 -6
  48. package/dist/chunks/Button-Cq5LBUMD.js +0 -87
  49. package/dist/chunks/Properties-CMLua9mU.js.map +0 -1
  50. package/dist/chunks/RangeSlider-C3to8A52.js.map +0 -1
  51. package/dist/chunks/Slider-Bk_G5sSW.js.map +0 -1
  52. package/dist/chunks/range.module-CgURq-Ua.js +0 -8
  53. package/dist/chunks/range.module-CgURq-Ua.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Prefs-D0l0PXLF.js","names":[],"sources":["../../src/components/paintValue.ts","../../src/components/Prefs/Prefs.module.css","../../src/components/Prefs/PrefsForm.tsx","../../src/components/Prefs/PrefsDialog.tsx"],"sourcesContent":["// Reading and writing the paint objects a property control may be pointed at.\n//\n// `data.fill` is a `FillStyle` and `data.stroke` a `Stroke`, so a control that\n// assumes either is a hex string reads `undefined` off it and writes a bare\n// string back over it. These helpers are what keep a control honest about the\n// shape it is holding.\n\n/** The color of a solid paint, or `undefined` for anything else — including a\n * value that isn't a paint at all. `fill` is optional on the solid member of\n * the union, so the tag alone can't decide it. */\nexport function solidColorOf(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const paint = value as { fill?: string; color?: unknown };\n if (paint.fill !== undefined && paint.fill !== 'solid') return undefined;\n return typeof paint.color === 'string' ? paint.color : undefined;\n}\n\n/** The single color a stroke shows, or `undefined` when it has none to show —\n * a gradient or pattern stroke, or no stroke at all. */\nexport function strokeColorOf(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null || !('paint' in value)) return undefined;\n return solidColorOf((value as { paint: unknown }).paint);\n}\n","/* Ported from WeaselDraw's wd-prefs-* rules (apps/draw/src/app.css),\n * generalized onto theme tokens. Column width is overridable via\n * --wzl-prefs-column-width at any ancestor scope. */\n\n.columns {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n align-items: flex-start;\n gap: 12px;\n overflow-y: auto;\n padding-right: 4px;\n}\n\n.column {\n flex: 0 0 var(--wzl-prefs-column-width, 300px);\n width: var(--wzl-prefs-column-width, 300px);\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.panel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 10px;\n background: var(--wzl-surface-sunken);\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n}\n\n.subpanel {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin-top: 6px;\n padding: 8px 8px 8px 10px;\n background: var(--wzl-surface-sunken);\n border-left: 2px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n}\n\n.groupHeader {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding-bottom: 4px;\n border-bottom: 1px solid var(--wzl-border);\n}\n\n.subpanel .groupHeader {\n border-bottom: none;\n padding-bottom: 2px;\n}\n\n.groupTitle {\n margin: 0;\n font-size: var(--wzl-font-size-sm);\n font-weight: var(--wzl-font-weight-bold);\n color: var(--wzl-fg);\n letter-spacing: 0.04em;\n text-transform: uppercase;\n}\n\n.subpanel .groupTitle {\n font-size: var(--wzl-font-size-sm);\n text-transform: none;\n letter-spacing: 0;\n}\n\n.groupDesc {\n margin: 0;\n font-size: var(--wzl-font-size-sm);\n color: var(--wzl-fg-subtle);\n line-height: 1.35;\n}\n\n.rows {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n font-size: var(--wzl-font-size-sm);\n color: var(--wzl-fg);\n cursor: default;\n}\n\n.rowLabel {\n flex: 1 1 auto;\n min-width: 0;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n/* Description affordance — quiet until hovered/focused. */\n.help {\n flex: 0 0 auto;\n padding: 0;\n border: none;\n background: none;\n color: var(--wzl-fg-subtle);\n font-size: var(--wzl-font-size-sm);\n line-height: 1;\n cursor: help;\n border-radius: var(--wzl-radius-sm);\n}\n\n.help:hover {\n color: var(--wzl-fg);\n}\n\n.help:focus-visible {\n outline: 2px solid var(--wzl-focus-ring);\n outline-offset: 1px;\n color: var(--wzl-fg);\n}\n\n/* Control slot — fixed width so the label can take the rest of the row.\n * Kit form controls render as block-level flex columns that fill it. */\n.rowControl {\n flex: 0 0 110px;\n min-width: 0;\n display: flex;\n align-items: center;\n}\n\n.textarea {\n width: 100%;\n padding: 4px 8px;\n background: var(--wzl-surface-sunken);\n color: var(--wzl-fg);\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n font: inherit;\n font-size: var(--wzl-font-size-sm);\n resize: vertical;\n}\n\n/* Names the unit a number leaf displays in, beside the field showing it. */\n.unitSuffix {\n flex: none;\n font-size: var(--wzl-font-size-sm);\n opacity: 0.6;\n}\n\n.unrenderable {\n font-family: var(--wzl-font-mono);\n font-size: var(--wzl-font-size-sm);\n color: var(--wzl-fg-subtle);\n}\n\n/* PrefsDialog header row: title text + optional headerExtra chrome. */\n.titleRow {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n}\n\n.titleRow > span:first-child {\n flex: 1;\n min-width: 0;\n}\n\n/* An object leaf renders its fields as its own rows. */\n.objectLeaf {\n display: flex;\n flex-direction: column;\n gap: 6px;\n inline-size: 100%;\n}\n\n.objectRow {\n display: grid;\n grid-template-columns: 96px 1fr;\n gap: 8px;\n align-items: center;\n}\n\n.objectLabel {\n font-size: var(--wzl-font-size-sm);\n opacity: 0.75;\n}\n\n.objectGroup {\n margin: 4px 0 0;\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","import { useMemo, type ReactNode } from 'react';\nimport { Focusable } from 'react-aria-components';\nimport { Checkbox } from '../Checkbox';\nimport { ColorField } from '../ColorField';\nimport { FontFamilySelect } from '../FontFamilySelect';\nimport { solidColorOf } from '../paintValue';\nimport { Input } from '../Input';\nimport { NumberField } from '../NumberField';\nimport { RadioGroup, Radio } from '../RadioGroup';\nimport { RangeSlider } from '../RangeSlider';\nimport { Select } from '../Select';\nimport { Switch } from '../Switch';\nimport { Tooltip, TooltipTrigger } from '../Tooltip';\nimport { isBuiltinToolPref } from '@weasel-js/core';\nimport {\n isPrefLeaf,\n prefDisplayBounds,\n prefValueAtPath,\n visiblePrefSubtree,\n type PrefGroup,\n type PrefLeaf,\n} from './schema';\nimport s from './Prefs.module.css';\n\n/** What a {@link PrefRenderer} is given for the leaf it is rendering. */\nexport interface PrefRenderContext {\n /** Dotted path of the leaf within the schema root. */\n path: string;\n /** The schema node. App renderers narrow this to their own kind shape. */\n pref: PrefLeaf;\n /** Current value — `values` at `path`, falling back to `pref.default`. */\n value: unknown;\n setValue: (value: unknown) => void;\n}\n\n/**\n * Renders the control cell for one preference leaf. Returning `null`\n * collapses the row.\n */\nexport type PrefRenderer = (ctx: PrefRenderContext) => ReactNode;\n\n/** Props for {@link PrefsForm}. */\nexport interface PrefsFormProps {\n /** Root of the schema tree. Core `ToolPrefGroup`s assign structurally. */\n schema: PrefGroup;\n /** Nested value tree (shape mirrors the schema). Sparse is fine —\n * missing leaves fall back to their schema `default`. */\n values?: unknown;\n /** Change callback with the leaf's dotted path. Values apply live;\n * there is no dirty/commit state. */\n onChange: (path: string, value: unknown) => void;\n /**\n * Per-kind renderers for app-defined kinds. Entries also override the\n * built-in kinds when keys collide. A renderer owns the control cell\n * (the label/tooltip row chrome stays with the form, unless the leaf\n * sets `block`); returning `null` collapses the row entirely. Unknown\n * kinds with no renderer show a labeled placeholder instead of\n * crashing.\n */\n renderers?: Record<string, PrefRenderer>;\n /** Reveal `hidden` leaves (dev tooling). Default false. */\n showHidden?: boolean;\n className?: string;\n}\n\n/**\n * Schema-driven preferences form. Top-level groups render as columns,\n * nested groups as indented sub-panels, leaves as label + control rows.\n * Storage-agnostic: pair with `PrefsDialog` for the modal composition,\n * and persist however the app likes via `onChange`.\n */\nexport function PrefsForm(props: PrefsFormProps) {\n const { schema, values, onChange, renderers, showHidden = false, className } = props;\n const visibleRoot = useMemo(\n () => visiblePrefSubtree(schema, showHidden),\n [schema, showHidden],\n );\n const ctx: WalkCtx = { values, onChange, renderers };\n return (\n <div className={[s.columns, className].filter(Boolean).join(' ')}>\n {Object.entries(visibleRoot?.children ?? {}).map(([key, child]) => (\n <div key={key} className={s.column}>\n {isPrefLeaf(child) ? (\n // Top-level leaves are unusual but legal — give each its own\n // column for symmetry with grouped siblings.\n <PrefRow ctx={ctx} path={key} pref={child} />\n ) : (\n <GroupBody ctx={ctx} group={child} path={key} depth={0} />\n )}\n </div>\n ))}\n </div>\n );\n}\n\ninterface WalkCtx {\n values: unknown;\n onChange: (path: string, value: unknown) => void;\n renderers?: Record<string, PrefRenderer>;\n}\n\nfunction GroupBody({ ctx, group, path, depth }: {\n ctx: WalkCtx;\n group: PrefGroup;\n path: string;\n depth: number;\n}) {\n return (\n <div className={depth === 0 ? s.panel : s.subpanel}>\n <div className={s.groupHeader}>\n <h3 className={s.groupTitle}>{group.name}</h3>\n {group.description !== undefined && (\n <p className={s.groupDesc}>{group.description}</p>\n )}\n </div>\n <div className={s.rows}>\n {Object.entries(group.children).map(([key, child]) => {\n const childPath = `${path}.${key}`;\n return isPrefLeaf(child) ? (\n <PrefRow key={key} ctx={ctx} path={childPath} pref={child} />\n ) : (\n <GroupBody key={key} ctx={ctx} group={child} path={childPath} depth={depth + 1} />\n );\n })}\n </div>\n </div>\n );\n}\n\nfunction PrefRow({ ctx, path, pref }: { ctx: WalkCtx; path: string; pref: PrefLeaf }) {\n const stored = prefValueAtPath(ctx.values, path);\n const renderCtx: PrefRenderContext = {\n path,\n pref,\n value: stored !== undefined ? stored : pref.default,\n setValue: (v) => ctx.onChange(path, v),\n };\n\n const custom = ctx.renderers?.[pref.kind];\n const control = custom ? custom(renderCtx) : renderBuiltin(renderCtx);\n if (custom && control === null) return null;\n\n // `block` leaves own their chrome (embedded editors with their own\n // header) — no label/tooltip row.\n if (pref.block) return <>{control}</>;\n\n return (\n <label className={s.row}>\n <span className={s.rowLabel}>\n {pref.name}\n {pref.description ? (\n // Help affordance carries the description tooltip. A tooltip\n // trigger must be interactive (keyboard-reachable), so this is\n // a real button — a bare label span would be neither focusable\n // nor announced.\n <TooltipTrigger>\n <Focusable>\n <button type=\"button\" className={s.help} aria-label={`About ${pref.name}`}>\n ⓘ\n </button>\n </Focusable>\n <Tooltip>{pref.description}</Tooltip>\n </TooltipTrigger>\n ) : null}\n </span>\n <span className={s.rowControl}>{control}</span>\n </label>\n );\n}\n\nfunction renderBuiltin(\n ctx: PrefRenderContext,\n // The object a nested leaf is a field of — what an enum `encoding` reads\n // and writes against. Undefined for a top-level leaf, which has none.\n siblings?: Record<string, unknown>,\n): ReactNode {\n const { pref, value, setValue } = ctx;\n if (pref.kind === 'font-family') {\n // Not a `ToolPref` kind: its options are the live font registry, which no\n // static schema can carry. Core's own text schema still declares it, so\n // the form ships the control rather than leaving every consumer to.\n //\n // The substitution probe runs at the weight and slant the family is stored\n // beside — fields of the same `TextStyle` object leaf — so the label names\n // the variant that will actually paint.\n const weight = siblings?.fontWeight;\n const slant = siblings?.fontStyle;\n return (\n <FontFamilySelect\n value={typeof value === 'string' ? value : undefined}\n onChange={setValue}\n weight={typeof weight === 'number' ? weight : undefined}\n fontStyle={slant === 'italic' ? 'italic' : undefined}\n aria-label={pref.name}\n />\n );\n }\n if (!isBuiltinToolPref(pref)) {\n // App-defined kind with no `renderers` entry: labeled placeholder, not a\n // crash — a missing wiring should be visible and recoverable.\n return <span className={s.unrenderable}>({pref.kind}: no renderer)</span>;\n }\n switch (pref.kind) {\n case 'boolean': {\n const checked = Boolean(value);\n return pref.control === 'switch' ? (\n <Switch isSelected={checked} onChange={setValue} aria-label={pref.name} />\n ) : (\n <Checkbox isSelected={checked} onChange={setValue} aria-label={pref.name} />\n );\n }\n case 'number': {\n const stored = typeof value === 'number' && Number.isFinite(value) ? value : 0;\n const unit = pref.unit;\n const display = unit ? unit.toDisplay(stored) : stored;\n // `min`/`max`/`step` are declared in the stored unit, like the value, so\n // they convert with it — a leaf storing radians and showing degrees was\n // clamping typed degrees against 0..6.28.\n const bounds = prefDisplayBounds(pref);\n const store = (v: number): void => setValue(unit ? unit.fromDisplay(v) : v);\n if (pref.control === 'slider') {\n return (\n <RangeSlider\n value={display}\n onChange={(v) => store(typeof v === 'number' ? v : v[0])}\n minValue={bounds.min}\n maxValue={bounds.max}\n step={bounds.step}\n aria-label={pref.name}\n />\n );\n }\n const field = (\n <NumberField\n value={display}\n onChange={store}\n minValue={bounds.min}\n maxValue={bounds.max}\n step={bounds.step}\n aria-label={pref.name}\n />\n );\n if (unit?.suffix === undefined) return field;\n return (\n <>\n {field}\n <span className={s.unitSuffix} aria-hidden=\"true\">{unit.suffix}</span>\n </>\n );\n }\n case 'string': {\n const text = typeof value === 'string' ? value : '';\n if (pref.control === 'textarea') {\n return (\n <textarea\n className={s.textarea}\n value={text}\n onChange={(e) => setValue(e.target.value)}\n aria-label={pref.name}\n rows={3}\n />\n );\n }\n return <Input value={text} onChange={setValue} aria-label={pref.name} />;\n }\n case 'enum': {\n const encoding = pref.encoding;\n // An encoded leaf stores something other than the option string (a dash\n // array), so reading the raw value selects nothing and writing one\n // replaces the stored form with the option string.\n const option = encoding\n ? encoding.read(value, siblings)\n : typeof value === 'string'\n ? value\n : pref.default;\n const choose = (next: string): void =>\n setValue(encoding ? encoding.write(next, siblings) : next);\n if (pref.control === 'radio') {\n return (\n <RadioGroup value={option ?? null} onChange={choose} aria-label={pref.name}>\n {pref.options.map((o) => (\n <Radio key={o.value} value={o.value} isDisabled={o.disabled}>\n {o.label}\n </Radio>\n ))}\n </RadioGroup>\n );\n }\n return (\n <Select<string>\n options={pref.options.map((o) => ({\n value: o.value,\n label: o.label,\n isDisabled: o.disabled,\n }))}\n selectedKey={option ?? null}\n onSelectionChange={choose}\n aria-label={pref.name}\n />\n );\n }\n case 'color': {\n const hex = typeof value === 'string' ? value : pref.default;\n return (\n <ColorField\n value={hex}\n alpha={pref.alpha}\n onChange={setValue}\n aria-label={pref.name}\n />\n );\n }\n case 'paint': {\n // A whole `FillStyle`. A gradient or pattern has no single color to\n // show, so the field goes blank rather than claiming one, and an edit\n // writes a whole solid union member rather than grafting a `color` key\n // onto the gradient.\n const solid = solidColorOf(value) ?? solidColorOf(pref.default);\n return (\n <ColorField\n value={solid}\n alpha={pref.alpha}\n onChange={(color) => setValue({ fill: 'solid', color })}\n aria-label={pref.name}\n />\n );\n }\n case 'object': {\n // One value with its fields hanging off it: each child renders its own\n // control and commits the parent object whole.\n const p = pref;\n const held = typeof value === 'object' && value !== null\n ? (value as Record<string, unknown>)\n : undefined;\n const objectRows = (children: Record<string, PrefLeaf | PrefGroup>): ReactNode[] => {\n const out: ReactNode[] = [];\n for (const [key, child] of Object.entries(children)) {\n if (!isPrefLeaf(child)) {\n const inner = objectRows(child.children);\n if (inner.length === 0) continue;\n out.push(<h4 key={`group:${key}`} className={s.objectGroup}>{child.name}</h4>, ...inner);\n continue;\n }\n out.push(\n <label key={key} className={s.objectRow}>\n <span className={s.objectLabel}>{child.name}</span>\n {renderBuiltin({\n path: `${ctx.path}.${key}`,\n pref: child,\n value: held?.[key],\n setValue: (v) => {\n const base = held ?? p.fromScalar?.(value) ?? {};\n setValue({ ...base, [key]: v });\n },\n }, held)}\n </label>,\n );\n }\n return out;\n };\n return <div className={s.objectLeaf}>{objectRows(p.children)}</div>;\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 row.\n const _exhaustive: never = pref;\n throw new Error(\n `PrefsForm: no control for built-in pref kind \"${(_exhaustive as { kind: string }).kind}\"`,\n );\n }\n }\n}\n","import type { ReactNode } from 'react';\nimport { Dialog } from '../Dialog';\nimport { PrefsForm, type PrefsFormProps } from './PrefsForm';\nimport s from './Prefs.module.css';\n\n/**\n * Props for {@link PrefsDialog} — everything {@link PrefsForm} takes, plus\n * the dialog's own open state and chrome.\n */\nexport interface PrefsDialogProps extends PrefsFormProps {\n isOpen: boolean;\n onOpenChange: (open: boolean) => void;\n /** Dialog heading. Defaults to the schema root's `name`. */\n title?: ReactNode;\n /** Extra chrome rendered inline with the title (e.g. a dev-mode\n * \"Show hidden\" switch). */\n headerExtra?: ReactNode;\n /** Class applied to the dialog's modal box. */\n dialogClassName?: string;\n}\n\n/**\n * The kit's preferences dialog: `Dialog` + `PrefsForm`. Changes apply\n * live through `onChange` — there is no OK/Cancel staging. For custom\n * placement (sidebar, popover, inline page), compose `PrefsForm`\n * directly; this wrapper is intentionally thin.\n */\nexport function PrefsDialog(props: PrefsDialogProps) {\n const { isOpen, onOpenChange, title, headerExtra, dialogClassName, ...form } = props;\n return (\n <Dialog\n isOpen={isOpen}\n onOpenChange={onOpenChange}\n aria-label={typeof title === 'string' ? title : form.schema.name}\n className={dialogClassName}\n title={\n headerExtra !== undefined ? (\n <span className={s.titleRow}>\n <span>{title ?? form.schema.name}</span>\n {headerExtra}\n </span>\n ) : (\n title ?? form.schema.name\n )\n }\n >\n <PrefsForm {...form} />\n </Dialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Prefs-4JSEtuU-.js","names":[],"sources":["../../src/components/paintValue.ts","../../src/components/Prefs/Prefs.module.css","../../src/components/Prefs/PrefsForm.tsx","../../src/components/Prefs/PrefsDialog.tsx"],"sourcesContent":["// Reading and writing the paint objects a property control may be pointed at.\n//\n// `data.fill` is a `FillStyle` and `data.stroke` a `Stroke`, so a control that\n// assumes either is a hex string reads `undefined` off it and writes a bare\n// string back over it. These helpers are what keep a control honest about the\n// shape it is holding.\n\n/** The color of a solid paint, or `undefined` for anything else — including a\n * value that isn't a paint at all. `fill` is optional on the solid member of\n * the union, so the tag alone can't decide it. */\nexport function solidColorOf(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const paint = value as { fill?: string; color?: unknown };\n if (paint.fill !== undefined && paint.fill !== 'solid') return undefined;\n return typeof paint.color === 'string' ? paint.color : undefined;\n}\n\n/** The single color a stroke shows, or `undefined` when it has none to show —\n * a gradient or pattern stroke, or no stroke at all. */\nexport function strokeColorOf(value: unknown): string | undefined {\n if (typeof value !== 'object' || value === null || !('paint' in value)) return undefined;\n return solidColorOf((value as { paint: unknown }).paint);\n}\n","/* Ported from WeaselDraw's wd-prefs-* rules (apps/draw/src/app.css),\n * generalized onto theme tokens. Column width is overridable via\n * --wzl-prefs-column-width at any ancestor scope. */\n\n.columns {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n align-items: flex-start;\n gap: 12px;\n overflow-y: auto;\n padding-right: 4px;\n}\n\n.column {\n flex: 0 0 var(--wzl-prefs-column-width, 300px);\n width: var(--wzl-prefs-column-width, 300px);\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.panel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 10px;\n background: var(--wzl-surface-sunken);\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n}\n\n.subpanel {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin-top: 6px;\n padding: 8px 8px 8px 10px;\n background: var(--wzl-surface-sunken);\n border-left: 2px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n}\n\n.groupHeader {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding-bottom: 4px;\n border-bottom: 1px solid var(--wzl-border);\n}\n\n.subpanel .groupHeader {\n border-bottom: none;\n padding-bottom: 2px;\n}\n\n.groupTitle {\n margin: 0;\n font-size: var(--wzl-font-size-sm);\n font-weight: var(--wzl-font-weight-bold);\n color: var(--wzl-fg);\n letter-spacing: 0.04em;\n text-transform: uppercase;\n}\n\n.subpanel .groupTitle {\n font-size: var(--wzl-font-size-sm);\n text-transform: none;\n letter-spacing: 0;\n}\n\n.groupDesc {\n margin: 0;\n font-size: var(--wzl-font-size-sm);\n color: var(--wzl-fg-subtle);\n line-height: 1.35;\n}\n\n.rows {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n font-size: var(--wzl-font-size-sm);\n color: var(--wzl-fg);\n cursor: default;\n}\n\n.rowLabel {\n flex: 1 1 auto;\n min-width: 0;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n/* Description affordance — quiet until hovered/focused. */\n.help {\n flex: 0 0 auto;\n padding: 0;\n border: none;\n background: none;\n color: var(--wzl-fg-subtle);\n font-size: var(--wzl-font-size-sm);\n line-height: 1;\n cursor: help;\n border-radius: var(--wzl-radius-sm);\n}\n\n.help:hover {\n color: var(--wzl-fg);\n}\n\n.help:focus-visible {\n outline: 2px solid var(--wzl-focus-ring);\n outline-offset: 1px;\n color: var(--wzl-fg);\n}\n\n/* Control slot — fixed width so the label can take the rest of the row.\n * Kit form controls render as block-level flex columns that fill it. */\n.rowControl {\n flex: 0 0 110px;\n min-width: 0;\n display: flex;\n align-items: center;\n}\n\n.textarea {\n width: 100%;\n padding: 4px 8px;\n background: var(--wzl-surface-sunken);\n color: var(--wzl-fg);\n border: 1px solid var(--wzl-border);\n border-radius: var(--wzl-radius-sm);\n font: inherit;\n font-size: var(--wzl-font-size-sm);\n resize: vertical;\n}\n\n/* Names the unit a number leaf displays in, beside the field showing it. */\n.unitSuffix {\n flex: none;\n font-size: var(--wzl-font-size-sm);\n opacity: 0.6;\n}\n\n.unrenderable {\n font-family: var(--wzl-font-mono);\n font-size: var(--wzl-font-size-sm);\n color: var(--wzl-fg-subtle);\n}\n\n/* PrefsDialog header row: title text + optional headerExtra chrome. */\n.titleRow {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n}\n\n.titleRow > span:first-child {\n flex: 1;\n min-width: 0;\n}\n\n/* An object leaf renders its fields as its own rows. */\n.objectLeaf {\n display: flex;\n flex-direction: column;\n gap: 6px;\n inline-size: 100%;\n}\n\n.objectRow {\n display: grid;\n grid-template-columns: 96px 1fr;\n gap: 8px;\n align-items: center;\n}\n\n.objectLabel {\n font-size: var(--wzl-font-size-sm);\n opacity: 0.75;\n}\n\n.objectGroup {\n margin: 4px 0 0;\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","import { useMemo, type ReactNode } from 'react';\nimport { Focusable } from 'react-aria-components';\nimport { Checkbox } from '../Checkbox';\nimport { ColorField } from '../ColorField';\nimport { FontFamilySelect } from '../FontFamilySelect';\nimport { solidColorOf } from '../paintValue';\nimport { Input } from '../Input';\nimport { NumberField } from '../NumberField';\nimport { RadioGroup, Radio } from '../RadioGroup';\nimport { RangeSlider } from '../RangeSlider';\nimport { Select } from '../Select';\nimport { Switch } from '../Switch';\nimport { Tooltip, TooltipTrigger } from '../Tooltip';\nimport { isBuiltinToolPref } from '@weasel-js/core';\nimport {\n isPrefLeaf,\n prefDisplayBounds,\n prefValueAtPath,\n visiblePrefSubtree,\n type PrefGroup,\n type PrefLeaf,\n} from './schema';\nimport s from './Prefs.module.css';\n\n/** What a {@link PrefRenderer} is given for the leaf it is rendering. */\nexport interface PrefRenderContext {\n /** Dotted path of the leaf within the schema root. */\n path: string;\n /** The schema node. App renderers narrow this to their own kind shape. */\n pref: PrefLeaf;\n /** Current value — `values` at `path`, falling back to `pref.default`. */\n value: unknown;\n setValue: (value: unknown) => void;\n}\n\n/**\n * Renders the control cell for one preference leaf. Returning `null`\n * collapses the row.\n */\nexport type PrefRenderer = (ctx: PrefRenderContext) => ReactNode;\n\n/** Props for {@link PrefsForm}. */\nexport interface PrefsFormProps {\n /** Root of the schema tree. Core `ToolPrefGroup`s assign structurally. */\n schema: PrefGroup;\n /** Nested value tree (shape mirrors the schema). Sparse is fine —\n * missing leaves fall back to their schema `default`. */\n values?: unknown;\n /** Change callback with the leaf's dotted path. Values apply live;\n * there is no dirty/commit state. */\n onChange: (path: string, value: unknown) => void;\n /**\n * Per-kind renderers for app-defined kinds. Entries also override the\n * built-in kinds when keys collide. A renderer owns the control cell\n * (the label/tooltip row chrome stays with the form, unless the leaf\n * sets `block`); returning `null` collapses the row entirely. Unknown\n * kinds with no renderer show a labeled placeholder instead of\n * crashing.\n */\n renderers?: Record<string, PrefRenderer>;\n /** Reveal `hidden` leaves (dev tooling). Default false. */\n showHidden?: boolean;\n className?: string;\n}\n\n/**\n * Schema-driven preferences form. Top-level groups render as columns,\n * nested groups as indented sub-panels, leaves as label + control rows.\n * Storage-agnostic: pair with `PrefsDialog` for the modal composition,\n * and persist however the app likes via `onChange`.\n */\nexport function PrefsForm(props: PrefsFormProps) {\n const { schema, values, onChange, renderers, showHidden = false, className } = props;\n const visibleRoot = useMemo(\n () => visiblePrefSubtree(schema, showHidden),\n [schema, showHidden],\n );\n const ctx: WalkCtx = { values, onChange, renderers };\n return (\n <div className={[s.columns, className].filter(Boolean).join(' ')}>\n {Object.entries(visibleRoot?.children ?? {}).map(([key, child]) => (\n <div key={key} className={s.column}>\n {isPrefLeaf(child) ? (\n // Top-level leaves are unusual but legal — give each its own\n // column for symmetry with grouped siblings.\n <PrefRow ctx={ctx} path={key} pref={child} />\n ) : (\n <GroupBody ctx={ctx} group={child} path={key} depth={0} />\n )}\n </div>\n ))}\n </div>\n );\n}\n\ninterface WalkCtx {\n values: unknown;\n onChange: (path: string, value: unknown) => void;\n renderers?: Record<string, PrefRenderer>;\n}\n\nfunction GroupBody({ ctx, group, path, depth }: {\n ctx: WalkCtx;\n group: PrefGroup;\n path: string;\n depth: number;\n}) {\n return (\n <div className={depth === 0 ? s.panel : s.subpanel}>\n <div className={s.groupHeader}>\n <h3 className={s.groupTitle}>{group.name}</h3>\n {group.description !== undefined && (\n <p className={s.groupDesc}>{group.description}</p>\n )}\n </div>\n <div className={s.rows}>\n {Object.entries(group.children).map(([key, child]) => {\n const childPath = `${path}.${key}`;\n return isPrefLeaf(child) ? (\n <PrefRow key={key} ctx={ctx} path={childPath} pref={child} />\n ) : (\n <GroupBody key={key} ctx={ctx} group={child} path={childPath} depth={depth + 1} />\n );\n })}\n </div>\n </div>\n );\n}\n\nfunction PrefRow({ ctx, path, pref }: { ctx: WalkCtx; path: string; pref: PrefLeaf }) {\n const stored = prefValueAtPath(ctx.values, path);\n const renderCtx: PrefRenderContext = {\n path,\n pref,\n value: stored !== undefined ? stored : pref.default,\n setValue: (v) => ctx.onChange(path, v),\n };\n\n const custom = ctx.renderers?.[pref.kind];\n const control = custom ? custom(renderCtx) : renderBuiltin(renderCtx);\n if (custom && control === null) return null;\n\n // `block` leaves own their chrome (embedded editors with their own\n // header) — no label/tooltip row.\n if (pref.block) return <>{control}</>;\n\n return (\n <label className={s.row}>\n <span className={s.rowLabel}>\n {pref.name}\n {pref.description ? (\n // Help affordance carries the description tooltip. A tooltip\n // trigger must be interactive (keyboard-reachable), so this is\n // a real button — a bare label span would be neither focusable\n // nor announced.\n <TooltipTrigger>\n <Focusable>\n <button type=\"button\" className={s.help} aria-label={`About ${pref.name}`}>\n ⓘ\n </button>\n </Focusable>\n <Tooltip>{pref.description}</Tooltip>\n </TooltipTrigger>\n ) : null}\n </span>\n <span className={s.rowControl}>{control}</span>\n </label>\n );\n}\n\nfunction renderBuiltin(\n ctx: PrefRenderContext,\n // The object a nested leaf is a field of — what an enum `encoding` reads\n // and writes against. Undefined for a top-level leaf, which has none.\n siblings?: Record<string, unknown>,\n): ReactNode {\n const { pref, value, setValue } = ctx;\n if (pref.kind === 'font-family') {\n // Not a `ToolPref` kind: its options are the live font registry, which no\n // static schema can carry. Core's own text schema still declares it, so\n // the form ships the control rather than leaving every consumer to.\n //\n // The substitution probe runs at the weight and slant the family is stored\n // beside — fields of the same `TextStyle` object leaf — so the label names\n // the variant that will actually paint.\n const weight = siblings?.fontWeight;\n const slant = siblings?.fontStyle;\n return (\n <FontFamilySelect\n value={typeof value === 'string' ? value : undefined}\n onChange={setValue}\n weight={typeof weight === 'number' ? weight : undefined}\n fontStyle={slant === 'italic' ? 'italic' : undefined}\n aria-label={pref.name}\n />\n );\n }\n if (!isBuiltinToolPref(pref)) {\n // App-defined kind with no `renderers` entry: labeled placeholder, not a\n // crash — a missing wiring should be visible and recoverable.\n return <span className={s.unrenderable}>({pref.kind}: no renderer)</span>;\n }\n switch (pref.kind) {\n case 'boolean': {\n const checked = Boolean(value);\n return pref.control === 'switch' ? (\n <Switch isSelected={checked} onChange={setValue} aria-label={pref.name} />\n ) : (\n <Checkbox isSelected={checked} onChange={setValue} aria-label={pref.name} />\n );\n }\n case 'number': {\n const stored = typeof value === 'number' && Number.isFinite(value) ? value : 0;\n const unit = pref.unit;\n const display = unit ? unit.toDisplay(stored) : stored;\n // `min`/`max`/`step` are declared in the stored unit, like the value, so\n // they convert with it — a leaf storing radians and showing degrees was\n // clamping typed degrees against 0..6.28.\n const bounds = prefDisplayBounds(pref);\n const store = (v: number): void => setValue(unit ? unit.fromDisplay(v) : v);\n if (pref.control === 'slider') {\n return (\n <RangeSlider\n value={display}\n onChange={(v) => store(typeof v === 'number' ? v : v[0])}\n minValue={bounds.min}\n maxValue={bounds.max}\n step={bounds.step}\n aria-label={pref.name}\n />\n );\n }\n const field = (\n <NumberField\n value={display}\n onChange={store}\n minValue={bounds.min}\n maxValue={bounds.max}\n step={bounds.step}\n aria-label={pref.name}\n />\n );\n if (unit?.suffix === undefined) return field;\n return (\n <>\n {field}\n <span className={s.unitSuffix} aria-hidden=\"true\">{unit.suffix}</span>\n </>\n );\n }\n case 'string': {\n const text = typeof value === 'string' ? value : '';\n if (pref.control === 'textarea') {\n return (\n <textarea\n className={s.textarea}\n value={text}\n onChange={(e) => setValue(e.target.value)}\n aria-label={pref.name}\n rows={3}\n />\n );\n }\n return <Input value={text} onChange={setValue} aria-label={pref.name} />;\n }\n case 'enum': {\n const encoding = pref.encoding;\n // An encoded leaf stores something other than the option string (a dash\n // array), so reading the raw value selects nothing and writing one\n // replaces the stored form with the option string.\n const option = encoding\n ? encoding.read(value, siblings)\n : typeof value === 'string'\n ? value\n : pref.default;\n const choose = (next: string): void =>\n setValue(encoding ? encoding.write(next, siblings) : next);\n if (pref.control === 'radio') {\n return (\n <RadioGroup value={option ?? null} onChange={choose} aria-label={pref.name}>\n {pref.options.map((o) => (\n <Radio key={o.value} value={o.value} isDisabled={o.disabled}>\n {o.label}\n </Radio>\n ))}\n </RadioGroup>\n );\n }\n return (\n <Select<string>\n options={pref.options.map((o) => ({\n value: o.value,\n label: o.label,\n isDisabled: o.disabled,\n }))}\n selectedKey={option ?? null}\n onSelectionChange={choose}\n aria-label={pref.name}\n />\n );\n }\n case 'color': {\n const hex = typeof value === 'string' ? value : pref.default;\n return (\n <ColorField\n value={hex}\n alpha={pref.alpha}\n onChange={setValue}\n aria-label={pref.name}\n />\n );\n }\n case 'paint': {\n // A whole `FillStyle`. A gradient or pattern has no single color to\n // show, so the field goes blank rather than claiming one, and an edit\n // writes a whole solid union member rather than grafting a `color` key\n // onto the gradient.\n const solid = solidColorOf(value) ?? solidColorOf(pref.default);\n return (\n <ColorField\n value={solid}\n alpha={pref.alpha}\n onChange={(color) => setValue({ fill: 'solid', color })}\n aria-label={pref.name}\n />\n );\n }\n case 'object': {\n // One value with its fields hanging off it: each child renders its own\n // control and commits the parent object whole.\n const p = pref;\n const held = typeof value === 'object' && value !== null\n ? (value as Record<string, unknown>)\n : undefined;\n const objectRows = (children: Record<string, PrefLeaf | PrefGroup>): ReactNode[] => {\n const out: ReactNode[] = [];\n for (const [key, child] of Object.entries(children)) {\n if (!isPrefLeaf(child)) {\n const inner = objectRows(child.children);\n if (inner.length === 0) continue;\n out.push(<h4 key={`group:${key}`} className={s.objectGroup}>{child.name}</h4>, ...inner);\n continue;\n }\n out.push(\n <label key={key} className={s.objectRow}>\n <span className={s.objectLabel}>{child.name}</span>\n {renderBuiltin({\n path: `${ctx.path}.${key}`,\n pref: child,\n value: held?.[key],\n setValue: (v) => {\n const base = held ?? p.fromScalar?.(value) ?? {};\n setValue({ ...base, [key]: v });\n },\n }, held)}\n </label>,\n );\n }\n return out;\n };\n return <div className={s.objectLeaf}>{objectRows(p.children)}</div>;\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 row.\n const _exhaustive: never = pref;\n throw new Error(\n `PrefsForm: no control for built-in pref kind \"${(_exhaustive as { kind: string }).kind}\"`,\n );\n }\n }\n}\n","import type { ReactNode } from 'react';\nimport { Dialog } from '../Dialog';\nimport { PrefsForm, type PrefsFormProps } from './PrefsForm';\nimport s from './Prefs.module.css';\n\n/**\n * Props for {@link PrefsDialog} — everything {@link PrefsForm} takes, plus\n * the dialog's own open state and chrome.\n */\nexport interface PrefsDialogProps extends PrefsFormProps {\n isOpen: boolean;\n onOpenChange: (open: boolean) => void;\n /** Dialog heading. Defaults to the schema root's `name`. */\n title?: ReactNode;\n /** Extra chrome rendered inline with the title (e.g. a dev-mode\n * \"Show hidden\" switch). */\n headerExtra?: ReactNode;\n /** Class applied to the dialog's modal box. */\n dialogClassName?: string;\n}\n\n/**\n * The kit's preferences dialog: `Dialog` + `PrefsForm`. Changes apply\n * live through `onChange` — there is no OK/Cancel staging. For custom\n * placement (sidebar, popover, inline page), compose `PrefsForm`\n * directly; this wrapper is intentionally thin.\n */\nexport function PrefsDialog(props: PrefsDialogProps) {\n const { isOpen, onOpenChange, title, headerExtra, dialogClassName, ...form } = props;\n return (\n <Dialog\n isOpen={isOpen}\n onOpenChange={onOpenChange}\n aria-label={typeof title === 'string' ? title : form.schema.name}\n className={dialogClassName}\n title={\n headerExtra !== undefined ? (\n <span className={s.titleRow}>\n <span>{title ?? form.schema.name}</span>\n {headerExtra}\n </span>\n ) : (\n title ?? form.schema.name\n )\n }\n >\n <PrefsForm {...form} />\n </Dialog>\n );\n}\n"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { t as e } from "./dlog-CMYiRF3Q.js";
2
2
  import { n as t, t as n } from "./Tooltip-BHre_vbI.js";
3
3
  import { n as r } from "./Disclosure-DiA6UKdM.js";
4
4
  import { i, n as a } from "./number-Bv1nbhOJ.js";
5
- import { t as o } from "./range.module-CgURq-Ua.js";
5
+ import { t as o } from "./range.module-CvSfABmu.js";
6
6
  import { n as s } from "./CurveEditor-DtjdpJVB.js";
7
7
  import { Fragment as c, jsx as l, jsxs as u } from "react/jsx-runtime";
8
8
  import { useCallback as d, useEffect as f, useId as p, useMemo as m, useRef as h, useState as g } from "react";
@@ -154,58 +154,58 @@ function y({ values: t, min: n, max: r, step: i, width: a, height: o = 110, defa
154
154
  });
155
155
  }
156
156
  var b = {
157
- panel: "_panel_1x92f_9",
158
- list: "_list_1x92f_3",
159
- row: "_row_1x92f_4",
160
- group: "_group_1x92f_4",
161
- subpanel: "_subpanel_1x92f_3",
162
- card: "_card_1x92f_14",
163
- cardList: "_cardList_1x92f_15",
164
- panelTitle: "_panelTitle_1x92f_42",
165
- listPairs: "_listPairs_1x92f_3",
166
- rowColor: "_rowColor_1x92f_4",
167
- span: "_span_1x92f_79",
168
- listOneUp: "_listOneUp_1x92f_87",
169
- densityTight: "_densityTight_1x92f_98",
170
- densityNormal: "_densityNormal_1x92f_110",
171
- densityRoomy: "_densityRoomy_1x92f_122",
172
- alignStart: "_alignStart_1x92f_144",
173
- alignCenter: "_alignCenter_1x92f_149",
174
- alignEnd: "_alignEnd_1x92f_154",
175
- alignBaseline: "_alignBaseline_1x92f_159",
176
- rowLabel: "_rowLabel_1x92f_186",
177
- readout: "_readout_1x92f_208",
178
- help: "_help_1x92f_220",
179
- readoutInput: "_readoutInput_1x92f_252",
180
- readoutGroup: "_readoutGroup_1x92f_290",
181
- readoutUnit: "_readoutUnit_1x92f_296",
182
- fieldUnitGroup: "_fieldUnitGroup_1x92f_303",
183
- rowInline: "_rowInline_1x92f_324",
184
- toggle: "_toggle_1x92f_342",
185
- alpha: "_alpha_1x92f_485",
186
- rowBlock: "_rowBlock_1x92f_494",
187
- rowCheckbox: "_rowCheckbox_1x92f_514",
188
- toggleButton: "_toggleButton_1x92f_557",
189
- toggleButtonSelected: "_toggleButtonSelected_1x92f_583",
190
- groupTitle: "_groupTitle_1x92f_602",
191
- groupHead: "_groupHead_1x92f_624",
192
- groupBody: "_groupBody_1x92f_4",
193
- groupPairs: "_groupPairs_1x92f_648",
194
- groupOneUp: "_groupOneUp_1x92f_661",
195
- subpanelTitle: "_subpanelTitle_1x92f_684",
196
- cardDragging: "_cardDragging_1x92f_728",
197
- cardExpanded: "_cardExpanded_1x92f_732",
198
- cardAccented: "_cardAccented_1x92f_736",
199
- cardHead: "_cardHead_1x92f_751",
200
- cardHandle: "_cardHandle_1x92f_772",
201
- cardIndexBadge: "_cardIndexBadge_1x92f_785",
202
- cardTitle: "_cardTitle_1x92f_803",
203
- cardPrimary: "_cardPrimary_1x92f_813",
204
- cardRemove: "_cardRemove_1x92f_828",
205
- cardBody: "_cardBody_1x92f_857",
206
- cardListWrap: "_cardListWrap_1x92f_869",
207
- cardDropHint: "_cardDropHint_1x92f_873",
208
- cardListEmpty: "_cardListEmpty_1x92f_881"
157
+ panel: "_panel_1628n_9",
158
+ list: "_list_1628n_3",
159
+ row: "_row_1628n_4",
160
+ group: "_group_1628n_4",
161
+ subpanel: "_subpanel_1628n_3",
162
+ card: "_card_1628n_14",
163
+ cardList: "_cardList_1628n_15",
164
+ panelTitle: "_panelTitle_1628n_42",
165
+ listPairs: "_listPairs_1628n_3",
166
+ rowColor: "_rowColor_1628n_4",
167
+ span: "_span_1628n_79",
168
+ listOneUp: "_listOneUp_1628n_87",
169
+ densityTight: "_densityTight_1628n_98",
170
+ densityNormal: "_densityNormal_1628n_110",
171
+ densityRoomy: "_densityRoomy_1628n_122",
172
+ alignStart: "_alignStart_1628n_144",
173
+ alignCenter: "_alignCenter_1628n_149",
174
+ alignEnd: "_alignEnd_1628n_154",
175
+ alignBaseline: "_alignBaseline_1628n_159",
176
+ rowLabel: "_rowLabel_1628n_186",
177
+ readout: "_readout_1628n_208",
178
+ help: "_help_1628n_220",
179
+ readoutInput: "_readoutInput_1628n_252",
180
+ readoutGroup: "_readoutGroup_1628n_293",
181
+ readoutUnit: "_readoutUnit_1628n_299",
182
+ fieldUnitGroup: "_fieldUnitGroup_1628n_306",
183
+ rowInline: "_rowInline_1628n_327",
184
+ toggle: "_toggle_1628n_345",
185
+ alpha: "_alpha_1628n_488",
186
+ rowBlock: "_rowBlock_1628n_497",
187
+ rowCheckbox: "_rowCheckbox_1628n_517",
188
+ toggleButton: "_toggleButton_1628n_560",
189
+ toggleButtonSelected: "_toggleButtonSelected_1628n_586",
190
+ groupTitle: "_groupTitle_1628n_605",
191
+ groupHead: "_groupHead_1628n_627",
192
+ groupBody: "_groupBody_1628n_4",
193
+ groupPairs: "_groupPairs_1628n_651",
194
+ groupOneUp: "_groupOneUp_1628n_664",
195
+ subpanelTitle: "_subpanelTitle_1628n_687",
196
+ cardDragging: "_cardDragging_1628n_731",
197
+ cardExpanded: "_cardExpanded_1628n_735",
198
+ cardAccented: "_cardAccented_1628n_739",
199
+ cardHead: "_cardHead_1628n_754",
200
+ cardHandle: "_cardHandle_1628n_775",
201
+ cardIndexBadge: "_cardIndexBadge_1628n_788",
202
+ cardTitle: "_cardTitle_1628n_806",
203
+ cardPrimary: "_cardPrimary_1628n_816",
204
+ cardRemove: "_cardRemove_1628n_831",
205
+ cardBody: "_cardBody_1628n_860",
206
+ cardListWrap: "_cardListWrap_1628n_872",
207
+ cardDropHint: "_cardDropHint_1628n_876",
208
+ cardListEmpty: "_cardListEmpty_1628n_884"
209
209
  }, x = {
210
210
  tight: b.densityTight,
211
211
  normal: b.densityNormal,
@@ -293,38 +293,42 @@ function O({ label: e, description: r }) {
293
293
  children: "ⓘ"
294
294
  }) }), /* @__PURE__ */ l(n, { children: r })] });
295
295
  }
296
- function k({ label: t, value: n, min: r, max: i, step: s = 1, onChange: c, onInput: u, format: d, unit: p, layout: m, description: g, span: _, density: v, align: y }) {
297
- let b = s >= 1 ? 0 : Math.min(6, Math.max(0, Math.ceil(-Math.log10(s)))), x = u ?? c, S = u ? c : void 0, C = h(null), w = h(S);
296
+ function k(e) {
297
+ let t = h(null), n = h(e);
298
298
  return f(() => {
299
- w.current = S;
299
+ n.current = e;
300
300
  }), f(() => {
301
- let e = C.current;
301
+ let e = t.current;
302
302
  if (!e) return;
303
- let t = () => w.current?.(Number(e.value));
304
- return e.addEventListener("change", t), () => e.removeEventListener("change", t);
305
- }, []), /* @__PURE__ */ l(D, {
306
- span: _,
303
+ let r = () => n.current?.(e.value);
304
+ return e.addEventListener("change", r), () => e.removeEventListener("change", r);
305
+ }, []), t;
306
+ }
307
+ function A({ label: t, value: n, min: r, max: i, step: s = 1, onChange: c, onInput: u, format: d, unit: f, layout: p, description: m, span: h, density: g, align: _ }) {
308
+ let v = s >= 1 ? 0 : Math.min(6, Math.max(0, Math.ceil(-Math.log10(s)))), y = u ?? c, b = u ? c : void 0, x = k(b && ((e) => b(Number(e))));
309
+ return /* @__PURE__ */ l(D, {
310
+ span: h,
307
311
  label: t,
308
- readout: /* @__PURE__ */ l(A, {
312
+ readout: /* @__PURE__ */ l(j, {
309
313
  value: n,
310
314
  min: r,
311
315
  max: i,
312
316
  format: d ?? ((e) => a(e, {
313
317
  useGrouping: !1,
314
- minimumFractionDigits: b,
315
- maximumFractionDigits: b
318
+ minimumFractionDigits: v,
319
+ maximumFractionDigits: v
316
320
  })),
317
- unit: p,
321
+ unit: f,
318
322
  onCommit: (e) => {
319
- x(e), S?.(e);
323
+ y(e), b?.(e);
320
324
  }
321
325
  }),
322
- layout: m,
323
- description: g,
324
- density: v,
325
- align: y,
326
+ layout: p,
327
+ description: m,
328
+ density: g,
329
+ align: _,
326
330
  children: /* @__PURE__ */ l("input", {
327
- ref: C,
331
+ ref: x,
328
332
  type: "range",
329
333
  className: o.range,
330
334
  tabIndex: -1,
@@ -337,12 +341,12 @@ function k({ label: t, value: n, min: r, max: i, step: s = 1, onChange: c, onInp
337
341
  e("property-panel", "slider", {
338
342
  label: t,
339
343
  value: r
340
- }), x(r);
344
+ }), y(r);
341
345
  }
342
346
  })
343
347
  });
344
348
  }
345
- function A({ value: e, min: t, max: n, format: r, unit: o, onCommit: s }) {
349
+ function j({ value: e, min: t, max: n, format: r, unit: o, onCommit: s }) {
346
350
  let [c, d] = g(null), f = (e) => r ? r(e) : a(e, {
347
351
  useGrouping: !1,
348
352
  maximumFractionDigits: 20
@@ -382,32 +386,35 @@ function A({ value: e, min: t, max: n, format: r, unit: o, onCommit: s }) {
382
386
  }), m]
383
387
  });
384
388
  }
385
- function j({ label: e, value: t, onChange: n, alpha: r, onAlphaChange: i, alphaDisabled: a, layout: s, description: c, span: d, density: f, align: p }) {
389
+ function M({ label: e, value: t, onChange: n, onInput: r, alpha: i, onAlphaChange: a, onAlphaInput: s, alphaDisabled: c, layout: d, description: f, span: p, density: m, align: h }) {
390
+ let g = i != null, _ = r ?? n, v = k(r && ((e) => n(e))), y = s ?? a, x = k(s && a && ((e) => a(Number(e))));
386
391
  return /* @__PURE__ */ u(D, {
387
- span: d,
392
+ span: p,
388
393
  label: e,
389
394
  variant: "color",
390
- layout: s,
391
- description: c,
392
- density: f,
393
- align: p,
395
+ layout: d,
396
+ description: f,
397
+ density: m,
398
+ align: h,
394
399
  children: [/* @__PURE__ */ l("input", {
400
+ ref: v,
395
401
  type: "color",
396
402
  value: t,
397
- onChange: (e) => n(e.target.value)
398
- }), r != null && /* @__PURE__ */ l("input", {
403
+ onChange: (e) => _(e.target.value)
404
+ }), g && /* @__PURE__ */ l("input", {
405
+ ref: x,
399
406
  type: "range",
400
407
  className: `${o.range} ${o.alpha} ${b.alpha}`,
401
408
  min: 0,
402
409
  max: 1,
403
410
  step: .01,
404
- value: r,
405
- disabled: a,
406
- onChange: (e) => i?.(Number(e.target.value))
411
+ value: i,
412
+ disabled: c,
413
+ onChange: (e) => y?.(Number(e.target.value))
407
414
  })]
408
415
  });
409
416
  }
410
- function M({ label: e, value: t, onChange: n, layout: r, span: i, description: a, density: o, align: s }) {
417
+ function N({ label: e, value: t, onChange: n, layout: r, span: i, description: a, density: o, align: s }) {
411
418
  return /* @__PURE__ */ l(D, {
412
419
  span: i,
413
420
  label: e,
@@ -423,7 +430,7 @@ function M({ label: e, value: t, onChange: n, layout: r, span: i, description: a
423
430
  })
424
431
  });
425
432
  }
426
- function N({ label: e, value: t, onChange: n, placeholder: r, maxLength: i, layout: a, description: o, span: s, density: c, align: u }) {
433
+ function P({ label: e, value: t, onChange: n, placeholder: r, maxLength: i, layout: a, description: o, span: s, density: c, align: u }) {
427
434
  return /* @__PURE__ */ l(D, {
428
435
  span: s,
429
436
  label: e,
@@ -440,38 +447,43 @@ function N({ label: e, value: t, onChange: n, placeholder: r, maxLength: i, layo
440
447
  })
441
448
  });
442
449
  }
443
- function P({ label: e, value: t, onChange: n, min: r, max: i, step: a, placeholder: o, unit: s, layout: c, description: d, span: f, density: p, align: m }) {
444
- let h = /* @__PURE__ */ l("input", {
450
+ function F({ label: e, value: t, onChange: n, onInput: r, min: i, max: a, step: o, placeholder: s, unit: c, layout: d, description: f, span: p, density: m, align: h }) {
451
+ let g = r ?? n, _ = /* @__PURE__ */ l("input", {
452
+ ref: k(r && ((e) => {
453
+ if (e === "") return;
454
+ let t = Number(e);
455
+ Number.isFinite(t) && n(t);
456
+ })),
445
457
  type: "number",
446
458
  value: t,
447
- min: r,
448
- max: i,
449
- step: a,
450
- placeholder: o,
459
+ min: i,
460
+ max: a,
461
+ step: o,
462
+ placeholder: s,
451
463
  onChange: (e) => {
452
464
  let t = e.target.value;
453
465
  if (t === "") return;
454
- let r = Number(t);
455
- Number.isFinite(r) && n(r);
466
+ let n = Number(t);
467
+ Number.isFinite(n) && g(n);
456
468
  }
457
469
  });
458
470
  return /* @__PURE__ */ l(D, {
459
- span: f,
471
+ span: p,
460
472
  label: e,
461
- layout: c,
462
- description: d,
463
- density: p,
464
- align: m,
465
- children: s == null ? h : /* @__PURE__ */ u("span", {
473
+ layout: d,
474
+ description: f,
475
+ density: m,
476
+ align: h,
477
+ children: c == null ? _ : /* @__PURE__ */ u("span", {
466
478
  className: b.fieldUnitGroup,
467
- children: [h, typeof s == "string" ? /* @__PURE__ */ l("span", {
479
+ children: [_, typeof c == "string" ? /* @__PURE__ */ l("span", {
468
480
  className: b.readoutUnit,
469
- children: s
470
- }) : s]
481
+ children: c
482
+ }) : c]
471
483
  })
472
484
  });
473
485
  }
474
- function F({ label: t, value: n, options: r, onChange: i, layout: a, description: o, span: s, density: c, align: u }) {
486
+ function I({ label: t, value: n, options: r, onChange: i, layout: a, description: o, span: s, density: c, align: u }) {
475
487
  return /* @__PURE__ */ l(D, {
476
488
  span: s,
477
489
  label: t,
@@ -495,7 +507,7 @@ function F({ label: t, value: n, options: r, onChange: i, layout: a, description
495
507
  })
496
508
  });
497
509
  }
498
- function I({ label: e, value: t, options: n, onChange: r, layout: i, description: a, span: o, density: s, align: c }) {
510
+ function L({ label: e, value: t, options: n, onChange: r, layout: i, description: a, span: o, density: s, align: c }) {
499
511
  return /* @__PURE__ */ l(D, {
500
512
  span: o,
501
513
  label: e,
@@ -520,7 +532,7 @@ function I({ label: e, value: t, options: n, onChange: r, layout: i, description
520
532
  }
521
533
  //#endregion
522
534
  //#region src/components/Properties/EffectCard.tsx
523
- function L({ title: e, children: t, className: n, density: r, align: i }) {
535
+ function R({ title: e, children: t, className: n, density: r, align: i }) {
524
536
  return /* @__PURE__ */ u("div", {
525
537
  className: C(b.subpanel, {
526
538
  density: r,
@@ -532,7 +544,7 @@ function L({ title: e, children: t, className: n, density: r, align: i }) {
532
544
  }), t]
533
545
  });
534
546
  }
535
- function R({ title: e, primary: t, accent: n, index: r, expanded: i = !0, onToggleExpanded: a, onRemove: o, draggable: s, onPressHandle: c, onReleaseHandle: d, onDragStart: f, onDragEnd: p, onDragOver: m, onDragLeave: h, onDrop: g, dragging: _, children: v, className: y }) {
547
+ function z({ title: e, primary: t, accent: n, index: r, expanded: i = !0, onToggleExpanded: a, onRemove: o, draggable: s, onPressHandle: c, onReleaseHandle: d, onDragStart: f, onDragEnd: p, onDragOver: m, onDragLeave: h, onDrop: g, dragging: _, children: v, className: y }) {
536
548
  let x = `${i ? ` ${b.cardExpanded}` : ""}${_ ? ` ${b.cardDragging}` : ""}${n ? ` ${b.cardAccented}` : ""}`, S = `${b.card}${x}${y ? ` ${y}` : ""}`, C = n ? { "--wzl-effect-card-accent": n } : void 0, w = a ? {
537
549
  role: "button",
538
550
  tabIndex: 0,
@@ -560,7 +572,7 @@ function R({ title: e, primary: t, accent: n, index: r, expanded: i = !0, onTogg
560
572
  title: "Drag to reorder",
561
573
  onMouseDown: c,
562
574
  onMouseUp: d,
563
- children: /* @__PURE__ */ l(z, {})
575
+ children: /* @__PURE__ */ l(B, {})
564
576
  }) : /* @__PURE__ */ l("button", {
565
577
  type: "button",
566
578
  className: `${b.cardIndexBadge} ${b.cardHandle}`,
@@ -598,7 +610,7 @@ function R({ title: e, primary: t, accent: n, index: r, expanded: i = !0, onTogg
598
610
  })]
599
611
  });
600
612
  }
601
- function z() {
613
+ function B() {
602
614
  return /* @__PURE__ */ u("svg", {
603
615
  width: "10",
604
616
  height: "14",
@@ -644,7 +656,7 @@ function z() {
644
656
  ]
645
657
  });
646
658
  }
647
- function B({ items: e, renderItem: t, onReorder: n, empty: r, defaultExpandedIds: i }) {
659
+ function V({ items: e, renderItem: t, onReorder: n, empty: r, defaultExpandedIds: i }) {
648
660
  let [a, o] = g(() => new Set(i ?? [])), [s, d] = g(null), [f, p] = g(null), [m, h] = g(null);
649
661
  return e.length === 0 ? /* @__PURE__ */ l(c, { children: r ?? /* @__PURE__ */ l("div", {
650
662
  className: b.cardListEmpty,
@@ -707,7 +719,7 @@ function B({ items: e, renderItem: t, onReorder: n, empty: r, defaultExpandedIds
707
719
  }
708
720
  //#endregion
709
721
  //#region src/components/Properties/PropertyGroup.tsx
710
- function V({ title: e, hidden: t, collapsible: n, defaultCollapsed: i, collapsed: a, onCollapsedChange: o, children: s, className: c, pack: d = "auto-color", density: f, align: m }) {
722
+ function H({ title: e, hidden: t, collapsible: n, defaultCollapsed: i, collapsed: a, onCollapsedChange: o, children: s, className: c, pack: d = "auto-color", density: f, align: m }) {
711
723
  let h = p(), [_, v] = g(i ?? !1);
712
724
  if (t) return null;
713
725
  let y = n ?? (i !== void 0 || a !== void 0 || o !== void 0), x = y && (a ?? _), S = () => {
@@ -741,6 +753,6 @@ function V({ title: e, hidden: t, collapsible: n, defaultCollapsed: i, collapsed
741
753
  });
742
754
  }
743
755
  //#endregion
744
- export { M as a, T as c, E as d, F as f, y as g, I as h, L as i, w as l, N as m, R as n, j as o, k as p, B as r, P as s, V as t, D as u };
756
+ export { N as a, T as c, E as d, I as f, y as g, L as h, R as i, w as l, P as m, z as n, M as o, A as p, V as r, F as s, H as t, D as u };
745
757
 
746
- //# sourceMappingURL=Properties-CMLua9mU.js.map
758
+ //# sourceMappingURL=Properties-BpUYKi7-.js.map