@kitlangton/knobs 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1270 -2
  3. package/dist/dev/chunk-e13znr7c.js +155 -0
  4. package/dist/dev/chunk-kdewv0mt.js +1334 -0
  5. package/dist/dev/chunk-ktsn42p7.js +15668 -0
  6. package/dist/dev/chunk-qtt698hp.js +1118 -0
  7. package/dist/dev/index.js +1389 -0
  8. package/dist/index.js +2367 -0
  9. package/dist/panel.js +23635 -0
  10. package/dist/react.js +326 -0
  11. package/dist/solid.js +336 -0
  12. package/dist/themes.js +235 -0
  13. package/dist/types/core/adapter.d.ts +21 -0
  14. package/dist/types/core/code.d.ts +3 -0
  15. package/dist/types/core/color.d.ts +27 -0
  16. package/dist/types/core/easing.d.ts +21 -0
  17. package/dist/types/core/internal.d.ts +18 -0
  18. package/dist/types/core/keys.d.ts +21 -0
  19. package/dist/types/core/mutate.d.ts +21 -0
  20. package/dist/types/core/ramp.d.ts +150 -0
  21. package/dist/types/core/random.d.ts +2 -0
  22. package/dist/types/core/readout.d.ts +73 -0
  23. package/dist/types/core/remote.d.ts +258 -0
  24. package/dist/types/core/schema.d.ts +526 -0
  25. package/dist/types/core/spring.d.ts +38 -0
  26. package/dist/types/core/storage.d.ts +8 -0
  27. package/dist/types/core/store.d.ts +474 -0
  28. package/dist/types/core/theme.d.ts +27 -0
  29. package/dist/types/core/variants.d.ts +17 -0
  30. package/dist/types/index.d.ts +13 -0
  31. package/dist/types/panel/attach.d.ts +5 -0
  32. package/dist/types/panel/choice.d.ts +56 -0
  33. package/dist/types/panel/die.d.ts +121 -0
  34. package/dist/types/panel/editors.d.ts +159 -0
  35. package/dist/types/panel/folds.d.ts +14 -0
  36. package/dist/types/panel/give.d.ts +73 -0
  37. package/dist/types/panel/gpu/atlas.d.ts +40 -0
  38. package/dist/types/panel/gpu/color.d.ts +128 -0
  39. package/dist/types/panel/gpu/die.wgsl.d.ts +1 -0
  40. package/dist/types/panel/gpu/renderer.d.ts +712 -0
  41. package/dist/types/panel/gpu/shader.wgsl.d.ts +2 -0
  42. package/dist/types/panel/haptics.d.ts +8 -0
  43. package/dist/types/panel/has.d.ts +14 -0
  44. package/dist/types/panel/hints.d.ts +45 -0
  45. package/dist/types/panel/icons.d.ts +30 -0
  46. package/dist/types/panel/index.d.ts +10 -0
  47. package/dist/types/panel/inspector.d.ts +20 -0
  48. package/dist/types/panel/measure.d.ts +6 -0
  49. package/dist/types/panel/menu.d.ts +38 -0
  50. package/dist/types/panel/morph.d.ts +38 -0
  51. package/dist/types/panel/motion.d.ts +77 -0
  52. package/dist/types/panel/page-hints.d.ts +82 -0
  53. package/dist/types/panel/page.d.ts +423 -0
  54. package/dist/types/panel/panel.d.ts +43 -0
  55. package/dist/types/panel/position.d.ts +5 -0
  56. package/dist/types/panel/prefs.d.ts +28 -0
  57. package/dist/types/panel/presets.d.ts +48 -0
  58. package/dist/types/panel/ramps.d.ts +14 -0
  59. package/dist/types/panel/readout-row.d.ts +21 -0
  60. package/dist/types/panel/reveal.d.ts +15 -0
  61. package/dist/types/panel/rolling-css.d.ts +2 -0
  62. package/dist/types/panel/search.d.ts +36 -0
  63. package/dist/types/panel/seg.d.ts +15 -0
  64. package/dist/types/panel/session.d.ts +21 -0
  65. package/dist/types/panel/slider.d.ts +92 -0
  66. package/dist/types/panel/smear.d.ts +7 -0
  67. package/dist/types/panel/spin.d.ts +13 -0
  68. package/dist/types/panel/styles.d.ts +11 -0
  69. package/dist/types/panel/swipe.d.ts +114 -0
  70. package/dist/types/panel/switcher.d.ts +20 -0
  71. package/dist/types/panel/tabs.d.ts +31 -0
  72. package/dist/types/panel/thumb.d.ts +36 -0
  73. package/dist/types/react/Knobs.d.ts +15 -0
  74. package/dist/types/react/VariantSwitcher.d.ts +10 -0
  75. package/dist/types/react/index.d.ts +3 -0
  76. package/dist/types/react/useControls.d.ts +11 -0
  77. package/dist/types/solid/index.d.ts +50 -0
  78. package/dist/types/themes/index.d.ts +1 -0
  79. package/dist/types/ui/bento-css.d.ts +1 -0
  80. package/dist/types/ui/bento-model.d.ts +96 -0
  81. package/dist/types/ui/bento.d.ts +77 -0
  82. package/dist/types/ui/commands.d.ts +11 -0
  83. package/dist/types/ui/confirm.d.ts +13 -0
  84. package/dist/types/ui/index.d.ts +20 -0
  85. package/dist/types/ui/layer.d.ts +70 -0
  86. package/dist/types/ui/look.d.ts +53 -0
  87. package/dist/types/ui/menu.d.ts +63 -0
  88. package/dist/types/ui/overlay-css.d.ts +1 -0
  89. package/dist/types/ui/overlay-model.d.ts +87 -0
  90. package/dist/types/ui/palette.d.ts +38 -0
  91. package/dist/types/ui/popover.d.ts +35 -0
  92. package/dist/types/ui/react.d.ts +45 -0
  93. package/dist/types/ui/readout-css.d.ts +1 -0
  94. package/dist/types/ui/readout-model.d.ts +137 -0
  95. package/dist/types/ui/readouts.d.ts +85 -0
  96. package/dist/types/ui/rgba.d.ts +6 -0
  97. package/dist/types/ui/solid.d.ts +40 -0
  98. package/dist/types/ui/split-model.d.ts +81 -0
  99. package/dist/types/ui/split.d.ts +75 -0
  100. package/dist/types/ui/timeline-model.d.ts +83 -0
  101. package/dist/types/ui/timeline.d.ts +76 -0
  102. package/dist/types/ui/toast.d.ts +19 -0
  103. package/dist/types/ui/tooltip.d.ts +19 -0
  104. package/dist/types/ui/tree-model.d.ts +125 -0
  105. package/dist/types/ui/tree.d.ts +95 -0
  106. package/dist/ui-react.js +6558 -0
  107. package/dist/ui-solid.js +6511 -0
  108. package/dist/ui.js +9238 -0
  109. package/package.json +99 -4
@@ -0,0 +1,474 @@
1
+ import { type CodeFormat } from "./code.js";
2
+ import type { TokenPreset } from "./theme.js";
3
+ import { type ReadoutInput, type ReadoutKeys, type Schema, type State, type Values } from "./schema.js";
4
+ import { type StoragePort } from "./storage.js";
5
+ import { type Variants } from "./variants.js";
6
+ export type ThemeMode = "dark" | "light" | "auto";
7
+ export type DensityMode = "compact" | "default" | "comfortable";
8
+ export type RendererMode = "gpu" | "dom" | "auto";
9
+ /** When a store's chip shows; see `ChipOptions.reveal`. */
10
+ export type RevealMode = "hover" | "shortcut" | "always";
11
+ /** The floating chip `attach` puts on an element, which grows into its panel. */
12
+ export interface ChipOptions {
13
+ /** Text beside the cog, e.g. `"knobs"`. Just the cog by default. */
14
+ readonly label?: string;
15
+ /**
16
+ * When the chip shows. `"hover"`: while the element is hovered or focused. `"shortcut"`: hidden
17
+ * until `shortcut` turns on knobs mode (remembered for the tab), then on hover. `"always"`:
18
+ * whenever its panel is closed. `"hover"` in development, `"shortcut"` in production builds.
19
+ */
20
+ readonly reveal?: RevealMode;
21
+ /** The key that toggles knobs mode for `"shortcut"` chips: `"Alt+Shift+D"` (⌥⇧D) by default. */
22
+ readonly shortcut?: string;
23
+ }
24
+ export type ChangeSource = "local" | "remote" | "reset" | "preset" | "variant" | "undo" | "redo" | "randomize" | "mutate" | "compare";
25
+ /**
26
+ * A key going into the hand and out of it: a drag or a press on its row, an inline edit, or a
27
+ * burst of arrow keys. Changes between a key's `start` and `end` are one gesture, so a host can
28
+ * open one undo entry at `start` and close it at `end`.
29
+ */
30
+ export interface GestureEvent {
31
+ readonly key: string;
32
+ readonly phase: "start" | "end";
33
+ }
34
+ export interface ChangeMeta {
35
+ readonly source: ChangeSource;
36
+ readonly coalesce?: boolean;
37
+ readonly key?: string;
38
+ readonly group?: string;
39
+ }
40
+ export interface ControlAction<V = State> {
41
+ readonly id?: string;
42
+ readonly label: string;
43
+ readonly group?: string;
44
+ readonly shortcut?: string;
45
+ run(values: V, controls: Controls<any>): void | string | Promise<void | string>;
46
+ }
47
+ /** One of a group's numbered presets; keys it leaves out sit at their defaults (see `groupPresets`). */
48
+ export interface GroupPreset<V = State> {
49
+ readonly name: string;
50
+ readonly values: Partial<V>;
51
+ }
52
+ /**
53
+ * How a page card shows its group presets: `names` as segments ("Native · Tight · Airy"), `menu`
54
+ * as a compact drop-down showing the selected one's name, `numbers` as 1, 2, 3…; `auto` (the
55
+ * default) shows names while they fit beside the title, else the menu.
56
+ */
57
+ export type GroupPresetDisplay = "auto" | "names" | "numbers" | "menu";
58
+ /** Display labels, kept apart from the ids that identify groups, sections and presets. */
59
+ export interface LabelOptions {
60
+ /** By group id: what its header shows (verbatim). Ids stay identity everywhere else. */
61
+ readonly groupLabels?: Readonly<Record<string, string>>;
62
+ /** By section id: what its heading shows. */
63
+ readonly sectionLabels?: Readonly<Record<string, string>>;
64
+ /** By preset id: what the preset strip shows, e.g. `{ default: "Native" }`. */
65
+ readonly presetLabels?: Readonly<Record<string, string>>;
66
+ /** Shorthand for `presetLabels.default`. */
67
+ readonly defaultLabel?: string;
68
+ /** How a page card shows its group presets (see `GroupPresetDisplay`). */
69
+ readonly groupPresetDisplay?: GroupPresetDisplay;
70
+ /** By group id: one muted line under its card's title on a page, over fields' `groupDescription`. */
71
+ readonly groupDescriptions?: Readonly<Record<string, string>>;
72
+ /** By section id: one muted line under its heading on a page, over fields' `sectionDescription`. */
73
+ readonly sectionDescriptions?: Readonly<Record<string, string>>;
74
+ }
75
+ /**
76
+ * How a page's sections start: `true` folds every section to its heading, or by section id. A
77
+ * section neither names keeps its fields' `sectionFolded`, else starts open.
78
+ */
79
+ export type SectionFolded = boolean | Readonly<Record<string, boolean>>;
80
+ /** Panel commands with a key binding; see `MountPanelOptions.keys`. */
81
+ export type PanelCommand = "random" | "mutate" | "reset" | "resetAll" | "compare" | "hint" | "undo" | "redo" | "copy" | "save" | "lock" | "presets" | "group" | "first" | "help" | "menu" | "leave";
82
+ /**
83
+ * Remaps (a single character, or a key name such as `"F2"`) or turns off (`false`) a panel's key
84
+ * bindings. Unlisted commands keep their default key. `true` keeps the default.
85
+ */
86
+ export type PanelKeys = {
87
+ readonly [C in PanelCommand]?: string | boolean;
88
+ } & {
89
+ /** `false` drops the j/k and h/l aliases: only the arrows move and adjust; the letters reach the page. */
90
+ readonly vim?: boolean;
91
+ /**
92
+ * `false` stops digits, `.`, `-` and Enter from opening a slider's inline number editor (they
93
+ * reach the page); clicking the readout still edits.
94
+ */
95
+ readonly edit?: boolean;
96
+ /** What ⇧ multiplies an arrow's step by; 10 by default. */
97
+ readonly bigStep?: number;
98
+ /** `false` stops ↑/↓ at the first and last rows instead of wrapping around. */
99
+ readonly wrap?: boolean;
100
+ };
101
+ /** A header button (see `MountPanelOptions.header`). */
102
+ export type HeaderButton = "random" | "reset" | "copy" | "undo" | "redo" | "help" | "close";
103
+ export interface HeaderOptions {
104
+ /** `false` hides the title; the buttons stay at the right. */
105
+ readonly title?: boolean;
106
+ /**
107
+ * Which buttons show (of those that are on). All but `help` by default: its `?` key opens the
108
+ * shortcuts either way, and the title's tooltip says so. List `"help"` to bring the button back.
109
+ */
110
+ readonly buttons?: readonly HeaderButton[];
111
+ }
112
+ export interface MountPanelOptions extends LabelOptions {
113
+ readonly title?: string | false;
114
+ /**
115
+ * Key bindings. An inline panel only ever hears keys while focus is inside it; pass this to remap
116
+ * or turn off commands so they don't shadow the host's own keys, e.g.
117
+ * `{ random: false, mutate: false, compare: false, hint: false, undo: false }` or `{ hint: "f" }`.
118
+ * The `?` popover and hint badges list only what's enabled.
119
+ */
120
+ readonly keys?: PanelKeys;
121
+ /**
122
+ * Hint mode's reach. `"page"` (the default): `s` labels every visible control in every panel
123
+ * and knobs/ui piece on the page (and the host's `data-hint` elements), so pressed here or
124
+ * anywhere outside a text field it jumps across all of them; with nothing else on the page it's
125
+ * this panel's alone, as always. `"panel"`: `s` here labels only this panel's controls, and they
126
+ * don't join the page's. `keys: { hint: false }` turns hint mode off here either way.
127
+ */
128
+ readonly hints?: "page" | "panel";
129
+ /**
130
+ * `false` hides what says a value differs from its default: modified dots and readout marks,
131
+ * group reset icons and the header's Reset. For panels whose values belong to someone else
132
+ * (a prototype's settings), where "default" means nothing. The store's `marks` by default.
133
+ */
134
+ readonly marks?: boolean;
135
+ /** Token values to style the panel with, e.g. `problemsPreset` (see `TokenPreset`). */
136
+ readonly preset?: TokenPreset;
137
+ /**
138
+ * `false` drops the header (title and buttons) for a host that draws its own. An object keeps
139
+ * it and chooses what shows: `{ title: false, buttons: ["random", "undo", "redo"] }`. A button
140
+ * left out keeps its key; a command that's off (`keys`) loses its button either way. Without a
141
+ * list, every button but `help` shows (see `HeaderOptions`).
142
+ */
143
+ readonly header?: boolean | HeaderOptions;
144
+ /**
145
+ * `false`: no per-tab session, so a rebuilt panel doesn't take focus back to the row it last
146
+ * had or reopen editors and folded groups. On while the store has storage.
147
+ */
148
+ readonly session?: boolean;
149
+ /** Groups folded (`true`) or open (`false`) at first, over their fields' `folded`. */
150
+ readonly groupFolded?: Readonly<Record<string, boolean>>;
151
+ /** Sub-headings (fields' `subsection`) that start folded (`true`), by name. */
152
+ readonly subsectionFolded?: Readonly<Record<string, boolean>>;
153
+ /**
154
+ * The tab strip's order and labels: tab ids, or `{ id, label }`. Tabs the fields name that
155
+ * aren't listed follow in the order they first appear.
156
+ */
157
+ readonly tabs?: readonly (string | {
158
+ readonly id: string;
159
+ readonly label?: string;
160
+ })[];
161
+ /** The tab shown first; otherwise the one last shown for these controls, else the first. */
162
+ readonly tab?: string;
163
+ /** Called when the shown tab changes (by a click, a key or `panel.tab()`). */
164
+ readonly onTab?: (tab: string) => void;
165
+ /**
166
+ * A search field under the header that filters rows by label, key or group as you type, with
167
+ * filter chips (`"modified"`, with its count, by default). `/` focuses it.
168
+ */
169
+ readonly search?: boolean | SearchOptions;
170
+ /**
171
+ * A group's Random (its die, `g`, ⌥ for Mutate) asks the host first: returning `true` means the
172
+ * host rolled the group itself (its own seeded "Randomize <group>"), and the panel only tumbles
173
+ * the die.
174
+ */
175
+ readonly groupRandom?: (group: string, mode: "random" | "mutate") => boolean | void;
176
+ /**
177
+ * A node for a row's accessory slot, just after its label (a pin, a marker), or null. Called
178
+ * with the row's key when the row is built; `panel.updateAccessories()` asks again. The node
179
+ * stays the host's: style it, listen on it. With the WebGPU renderer it stays DOM (see README).
180
+ */
181
+ readonly rowAccessory?: (key: string) => Node | null;
182
+ /**
183
+ * Light haptic ticks on phones (a stepped slider crossing a stop, the default's magnet, a
184
+ * choice changing mid-pan, a switch flipping); on by default, `false` turns them off.
185
+ * Reduced motion turns them off too.
186
+ */
187
+ readonly haptics?: boolean;
188
+ /** Called once the panel is mounted, with a handle to focus it (a host's "Tune" key). */
189
+ readonly onReady?: (panel: PanelHandle) => void;
190
+ /** What Copy (and ⇧C) puts on the clipboard; `controls.code()` by default. */
191
+ readonly copy?: () => string;
192
+ readonly theme?: ThemeMode;
193
+ readonly density?: DensityMode;
194
+ readonly renderer?: RendererMode;
195
+ readonly variants?: boolean;
196
+ readonly history?: boolean;
197
+ readonly footer?: boolean;
198
+ readonly actions?: readonly ControlAction<any>[];
199
+ readonly onClose?: () => void;
200
+ }
201
+ /** A search field's filter chip: it narrows the rows to those `test` accepts. */
202
+ export interface SearchChip {
203
+ readonly id: string;
204
+ readonly label: string;
205
+ /** Shows how many rows it would keep beside its label. */
206
+ readonly count?: boolean;
207
+ test(key: string, controls: Controls<any>): boolean;
208
+ }
209
+ export interface SearchOptions {
210
+ readonly placeholder?: string;
211
+ /** `"modified"` (rows off their defaults) or chips of your own; `["modified"]` by default. */
212
+ readonly chips?: readonly ("modified" | SearchChip)[];
213
+ }
214
+ /**
215
+ * An undo stack the host owns (an editor's ⌘Z that covers more than these values). The panel's
216
+ * undo and redo buttons and keys call it; compare (hold `\`) still previews knobs' own last entry.
217
+ */
218
+ export interface HostHistory {
219
+ undo(): void;
220
+ redo(): void;
221
+ canUndo(): boolean;
222
+ canRedo(): boolean;
223
+ /** Called when `canUndo` or `canRedo` may have changed; returns an unsubscribe. */
224
+ subscribe?(cb: () => void): () => void;
225
+ }
226
+ /**
227
+ * What `storage` keeps: `"all"` (values, locks and saved variants; the default), `"variants"`
228
+ * (only saved variants, for values the host owns) or `"none"`.
229
+ */
230
+ export type PersistMode = "all" | "variants" | "none";
231
+ export interface SyncOptions {
232
+ /** Keys missing from `values` return to their defaults. */
233
+ readonly replace?: boolean;
234
+ /**
235
+ * Keys whose values differ across what the host is showing (a multi-selection): they show an
236
+ * indeterminate "—" until edited. Replaces the store's mixed keys (see `Controls.setMixed`).
237
+ */
238
+ readonly mixed?: readonly string[];
239
+ /**
240
+ * A different item arrived: panels place the values without springing or rolling, and undo
241
+ * history starts over (it belonged to the previous item).
242
+ */
243
+ readonly land?: boolean;
244
+ }
245
+ /** A mounted panel: what `mount` hands `onReady`. */
246
+ export interface PanelHandle {
247
+ readonly element: HTMLElement;
248
+ focus(): void;
249
+ dispose(): void;
250
+ }
251
+ /**
252
+ * The panel module a shipped store mounts with: `() => import("knobs/panel")`. Structural, so the
253
+ * core never imports the panel itself.
254
+ */
255
+ export interface PanelModule {
256
+ mountPanel(controls: Controls<any>, container: HTMLElement, options?: MountPanelOptions): PanelHandle;
257
+ attach(controls: Controls<any>, element: HTMLElement, chip?: ChipOptions): () => void;
258
+ }
259
+ export interface ControlsOptions<S extends Schema> extends LabelOptions {
260
+ enabled?: boolean | (() => boolean);
261
+ /**
262
+ * Ships this store as product UI: it stays live in production builds (where stores are
263
+ * otherwise locked to their defaults) and mounts panels from the module this loads, e.g.
264
+ * `ship: () => import("knobs/panel")`. `enabled` defaults to true. Stores without it stay
265
+ * dev-only and ship constants.
266
+ */
267
+ ship?: () => Promise<PanelModule>;
268
+ /** The chip `attach` shows: its label, when it shows and the knobs-mode key (see `ChipOptions`). */
269
+ chip?: ChipOptions;
270
+ /**
271
+ * The longest (ms) a key changed here outranks incoming values for it (see `sync`), so an echo
272
+ * of an older value still in flight can't pull it back. The hold ends early once the remote
273
+ * reports the value that was sent (for numbers, within half a step). 0 by default; remote
274
+ * controls use 600. Raise it for peers that acknowledge slowly.
275
+ */
276
+ echo?: number;
277
+ storage?: StoragePort | null;
278
+ /** What `storage` keeps: everything, only saved variants, or nothing (see `PersistMode`). */
279
+ persist?: PersistMode;
280
+ /**
281
+ * The source Random and Mutate draw from, `[0, 1)` like `Math.random` (the default). Pass a
282
+ * seeded generator to make a roll reproducible.
283
+ */
284
+ random?: () => number;
285
+ /**
286
+ * Whether Random and Mutate may change a key, beside locks and each field's `random: false`
287
+ * (e.g. never seeds or structural pickers).
288
+ */
289
+ tunable?: (key: keyof S & string, values: Values<S>) => boolean;
290
+ /** An undo stack the host owns; undo and redo call it (see `HostHistory`). */
291
+ history?: HostHistory;
292
+ presets?: Readonly<Record<string, Partial<Values<S>>>>;
293
+ /**
294
+ * Numbered presets per group, shown as 1, 2, 3… on the group's card in a page. A group's
295
+ * presets cover every key any of them names; keys a preset leaves out are at their defaults,
296
+ * so `{ name: "Today", values: {} }` is the shipped look.
297
+ */
298
+ groupPresets?: Readonly<Record<string, readonly GroupPreset<Values<S>>[]>>;
299
+ actions?: readonly ControlAction<Values<S>>[];
300
+ theme?: ThemeMode;
301
+ density?: DensityMode;
302
+ renderer?: RendererMode;
303
+ /** Default key bindings for this store's panels (see `MountPanelOptions.keys`). */
304
+ keys?: PanelKeys;
305
+ /** `false`: its panels show no modified marks or resets (see `MountPanelOptions.marks`). */
306
+ marks?: boolean;
307
+ /** How its groups start, folded or open, under the mount's `groupFolded` (see there). */
308
+ groupFolded?: Readonly<Record<string, boolean>>;
309
+ /** How a page's sections start, under the mount's `sectionFolded` (see `SectionFolded`). */
310
+ sectionFolded?: SectionFolded;
311
+ onChange?: (patch: Partial<Values<S>>, values: Values<S>, meta: ChangeMeta) => void;
312
+ /** A key went into the hand or out of it (see `GestureEvent`). */
313
+ onGesture?: (event: GestureEvent) => void;
314
+ onReset?: (target: {
315
+ key?: keyof S & string;
316
+ group?: string;
317
+ }) => void;
318
+ /** An action row was pressed (after its own `run`), or a toolbar button (with its item's id). */
319
+ onAction?: (key: string) => void;
320
+ /** A group's numbered preset was applied (after its values are set). */
321
+ onPreset?: (target: {
322
+ group: string;
323
+ index: number;
324
+ name: string;
325
+ }) => void;
326
+ }
327
+ export interface Controls<S extends Schema = Schema> {
328
+ readonly id: string;
329
+ readonly schema: S;
330
+ readonly locked: Values<S>;
331
+ readonly enabled: boolean;
332
+ readonly storage: StoragePort | null;
333
+ /** What `storage` keeps (see `PersistMode`). */
334
+ readonly persist: PersistMode;
335
+ readonly presets: Readonly<Record<string, Values<S>>>;
336
+ readonly variants: Variants<Values<S>>;
337
+ readonly actions: readonly ControlAction<Values<S>>[];
338
+ readonly theme: ThemeMode;
339
+ readonly density: DensityMode;
340
+ readonly renderer: RendererMode;
341
+ readonly keys: PanelKeys | undefined;
342
+ readonly marks: boolean;
343
+ /** The chip's options, with `reveal` and `shortcut` resolved. */
344
+ readonly chip: ChipOptions & {
345
+ readonly reveal: RevealMode;
346
+ readonly shortcut: string;
347
+ };
348
+ /** How page cards show group presets (`groupPresetDisplay`, `auto` by default). */
349
+ readonly groupPresetDisplay: GroupPresetDisplay;
350
+ /** A group's display label (`groupLabels`, else its fields' `groupLabel`), if it has one. */
351
+ groupLabel(group: string): string | undefined;
352
+ /** A section's display label (`sectionLabels`, else its fields' `sectionLabel`), if it has one. */
353
+ sectionLabel(section: string): string | undefined;
354
+ /** A group's description (`groupDescriptions`, else its fields' `groupDescription`), if any. */
355
+ groupDescription(group: string): string | undefined;
356
+ /** A section's description (`sectionDescriptions`, else its fields' `sectionDescription`), if any. */
357
+ sectionDescription(section: string): string | undefined;
358
+ /** How groups start (`groupFolded`), as given; panels read it under their own `groupFolded`. */
359
+ readonly groupFolded: Readonly<Record<string, boolean>> | undefined;
360
+ /** How a page's sections start (`sectionFolded`), as given. */
361
+ readonly sectionFolded: SectionFolded | undefined;
362
+ /** A preset's display label (`presetLabels`, `defaultLabel`), else its id. */
363
+ presetLabel(id: string): string;
364
+ readonly error: string | null;
365
+ get(): Values<S>;
366
+ getServerSnapshot(): Values<S>;
367
+ /** Apply a change. `coalesce` folds it into the previous history entry (used while dragging). */
368
+ set(patch: Partial<Values<S>>, options?: {
369
+ coalesce?: boolean;
370
+ source?: ChangeSource;
371
+ key?: string;
372
+ group?: string;
373
+ }): void;
374
+ /**
375
+ * Apply external state (props, a server, a peer) without recording undo history or emitting a
376
+ * local change. A key that is held (in hand, or changed here within `echo` ms) keeps its value;
377
+ * the newest incoming value for it lands when the hold ends.
378
+ */
379
+ sync(values: Partial<Values<S>>, options?: SyncOptions): void;
380
+ /**
381
+ * Marks a key as in hand (a drag, an inline edit) or lets it go. While held, `sync` doesn't
382
+ * touch it; letting go applies the newest value that arrived meanwhile. Panels call this.
383
+ */
384
+ hold(key: string, held?: boolean): void;
385
+ /** Whether incoming values for `key` are held back right now (see `sync`). */
386
+ isHeld(key: string): boolean;
387
+ /**
388
+ * Presses an action row: its `run`, then `onAction(key)`. For a toolbar, `item` names the
389
+ * button (its id, else its label) and `onAction` gets the item's id.
390
+ */
391
+ action(key: keyof S & string, item?: string): void;
392
+ /**
393
+ * Shows a live value in a readout row (`watch`, `sparkline`, `graph`, `meter`, `table`, `log`):
394
+ * a number or text, a sample for a history, `{ [series]: number }` for a graph, rows for a
395
+ * table, an entry for a log. `null` clears it. Rows redraw at most once a frame and sleep
396
+ * when nothing arrives; nothing is recorded in the values, history or storage.
397
+ */
398
+ watch<K extends ReadoutKeys<S>>(key: K, value: ReadoutInput<S[K]>): void;
399
+ /** The latest value pushed to a readout (`undefined` before any). */
400
+ watched(key: ReadoutKeys<S>): unknown;
401
+ /** True while the change being notified landed (`sync(…, { land: true })`): place, don't animate. */
402
+ readonly landing: boolean;
403
+ undo(): void;
404
+ redo(): void;
405
+ readonly canUndo: boolean;
406
+ readonly canRedo: boolean;
407
+ subscribe(cb: () => void): () => void;
408
+ /**
409
+ * Back to the defaults: every field, or only `keys`. Locks don't apply; disabled and read-only
410
+ * fields keep their values.
411
+ */
412
+ reset(keys?: readonly (keyof S & string)[]): void;
413
+ resetKey(key: keyof S & string): void;
414
+ resetGroup(group: string): void;
415
+ isModified(key?: keyof S & string): boolean;
416
+ modified(): Partial<Values<S>>;
417
+ /**
418
+ * Random values for every field that can roll (see `canRoll`), or those among `keys`, drawn
419
+ * from `random`. One undo step.
420
+ */
421
+ randomize(keys?: readonly (keyof S & string)[]): void;
422
+ /** Small changes around the current values instead of a re-roll; skips what can't roll. */
423
+ mutate(keys?: readonly (keyof S & string)[]): void;
424
+ /** Keeps fields out of Random and Mutate (or lets them back in). Saved with the values. */
425
+ lock(keys: (keyof S & string) | readonly (keyof S & string)[], locked?: boolean): void;
426
+ isLocked(key: keyof S & string): boolean;
427
+ /**
428
+ * Whether Random and Mutate may change `key`: it holds a value and isn't locked, `random:
429
+ * false`, refused by `tunable`, disabled or read-only.
430
+ */
431
+ canRoll(key: keyof S & string): boolean;
432
+ /** Whether the field is disabled now (`disabled`, static or from the values). */
433
+ isDisabled(key: keyof S & string): boolean;
434
+ /** Whether the field is read-only now (`readOnly`, static or from the values). */
435
+ isReadOnly(key: keyof S & string): boolean;
436
+ /**
437
+ * Asks panels to read again what they derive without new values: function `disabled` and
438
+ * `readOnly` flags (which may read state elsewhere, another store's say) and row accessories.
439
+ */
440
+ refresh(): void;
441
+ /**
442
+ * Marks keys as mixed (a multi-selection whose items differ): panels show "—" until the key is
443
+ * edited, and the first edit reaches `onChange` even when it picks the value shown.
444
+ */
445
+ setMixed(keys: (keyof S & string) | readonly (keyof S & string)[], mixed?: boolean): void;
446
+ isMixed(key: keyof S & string): boolean;
447
+ /**
448
+ * While held, `get()` shows the values from before the last change (the previous undo entry);
449
+ * releasing restores the current ones. Nothing is recorded or stored; `onChange` reports
450
+ * `source: "compare"` both ways.
451
+ */
452
+ compare(hold: boolean): void;
453
+ readonly comparing: boolean;
454
+ preset(id: string): void;
455
+ matchingPreset(): string;
456
+ /** Each group's numbered presets, complete over the keys they cover. */
457
+ readonly groupPresets: Readonly<Record<string, readonly GroupPreset<Values<S>>[]>>;
458
+ /**
459
+ * Applies a group's preset (0-based), one undo step; `coalesce` folds it into the previous
460
+ * one (a pan across a card's presets).
461
+ */
462
+ groupPreset(group: string, index: number, options?: {
463
+ coalesce?: boolean;
464
+ }): void;
465
+ /** The group preset the current values match, or -1. */
466
+ matchingGroupPreset(group: string): number;
467
+ code(format?: CodeFormat, values?: Values<S>): string;
468
+ /** Shows the floating chip on `element`; `chip` overrides the store's chip options. */
469
+ attach(element: HTMLElement, chip?: ChipOptions): () => void;
470
+ mount(container: HTMLElement, options?: MountPanelOptions): () => void;
471
+ }
472
+ /** Whether two fields draw and behave the same (a reshape keeps such a key's value). */
473
+ export declare function sameField(a: unknown, b: unknown): boolean;
474
+ export declare function defineControls<const S extends Schema>(id: string, input: S, options?: ControlsOptions<S>): Controls<S>;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * A named set of `--k-*` token values for dark and light, applied to panels mounted with
3
+ * `{ preset }` (or with `data-preset="<name>"` and `presetCss(preset)` in your own stylesheet).
4
+ * `css` adds rules for anything tokens don't cover; `&` stands for the panel.
5
+ */
6
+ export interface TokenPreset {
7
+ readonly name: string;
8
+ readonly dark: Readonly<Record<string, string | number>>;
9
+ readonly light?: Readonly<Record<string, string | number>>;
10
+ readonly css?: string;
11
+ }
12
+ /** The stylesheet for a preset: its dark tokens, its light ones wherever a panel is light. */
13
+ export declare function presetCss(preset: TokenPreset): string;
14
+ /**
15
+ * A dense, square editor (Arcade's Lab): radius 0, compact 26 px rows, the flat frame (groups as
16
+ * plain sections split by hairlines, no cards, and no panel card when inline), the Lab's greys
17
+ * and one blue accent, small type with monospaced readouts, and no glass, glow or lift. Mount it
18
+ * inside the host's own card or column.
19
+ */
20
+ export declare const labPreset: TokenPreset;
21
+ /**
22
+ * The Problems viewer's parameter panel: separate 34 px bars 4 px apart, its blue fill and
23
+ * labels, a spring card that stacks its curve over two sliders, washes that arrive at once and
24
+ * leave over 100 ms, and Problems' own slider spring (ω 34, ζ 0.86). An inline panel sheds its
25
+ * chrome to sit in the host's column. Every difference from the defaults is a token.
26
+ */
27
+ export declare const problemsPreset: TokenPreset;
@@ -0,0 +1,17 @@
1
+ import { type Schema, type Values } from "./schema.js";
2
+ import type { StoragePort } from "./storage.js";
3
+ export interface Variant<V> {
4
+ readonly id: string;
5
+ readonly name?: string;
6
+ readonly values: V;
7
+ }
8
+ export interface Variants<V> {
9
+ readonly ready: boolean;
10
+ list(): readonly Variant<V>[];
11
+ save(name?: string): Variant<V>;
12
+ load(id: string): void;
13
+ remove(id: string): void;
14
+ export(): string;
15
+ import(json: string): void;
16
+ }
17
+ export declare function createVariants<S extends Schema>(schema: () => S, id: string, key: string, storage: () => StoragePort | null, get: () => Values<S>, set: (v: Values<S>) => void, notify: () => void, enabled: () => boolean): Variants<Values<S>>;
@@ -0,0 +1,13 @@
1
+ export { knob, choice, toggle, spring, color, point, easing, action, flatSpring, vec2, vec3, range, angle, seed, keybind, gradient, curve, toolbar, toolbarShortcut, watch, sparkline, graph, meter, table, log, hasValue, isReadout, wrapAngle, SPRING, type FieldMeta, type ActionField, type SpringPart, type ValueField, type Knob, type Choice, type Toggle, type SpringField, type ColorField, type PointField, type EasingField, type VecField, type Vec3, type RangeField, type AngleField, type SeedField, type KeybindField, type GradientField, type CurveField, type ToolbarField, type ToolbarItem, type ToolbarShortcut, type WatchField, type SparklineField, type GraphField, type GraphSeries, type GraphThreshold, type MeterField, type TableField, type LogField, type ReadoutField, type ReadoutKeys, type ReadoutInput, type Point, type Field, type Schema, type Values, type Value, type State, } from "./core/schema.js";
2
+ export { springCss, springMotion, type SpringValue } from "./core/spring.js";
3
+ export { colorHex, parseColor, type Oklch } from "./core/color.js";
4
+ export { sampleGradient, sampleCurve, gradientCss, CURVE_SHAPES, type GradientStop, type GradientSpace, type CurvePoint, type CurveInterp, type SampleGradientOptions, type SampleCurveOptions, } from "./core/ramp.js";
5
+ export { bezierCss, bezierPoint, EASINGS, type Bezier, type EasingName } from "./core/easing.js";
6
+ export { defineControls, type ChangeMeta, type GestureEvent, type ChangeSource, type ChipOptions, type ControlAction, type Controls, type ControlsOptions, type DensityMode, type GroupPreset, type GroupPresetDisplay, type HeaderButton, type HeaderOptions, type HostHistory, type LabelOptions, type MountPanelOptions, type PanelCommand, type PanelHandle, type PanelModule, type PanelKeys, type PersistMode, type SearchChip, type SearchOptions, type SyncOptions, type RendererMode, type RevealMode, type SectionFolded, type ThemeMode, } from "./core/store.js";
7
+ export { createRemoteControls, descriptorToField, formatSwift, fromDescriptors, fromSettings, type SettingsGroup, type SettingsItem, type ActionDescriptor, type ChoiceOption, type ControlDescriptor, type SpringBindingDescriptor, type ToolbarDescriptor, type ReadoutDescriptor, type ValueDescriptor, type RemoteControls, type RemoteControlsOptions, } from "./core/remote.js";
8
+ export type { LogEntry, LogLevel, TableRows, WatchValue } from "./core/readout.js";
9
+ export { memoryStorage, urlFlag, type StoragePort } from "./core/storage.js";
10
+ export type { TokenPreset } from "./core/theme.js";
11
+ export { formatCode, type CodeFormat } from "./core/code.js";
12
+ export { comboFromEvent, letterOf, comboLabel, comboParts, matchesCombo, normalizeCombo, } from "./core/keys.js";
13
+ export type { Variant, Variants } from "./core/variants.js";
@@ -0,0 +1,5 @@
1
+ import type { ChipOptions, Controls } from "../core/store.js";
2
+ import type { Schema } from "../core/schema.js";
3
+ export declare function attach<S extends Schema>(controls: Controls<S>, element: HTMLElement, options?: ChipOptions): () => void;
4
+ /** Parses `"Alt+Shift+D"` into a test for keydown events. Letters and digits match by key position. */
5
+ export declare function matchShortcut(shortcut: string): (event: KeyboardEvent) => boolean;
@@ -0,0 +1,56 @@
1
+ export interface ChoiceItem {
2
+ readonly value: string;
3
+ readonly label: string;
4
+ }
5
+ /** `auto` is segmented while it fits and has at most `COMPACT_CHOICES` options; else a menu. */
6
+ export type ChoiceDisplay = "auto" | "menu" | "segmented";
7
+ /** More options than this always drop down a menu (unless `display: "segmented"`). */
8
+ export declare const COMPACT_CHOICES = 4;
9
+ /** A menu of more options than this opens with a search field. */
10
+ export declare const SEARCH_CHOICES = 8;
11
+ export interface ChoiceControl {
12
+ /** The segmented control and the menu button; one is hidden at a time. */
13
+ readonly seg: HTMLElement;
14
+ readonly buttons: readonly HTMLButtonElement[];
15
+ readonly menu: HTMLButtonElement;
16
+ readonly menuValue: HTMLElement;
17
+ /** Whichever of the two is showing: the row's ↑↓ stop. */
18
+ focusTarget(): HTMLElement;
19
+ isMenu(): boolean;
20
+ /**
21
+ * Shows `value` (the pill under its segment and the menu's label); `mixed` shows none of them
22
+ * (no pill, "—"), for a multi-selection whose items differ.
23
+ */
24
+ show(value: string, instant?: boolean, mixed?: boolean): void;
25
+ /** New labels for the same options. */
26
+ relabel(items: readonly ChoiceItem[]): void;
27
+ /** Steps one option (←/→), stopping at the ends unless `wrap`. */
28
+ step(direction: number, wrap: boolean): void;
29
+ dispose(): void;
30
+ }
31
+ /**
32
+ * One choice control for every segmented row (fields and custom cards): segments while their
33
+ * labels fit with room to spare, a compact button that drops down a menu when they don't or when
34
+ * there are many. It measures on resize, so it switches both ways as the width changes. ←/→ on
35
+ * either steps through the options; Enter or Space opens the menu.
36
+ */
37
+ export declare function createChoice(doc: Document, row: HTMLElement, o: {
38
+ readonly label?: string;
39
+ readonly items: readonly ChoiceItem[];
40
+ readonly display?: ChoiceDisplay;
41
+ /** The current value (the store's, or the card's own). */
42
+ value(): string;
43
+ /** `coalesce`: a later change in the same pan, folded into its undo step. */
44
+ choose(value: string, coalesce?: boolean): void;
45
+ wake?(): void;
46
+ /** The width (px) the segments may take; the row's, less its label, by default. */
47
+ room?(): number;
48
+ /** What a resize is watched on, when not the row (the room depends on more than it). */
49
+ observe?: HTMLElement;
50
+ /** Most options shown as segments under `auto`; `COMPACT_CHOICES` by default. */
51
+ compact?: number;
52
+ /** Menu items show their 1-based number as a key equivalent. */
53
+ numbered?: boolean;
54
+ /** The menu's label when no option matches the value. */
55
+ none?: string;
56
+ }): ChoiceControl;