@kitlangton/knobs 0.1.0 → 0.2.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 (48) hide show
  1. package/README.md +174 -30
  2. package/dist/dev/{chunk-e13znr7c.js → chunk-4km87zfx.js} +3 -3
  3. package/dist/dev/{chunk-ktsn42p7.js → chunk-5cr85vv6.js} +7448 -5390
  4. package/dist/dev/{chunk-kdewv0mt.js → chunk-693jd1gj.js} +171 -70
  5. package/dist/dev/{chunk-qtt698hp.js → chunk-gezhc24g.js} +21 -7
  6. package/dist/dev/index.js +377 -106
  7. package/dist/index.js +392 -108
  8. package/dist/panel.js +9518 -6749
  9. package/dist/types/core/random.d.ts +61 -1
  10. package/dist/types/core/remote.d.ts +3 -1
  11. package/dist/types/core/schema.d.ts +30 -6
  12. package/dist/types/core/store.d.ts +68 -6
  13. package/dist/types/core/variants.d.ts +10 -2
  14. package/dist/types/index.d.ts +3 -2
  15. package/dist/types/panel/editors.d.ts +11 -0
  16. package/dist/types/panel/flash.d.ts +13 -0
  17. package/dist/types/panel/gpu/renderer.d.ts +78 -6
  18. package/dist/types/panel/haptics.d.ts +6 -2
  19. package/dist/types/panel/has.d.ts +0 -1
  20. package/dist/types/panel/index.d.ts +1 -0
  21. package/dist/types/panel/inspector.d.ts +32 -2
  22. package/dist/types/panel/optkeys.d.ts +7 -0
  23. package/dist/types/panel/presets.d.ts +17 -5
  24. package/dist/types/panel/ramp-morph.d.ts +117 -0
  25. package/dist/types/panel/ramps.d.ts +1 -1
  26. package/dist/types/panel/reel.d.ts +43 -0
  27. package/dist/types/panel/scope.d.ts +19 -0
  28. package/dist/types/panel/slider.d.ts +1 -1
  29. package/dist/types/panel/sound.d.ts +137 -0
  30. package/dist/types/panel/styles.d.ts +1 -1
  31. package/dist/types/panel/toolbar-grid.d.ts +6 -0
  32. package/dist/types/ui/activity-css.d.ts +1 -0
  33. package/dist/types/ui/activity-model.d.ts +122 -0
  34. package/dist/types/ui/activity.d.ts +67 -0
  35. package/dist/types/ui/follow.d.ts +17 -0
  36. package/dist/types/ui/index.d.ts +5 -1
  37. package/dist/types/ui/look.d.ts +9 -0
  38. package/dist/types/ui/picker-css.d.ts +1 -0
  39. package/dist/types/ui/picker-model.d.ts +53 -0
  40. package/dist/types/ui/picker.d.ts +67 -0
  41. package/dist/types/ui/react.d.ts +27 -0
  42. package/dist/types/ui/solid.d.ts +23 -0
  43. package/dist/types/ui/split-model.d.ts +54 -0
  44. package/dist/types/ui/split.d.ts +17 -0
  45. package/dist/ui-react.js +3239 -567
  46. package/dist/ui-solid.js +3123 -472
  47. package/dist/ui.js +2688 -100
  48. package/package.json +1 -1
@@ -1,2 +1,62 @@
1
- import { type Schema, type Values } from "./schema.js";
1
+ import { type CurvePoint, type GradientStop } from "./ramp.js";
2
+ import { type Schema, type ValueField, type Values } from "./schema.js";
3
+ /** One field's roll across its whole range (see `randomValues`). */
4
+ export declare function pick(field: ValueField, random: () => number): string | number | boolean | number[] | readonly [0.25, 0.1, 0.25, 1] | readonly [0.42, 0, 1, 1] | readonly [0, 0, 0.58, 1] | readonly [0.42, 0, 0.58, 1] | readonly [0, 0, 1, 1] | readonly [0.65, 0, 0.35, 1] | readonly [0.32, 0.72, 0, 1] | readonly [0.34, 1.56, 0.64, 1] | GradientStop[] | CurvePoint[] | {
5
+ duration: number;
6
+ bounce: number;
7
+ l?: undefined;
8
+ c?: undefined;
9
+ h?: undefined;
10
+ x?: undefined;
11
+ y?: undefined;
12
+ z?: undefined;
13
+ } | {
14
+ l: number;
15
+ c: number;
16
+ h: number;
17
+ duration?: undefined;
18
+ bounce?: undefined;
19
+ x?: undefined;
20
+ y?: undefined;
21
+ z?: undefined;
22
+ } | {
23
+ x: number;
24
+ y: number;
25
+ duration?: undefined;
26
+ bounce?: undefined;
27
+ l?: undefined;
28
+ c?: undefined;
29
+ h?: undefined;
30
+ z?: undefined;
31
+ } | {
32
+ x: number;
33
+ y: number;
34
+ z: number;
35
+ duration?: undefined;
36
+ bounce?: undefined;
37
+ l?: undefined;
38
+ c?: undefined;
39
+ h?: undefined;
40
+ };
41
+ /**
42
+ * How far Random strays from an active preset. A number lands in a window 35% of its range wide,
43
+ * centred on the preset's value and slid inward at an end so it keeps its width: far enough to
44
+ * be a new take, near enough to stay recognisably that preset (Mutate moves 8–12% from the
45
+ * current values; a free roll covers 100%). Choices and opted-in switches keep the preset's
46
+ * pick, changing `switch` of the time. Colours keep their family: hue within ±`hue`°,
47
+ * lightness ±`lightness`, chroma ±`chroma` of what that hue can hold. Seeds roll freely: a new
48
+ * pattern is what a seed is for.
49
+ */
50
+ export declare const AROUND: {
51
+ readonly spread: 0.35;
52
+ readonly switch: 0.3;
53
+ readonly hue: 20;
54
+ readonly lightness: 0.06;
55
+ readonly chroma: 0.15;
56
+ };
57
+ /**
58
+ * One field's roll around `base` (an active preset's value): see `AROUND`. Fields with nothing
59
+ * to vary around (keys, seeds) roll as `pick` does.
60
+ */
61
+ export declare function around(field: ValueField, base: unknown, random: () => number): unknown;
2
62
  export declare function randomValues<S extends Schema>(schema: S, random?: () => number): Values<S>;
@@ -26,9 +26,11 @@ export interface SpringBindingDescriptor extends DescriptorMeta {
26
26
  };
27
27
  readonly default?: SpringValue;
28
28
  }
29
- /** A button row: pressing it calls `onAction(key)`. */
29
+ /** A button row: pressing it calls `onAction(key)`. `shortcut` takes a combination here. */
30
30
  export interface ActionDescriptor extends DescriptorMeta {
31
31
  readonly kind: "action";
32
+ readonly global?: boolean;
33
+ readonly rebind?: boolean;
32
34
  readonly default?: undefined;
33
35
  readonly bind?: undefined;
34
36
  }
@@ -20,7 +20,10 @@ export interface FieldMeta {
20
20
  /** One muted line under the section's heading on a page (the first field that gives one wins). */
21
21
  readonly sectionDescription?: string;
22
22
  readonly shortcut?: string;
23
- /** `false` keeps the field out of Random and Mutate, as a lock does, but by declaration. */
23
+ /**
24
+ * `false` keeps the field out of Random and Mutate, as a lock does, but by declaration. Switches,
25
+ * seeds and key combinations start out; `true` brings them in.
26
+ */
24
27
  readonly random?: boolean;
25
28
  /**
26
29
  * The row dims and takes no edits from the pointer or keys (focus can still reach it, to read
@@ -141,6 +144,12 @@ export interface CurveField extends FieldMeta {
141
144
  export interface ActionField extends FieldMeta {
142
145
  readonly kind: "action";
143
146
  readonly run?: () => void;
147
+ /** The combination that presses it (`"Shift+K"`), shown as a key cap at the row's end. */
148
+ readonly shortcut?: string;
149
+ /** Where `shortcut` works, as a toolbar button's (see `ToolbarShortcut`); `"panel"` by default. */
150
+ readonly scope?: "page" | "panel" | "none";
151
+ /** The user can rebind it in place: a click or Enter on the key cap listens for a new one. */
152
+ readonly rebind?: boolean;
144
153
  }
145
154
  export interface Vec3 {
146
155
  readonly x: number;
@@ -350,9 +359,17 @@ export declare function choice<const T extends readonly [string, ...string[]]>(v
350
359
  readonly labels?: Readonly<Partial<Record<T[number], string>>>;
351
360
  readonly display?: "menu" | "segmented";
352
361
  }): Choice<T[number]>;
353
- /** `action("Replay", { run })`: a button row. Its key also reaches the store's `onAction`. */
354
- export declare function action(label?: string, meta?: Omit<FieldMeta, "label"> & {
362
+ /**
363
+ * `action("Replay", { run, shortcut: "Shift+K", global: true, rebind: true })`: a button row. Its
364
+ * key also reaches the store's `onAction`. `shortcut` presses it as a toolbar button's does (a
365
+ * combination, or `{ key, scope }`), and `rebind` lets the user change it from its key cap.
366
+ */
367
+ export declare function action(label?: string, meta?: Omit<FieldMeta, "label" | "shortcut"> & {
355
368
  run?: () => void;
369
+ shortcut?: string | ToolbarShortcut;
370
+ /** A plain-string shortcut works anywhere on the page (as `scope: "page"`). */
371
+ global?: boolean;
372
+ rebind?: boolean;
356
373
  }): ActionField;
357
374
  type SpringHalf = number | Knob;
358
375
  /**
@@ -369,6 +386,10 @@ export declare function flatSpring(parts?: {
369
386
  readonly duration: Knob;
370
387
  readonly bounce: Knob;
371
388
  };
389
+ /**
390
+ * A switch. Switches usually show or hide things (a view, a scene's parts), so Random and Mutate
391
+ * leave them alone unless `random: true`.
392
+ */
372
393
  export declare function toggle(value: boolean, meta?: FieldMeta): Toggle;
373
394
  /** `spring({ duration: 0.4, bounce: 0.2 })`, like SwiftUI's `.spring(duration:bounce:)`. */
374
395
  export declare function spring(value?: Partial<SpringValue>, meta?: FieldMeta): SpringField;
@@ -469,11 +490,14 @@ export declare function curve(points: readonly CurvePoint[] | keyof typeof CURVE
469
490
  readonly presets?: Readonly<Record<string, readonly CurvePoint[]>>;
470
491
  readonly shapes?: boolean;
471
492
  }): CurveField;
472
- /** A toolbar item's shortcut as `{ key, scope }`, or undefined without one. */
473
- export declare function toolbarShortcut(item: ToolbarItem): {
493
+ type ResolvedShortcut = {
474
494
  readonly key: string;
475
495
  readonly scope: "page" | "panel" | "none";
476
- } | undefined;
496
+ };
497
+ /** A toolbar item's shortcut as `{ key, scope }`, or undefined without one. */
498
+ export declare function toolbarShortcut(item: ToolbarItem): ResolvedShortcut | undefined;
499
+ /** An action row's shortcut as `{ key, scope }`, or undefined without one. */
500
+ export declare function actionShortcut(field: ActionField): ResolvedShortcut | undefined;
477
501
  /** A wrapping row of buttons with icons and shortcut chips; holds no value. */
478
502
  export declare function toolbar(items: readonly ToolbarItem[], meta?: FieldMeta): ToolbarField;
479
503
  type ReadoutOptions = Omit<ReadoutMeta, "shortcut" | "random" | "readOnly">;
@@ -36,6 +36,16 @@ export interface ChangeMeta {
36
36
  readonly coalesce?: boolean;
37
37
  readonly key?: string;
38
38
  readonly group?: string;
39
+ /** A reset that went back to the active preset (see `Controls.activePreset`), not the defaults. */
40
+ readonly base?: boolean;
41
+ }
42
+ /**
43
+ * The preset last applied from the strip: a schema preset (by id) or a saved variant. It's the
44
+ * base Reset returns to and Random explores around.
45
+ */
46
+ export interface PresetRef {
47
+ readonly kind: "preset" | "variant";
48
+ readonly id: string;
39
49
  }
40
50
  export interface ControlAction<V = State> {
41
51
  readonly id?: string;
@@ -98,6 +108,14 @@ export type PanelKeys = {
98
108
  /** `false` stops ↑/↓ at the first and last rows instead of wrapping around. */
99
109
  readonly wrap?: boolean;
100
110
  };
111
+ /** `glass` (the default): ticks with a faint ring. `mech`: drier, the rings all but gone. */
112
+ export type SoundTheme = "glass" | "mech";
113
+ /** A mount's `sound`, beyond `true`. */
114
+ export interface SoundOptions {
115
+ /** This panel's level, 0–1 (times `sound.volume`). */
116
+ readonly volume?: number;
117
+ readonly theme?: SoundTheme;
118
+ }
101
119
  /** A header button (see `MountPanelOptions.header`). */
102
120
  export type HeaderButton = "random" | "reset" | "copy" | "undo" | "redo" | "help" | "close";
103
121
  export interface HeaderOptions {
@@ -185,6 +203,13 @@ export interface MountPanelOptions extends LabelOptions {
185
203
  * Reduced motion turns them off too.
186
204
  */
187
205
  readonly haptics?: boolean;
206
+ /**
207
+ * Sound effects: quiet, short ticks and clicks synthesized live for the panel's moments (a
208
+ * slider's detents, a switch, Random, Copy…). Off by default; `true` turns them on, or
209
+ * `{ volume, theme }`. The page's switch (`sound.enable(false)` from `knobs/panel`) mutes every
210
+ * panel, and the `?` popover offers it as Sounds, kept in the store's storage.
211
+ */
212
+ readonly sound?: boolean | SoundOptions;
188
213
  /** Called once the panel is mounted, with a handle to focus it (a host's "Tune" key). */
189
214
  readonly onReady?: (panel: PanelHandle) => void;
190
215
  /** What Copy (and ⇧C) puts on the clipboard; `controls.code()` by default. */
@@ -406,17 +431,28 @@ export interface Controls<S extends Schema = Schema> {
406
431
  readonly canRedo: boolean;
407
432
  subscribe(cb: () => void): () => void;
408
433
  /**
409
- * Back to the defaults: every field, or only `keys`. Locks don't apply; disabled and read-only
410
- * fields keep their values.
434
+ * Back to the active preset (see `activePreset`) while anything differs from it, else to the
435
+ * defaults: every field, or only `keys`. Reaching the defaults with every field also forgets
436
+ * the active preset. Locks don't apply; disabled and read-only fields keep their values.
411
437
  */
412
438
  reset(keys?: readonly (keyof S & string)[]): void;
439
+ /** One key, as `reset` does: to the active preset's value, else (already there) its default. */
413
440
  resetKey(key: keyof S & string): void;
441
+ /**
442
+ * A group, as `reset` does: to its active group preset (or the panel's active preset) while it
443
+ * differs from it, else to its defaults, which forgets the group's preset.
444
+ */
414
445
  resetGroup(group: string): void;
446
+ /** Whether the key (or anything) differs from the active preset; the defaults without one. */
415
447
  isModified(key?: keyof S & string): boolean;
448
+ /** Whether `reset` (of every field, or `keys`) would change anything. */
449
+ canReset(keys?: readonly (keyof S & string)[]): boolean;
450
+ /** What differs from the defaults (the values `code()` and Swift export care about). */
416
451
  modified(): Partial<Values<S>>;
417
452
  /**
418
453
  * Random values for every field that can roll (see `canRoll`), or those among `keys`, drawn
419
- * from `random`. One undo step.
454
+ * from `random`. With an active preset, they land near its values (see `AROUND`) instead of
455
+ * anywhere in range. One undo step.
420
456
  */
421
457
  randomize(keys?: readonly (keyof S & string)[]): void;
422
458
  /** Small changes around the current values instead of a re-roll; skips what can't roll. */
@@ -424,6 +460,17 @@ export interface Controls<S extends Schema = Schema> {
424
460
  /** Keeps fields out of Random and Mutate (or lets them back in). Saved with the values. */
425
461
  lock(keys: (keyof S & string) | readonly (keyof S & string)[], locked?: boolean): void;
426
462
  isLocked(key: keyof S & string): boolean;
463
+ /**
464
+ * The combination that presses an action row now, or a toolbar's button (`item`, its id or
465
+ * label): the user's rebinding, else the declared `shortcut`; `""` when cleared, undefined
466
+ * when there's none.
467
+ */
468
+ shortcut(key: keyof S & string, item?: string): string | undefined;
469
+ /**
470
+ * Rebinds an action row's shortcut (or a toolbar button's): a combination, `""` for none, or
471
+ * undefined to restore the declared one. Not undoable; saved with the values, as locks are.
472
+ */
473
+ rebind(key: keyof S & string, combo: string | undefined, item?: string): void;
427
474
  /**
428
475
  * Whether Random and Mutate may change `key`: it holds a value and isn't locked, `random:
429
476
  * false`, refused by `tunable`, disabled or read-only.
@@ -451,13 +498,28 @@ export interface Controls<S extends Schema = Schema> {
451
498
  */
452
499
  compare(hold: boolean): void;
453
500
  readonly comparing: boolean;
454
- preset(id: string): void;
501
+ /**
502
+ * Applies a preset and makes it the active one (`"default"` clears it); one undo step, or
503
+ * folded into the last with `coalesce` (a pan across the strip).
504
+ */
505
+ preset(id: string, options?: {
506
+ coalesce?: boolean;
507
+ }): void;
455
508
  matchingPreset(): string;
509
+ /**
510
+ * The preset (or saved variant) last applied, which Reset returns to and Random explores
511
+ * around, or null. Kept with the values; forgotten by a reset to the defaults.
512
+ */
513
+ activePreset(): PresetRef | null;
514
+ /** The group preset last applied to `group` (0-based), or -1. */
515
+ activeGroupPreset(group: string): number;
516
+ /** The values Reset returns to first: the active presets over the defaults. */
517
+ baseValues(): Values<S>;
456
518
  /** Each group's numbered presets, complete over the keys they cover. */
457
519
  readonly groupPresets: Readonly<Record<string, readonly GroupPreset<Values<S>>[]>>;
458
520
  /**
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).
521
+ * Applies a group's preset (0-based) and makes it the group's active one, one undo step;
522
+ * `coalesce` folds it into the previous one (a pan across a card's presets).
461
523
  */
462
524
  groupPreset(group: string, index: number, options?: {
463
525
  coalesce?: boolean;
@@ -9,9 +9,17 @@ export interface Variants<V> {
9
9
  readonly ready: boolean;
10
10
  list(): readonly Variant<V>[];
11
11
  save(name?: string): Variant<V>;
12
- load(id: string): void;
12
+ /**
13
+ * Applies a saved variant, making it the active preset (see `Controls.activePreset`); one undo
14
+ * step, or folded into the last with `coalesce` (a pan across the strip).
15
+ */
16
+ load(id: string, options?: {
17
+ coalesce?: boolean;
18
+ }): void;
19
+ /** Renames a saved variant (an empty name clears it); kept in storage with the variants. */
20
+ rename(id: string, name: string): void;
13
21
  remove(id: string): void;
14
22
  export(): string;
15
23
  import(json: string): void;
16
24
  }
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>>;
25
+ export declare function createVariants<S extends Schema>(schema: () => S, id: string, key: string, storage: () => StoragePort | null, get: () => Values<S>, set: (v: Values<S>, id: string, coalesce: boolean) => void, notify: () => void, enabled: () => boolean): Variants<Values<S>>;
@@ -1,9 +1,10 @@
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";
1
+ export { knob, choice, toggle, spring, color, point, easing, action, flatSpring, vec2, vec3, range, angle, seed, keybind, gradient, curve, toolbar, toolbarShortcut, actionShortcut, 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
2
  export { springCss, springMotion, type SpringValue } from "./core/spring.js";
3
3
  export { colorHex, parseColor, type Oklch } from "./core/color.js";
4
4
  export { sampleGradient, sampleCurve, gradientCss, CURVE_SHAPES, type GradientStop, type GradientSpace, type CurvePoint, type CurveInterp, type SampleGradientOptions, type SampleCurveOptions, } from "./core/ramp.js";
5
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";
6
+ export { AROUND } from "./core/random.js";
7
+ export { defineControls, type ChangeMeta, type GestureEvent, type ChangeSource, type ChipOptions, type ControlAction, type Controls, type ControlsOptions, type DensityMode, type GroupPreset, type GroupPresetDisplay, type PresetRef, 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 SoundOptions, type SoundTheme, type ThemeMode, } from "./core/store.js";
7
8
  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
9
  export type { LogEntry, LogLevel, TableRows, WatchValue } from "./core/readout.js";
9
10
  export { memoryStorage, urlFlag, type StoragePort } from "./core/storage.js";
@@ -54,6 +54,17 @@ export interface EditorRow {
54
54
  dispose(): void;
55
55
  readonly tracks: readonly SubTrack[];
56
56
  readonly gpu: readonly GpuRowBinding[];
57
+ /** Named options the digits pick while the row is focused (a ramp's or easing's presets). */
58
+ readonly options?: RowOptions;
59
+ }
60
+ /** A row's options as the digits 1–9 pick them (see scope.ts). */
61
+ export interface RowOptions {
62
+ readonly count: number;
63
+ choose(i: number): void;
64
+ /** The options while they show (segments, chips), for a digit on each; empty while hidden. */
65
+ shown(): readonly HTMLElement[];
66
+ /** What stands for them while they're hidden (a menu's button, a head's mini curve). */
67
+ readonly anchor: Element;
57
68
  }
58
69
  export declare const reducedMotion: (doc: Document) => boolean;
59
70
  export declare const rgbCss: (rgb: readonly number[]) => string;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * A button's press flash: its wash brightens at once and eases out, so a command fired from a key
3
+ * shows on the button it belongs to (Undo, Redo, Reset, Copy, Random, a group's die and reset).
4
+ * The DOM runs it as a CSS animation (`data-k-flash`, see the styles); the GPU renderer draws the
5
+ * same curve from `flashOf`.
6
+ */
7
+ export declare const FLASH_MS = 320;
8
+ export declare function flash(button: HTMLElement | undefined, wake?: () => void): void;
9
+ /**
10
+ * How much of `button`'s flash is left at `now` (1 to 0): ease-out cubic, as the DOM's
11
+ * `cubic-bezier(.33,1,.68,1)` from the flash back to the button's own wash.
12
+ */
13
+ export declare function flashOf(button: Element, now: number): number;
@@ -3,7 +3,7 @@ import { type HeaderDie } from "../die.js";
3
3
  import { type IconName } from "../icons.js";
4
4
  import { Edges } from "../motion.js";
5
5
  import type { MorphFrame } from "../morph.js";
6
- import type { PresetStrip } from "../presets.js";
6
+ import { type PresetStrip } from "../presets.js";
7
7
  import type { DigitPlace, Slider } from "../slider.js";
8
8
  import type { TabStrip } from "../tabs.js";
9
9
  import type { ReadoutView } from "../../ui/readouts.js";
@@ -130,6 +130,11 @@ export interface GpuEasingRow {
130
130
  curve(): readonly number[];
131
131
  /** P0, P1, P3, P2: the two arms from the ends to their handles. */
132
132
  arms(): readonly number[];
133
+ /** Steps the row's morph (curve, arms, handles) to this frame; whether it still moves. */
134
+ advance(now: number): boolean;
135
+ /** The line's motion blur (px), and each handle's (px across, px down): 0 at rest. */
136
+ blur(): number;
137
+ smear(i: number): readonly [number, number];
133
138
  /** (0,0) and (1,1) in the box. */
134
139
  guides(): readonly number[];
135
140
  readonly handles: readonly HTMLElement[];
@@ -148,6 +153,13 @@ export interface GpuRampHandle {
148
153
  readonly selected: boolean;
149
154
  /** Dragged off the strip or graph: it goes when let go. */
150
155
  readonly leaving: boolean;
156
+ /**
157
+ * How grown it is (0…1), when its row's morph sizes it: budding out of the line or merging
158
+ * into a neighbour. Its position is then already the morph's, so it isn't eased again.
159
+ */
160
+ readonly grow?: number;
161
+ /** Its motion blur (px along its travel, across and down), 0, 0 at rest. */
162
+ readonly smear?: readonly [number, number];
151
163
  }
152
164
  export interface GpuGradientRow {
153
165
  readonly kind: "gradient";
@@ -156,12 +168,15 @@ export interface GpuGradientRow {
156
168
  readonly row: HTMLElement;
157
169
  readonly x: GpuExpander;
158
170
  readonly read: HTMLElement;
159
- /** The head's swatch strip, and its stop ticks (x in the strip). */
171
+ /** The head's swatch strip, and its stop ticks (x in the strip; grown 0…1 as its stop is). */
160
172
  readonly mini: HTMLElement;
161
173
  ticks(): readonly {
162
174
  readonly el: HTMLElement;
163
175
  readonly x: number;
176
+ readonly grow?: number;
164
177
  }[];
178
+ /** Steps the row's morph to this frame (samples, stops and ticks); whether it still moves. */
179
+ advance(now: number): boolean;
165
180
  /** Evenly spaced samples as gamma sRGB triples, flat (see `gradientSamples`). */
166
181
  samples(): readonly number[];
167
182
  /** The editor's strip and how far in from each end its ramp starts (the handles' radius). */
@@ -192,7 +207,12 @@ export interface GpuCurveRow {
192
207
  readonly graph: HTMLElement;
193
208
  /** Grid lines as x0, y0, x1, y1 quadruples in the graph. */
194
209
  grid(): readonly number[];
210
+ /** The line and mini curve as the row's morph shows them now (stepped to `now`, ms). */
195
211
  curve(): readonly number[];
212
+ /** Steps the row's morph to this frame; whether it still moves. */
213
+ advance(now: number): boolean;
214
+ /** The line's motion blur (px), 0 at rest. */
215
+ blur(): number;
196
216
  handles(): readonly GpuRampHandle[];
197
217
  /** The hover crosshair: its x, the curve's y there, and the readout's element. */
198
218
  hover(): {
@@ -212,9 +232,17 @@ export interface GpuActionRow {
212
232
  readonly display: string;
213
233
  readonly row: HTMLElement;
214
234
  readonly button: HTMLButtonElement;
235
+ /** Its shortcut's key cap, at the row's end: a rebindable one listens while `capturing`. */
236
+ readonly keys?: GpuKeyCap;
215
237
  readonly wave: RowWave;
216
238
  isModified(): boolean;
217
239
  }
240
+ /** A key cap (a keybind's, an action's shortcut): its text, and whether it's listening for keys. */
241
+ export interface GpuKeyCap {
242
+ readonly chip: HTMLElement;
243
+ readonly text: HTMLElement;
244
+ capturing(): boolean;
245
+ }
218
246
  /** A vector's number cell: its axis letter and readout (DOM-placed text). */
219
247
  export interface GpuCell {
220
248
  readonly el: HTMLElement;
@@ -517,7 +545,6 @@ declare class GpuOverlayManager {
517
545
  private curSubHovered;
518
546
  /** Whether the pointer has one of the panel's rows (and keys haven't taken over since). */
519
547
  private curPointerRow;
520
- private readonly hotRows;
521
548
  private curPanelRect;
522
549
  private curPanelScrollTop;
523
550
  private curLayoutKey;
@@ -601,7 +628,6 @@ declare class GpuOverlayManager {
601
628
  * their hover twins, at --k-button-radius.
602
629
  */
603
630
  private drawButtonBox;
604
- private isHot;
605
631
  /** Square inside its card, except the card's own rounded corners on the first and last rows. */
606
632
  private rowCorners;
607
633
  /** A row's hover wash, drawn beneath its control like the DOM row background. */
@@ -613,6 +639,11 @@ declare class GpuOverlayManager {
613
639
  private drawSeparator;
614
640
  /** The focus wash, matching the row's corners inside its card. */
615
641
  private drawFocus;
642
+ /**
643
+ * A header or group button that blooms in and out of its slot (`data-shown`): its opacity, scale
644
+ * and blur (σ, px) from the DOM's transition while one runs, else simply there or not.
645
+ */
646
+ private bloomOf;
616
647
  private drawIcon;
617
648
  /**
618
649
  * Copy, and the check it turns into once copied, from the DOM's transitions while they run
@@ -621,6 +652,18 @@ declare class GpuOverlayManager {
621
652
  private drawCopy;
622
653
  /** The die is always traced, at rest too, exactly as the DOM canvas does: nothing swaps. */
623
654
  private drawDie;
655
+ /** Panels whose DOM overlays `cutUnderHead` has cut, to restore once nothing is under. */
656
+ private readonly headCuts;
657
+ /**
658
+ * The DOM pieces the GPU leaves above its canvas (a row's accessory, the number being typed,
659
+ * the search field) are cut at a pinned header's foot (`foot`, viewport y), as the rows are.
660
+ */
661
+ private cutUnderHead;
662
+ /**
663
+ * A button's press flash (flash.ts) over its wash: the wash brightened toward the text colour,
664
+ * easing out. Returns `ink` brightened toward the text colour while the flash lasts.
665
+ */
666
+ private drawFlash;
624
667
  /** A circular icon button's hover/focus wash. */
625
668
  private drawRoundWash;
626
669
  /**
@@ -648,6 +691,7 @@ declare class GpuOverlayManager {
648
691
  /** The current row's selection wash (0…1), which its hover wash gives way to. */
649
692
  private curSelected;
650
693
  /** A stroke through `pts` (x,y pairs in viewport px), round-capped like SVG's. */
694
+ /** `blur` (px) softens it across, as a moving line's smear (the DOM's Gaussian over √12). */
651
695
  private pushLine;
652
696
  /**
653
697
  * Eases a list of numbers toward `target` on the fill spring, the way the DOM transitions the
@@ -660,6 +704,26 @@ declare class GpuOverlayManager {
660
704
  private shape;
661
705
  /** An outline like CSS `outline: 1.5px` at `offset`, following `radius`; drawn while focus-visible. */
662
706
  private readout;
707
+ /** Each name-like readout's text and the one before it, for their crossfade (see `fadeText`). */
708
+ private readonly fades;
709
+ /** Text that crossfades from what it said before when its element says `data-fade`, as the DOM's. */
710
+ private fadeText;
711
+ /**
712
+ * A rolling readout (reel.ts), right-aligned where its element ends: each number a wheel per
713
+ * place, as the slider's readout draws its digits, and the text between them still. Laid out
714
+ * here from the glyphs' advances (tabular digits), not from the DOM's spans, which may not
715
+ * have caught up yet.
716
+ */
717
+ private drawRolling;
718
+ private advanceOf;
719
+ /**
720
+ * A number's places, right-aligned at `right`: each digit a wheel seen through a feathered
721
+ * slot (faces 2.1em apart, as the DOM's reel), blurring with speed; punctuation keeps its own
722
+ * advance, and a place on its way out keeps its last spot. True while any place moves.
723
+ */
724
+ private drawPlaces;
725
+ /** The soft wash on the card focus is on (`data-k-scope`), as the DOM's ::after. */
726
+ private drawScope;
663
727
  /** A disclosure head (hover, label and changed dot, caret, focus); returns the body's clip, if open. */
664
728
  private drawHead;
665
729
  /** A disclosure chevron, turning a quarter on the lead spring as it opens. */
@@ -674,8 +738,6 @@ declare class GpuOverlayManager {
674
738
  private drawEasing;
675
739
  /** A gradient strip from its samples (see shader kind 13), `inset` in from each end. */
676
740
  private pushRamp;
677
- /** How far a handle that arrived mid-life has grown in (`data-born`), like the DOM's keyframes. */
678
- private appear;
679
741
  private drawGradient;
680
742
  private drawCurve;
681
743
  private readonly lastHover;
@@ -694,6 +756,16 @@ declare class GpuOverlayManager {
694
756
  private drawRange;
695
757
  private drawAngle;
696
758
  private drawKeybind;
759
+ /** When each listening key cap started, so its breathing starts from full strength. */
760
+ private readonly capSince;
761
+ /** Each key cap's text and the one before it, for their crossfade. */
762
+ private readonly capTexts;
763
+ /**
764
+ * A key cap, as the DOM's: its tray's width glides to fit new text (snappy, no bounce) with its
765
+ * right edge still, while the old text fades out and the new in. Listening, it takes a soft
766
+ * accent wash and its text breathes; no ring.
767
+ */
768
+ private drawKeyCap;
697
769
  /** A readout's clip: the card's, narrowed to `el` when given. */
698
770
  private clipTo;
699
771
  /** Paints a readout's ops (local px from `r`'s corner), clipped to it, as its canvas does. */
@@ -1,5 +1,9 @@
1
- /** One light tick for a change made by `from`'s control, unless ticks are off or too close. */
2
- export declare function tick(from: Element): void;
1
+ import { type SoundCue } from "./sound.js";
2
+ /**
3
+ * One light tick for a change made by `from`'s control, unless ticks are off or too close. With
4
+ * `sound`, a panel that plays sounds plays that cue at the same moment (see sound.ts).
5
+ */
6
+ export declare function tick(from: Element, sound?: SoundCue): void;
3
7
  /**
4
8
  * Lays a label for the hidden switch over `target` (a positioned button), so a tap on it is the
5
9
  * user's own tap on a switch: the only tick iOS 26.5+ plays. Only where Safari's switch exists and
@@ -7,7 +7,6 @@
7
7
  * - `data-k-fv` on each ancestor of the focused element while it's `:focus-visible`
8
8
  * (`:has(:focus-visible)`); `"track"` when that's a slider track, `"head"` an editor's header.
9
9
  * - `data-k-active` on each ancestor of a `.knobs-active` element (`:has(.knobs-active)`).
10
- * - `data-k-head-hover` on an editor row whose header is hovered (`:has(>.knobs-x-head:hover)`).
11
10
  * - `data-k-row-hover` on the root while the pointer is on one of its rows
12
11
  * (`:has(.knobs-card>:hover)`).
13
12
  */
@@ -6,5 +6,6 @@ export { Spring, VelocityTracker } from "./motion.js";
6
6
  export { createPanel, mountPage, mountPanel, type Panel, type PanelSearch } from "./panel.js";
7
7
  export { balancedRuns, type FoldSummaryInfo, type MountPageOptions, type Page, type PageAction, type PageCard, type PageCardOptions, type PageControl, type PageFoldOptions, type PageFoldSource, type PageGroupInfo, type PageHighlight, type PageSectionInfo, } from "./page.js";
8
8
  export { createPresetStrip, nextPresetIndex, resolveActivePresetEntry, type ActivePresetState, type PresetEntry, type PresetStrip, } from "./presets.js";
9
+ export { sound, type CueDetail, type Sound, type SoundCue, type SoundLayer, type SoundOptions, type SoundTheme, } from "./sound.js";
9
10
  export { injectStyles } from "./styles.js";
10
11
  export { attachVariantSwitcher, getSwitcherOptions, type ChoiceKey, type SwitcherOptions, } from "./switcher.js";
@@ -1,4 +1,4 @@
1
- import { type AngleField, type KeybindField, type Point, type RangeField, type SeedField, type ToolbarField, type ToolbarItem, type Vec3, type VecField } from "../core/schema.js";
1
+ import { toolbarShortcut, type AngleField, type KeybindField, type Point, type RangeField, type SeedField, type ToolbarField, type ToolbarItem, type Vec3, type VecField } from "../core/schema.js";
2
2
  import type { EditorRow, RowContext } from "./editors.js";
3
3
  type Vec = Point & {
4
4
  readonly z?: number;
@@ -7,6 +7,28 @@ export declare function vecRow(c: RowContext<VecField>): EditorRow;
7
7
  export declare function rangeRow(c: RowContext<RangeField>): EditorRow;
8
8
  export declare function angleRow(c: RowContext<AngleField>): EditorRow;
9
9
  export declare function seedRow(c: RowContext<SeedField>): EditorRow;
10
+ export interface KeyCap {
11
+ readonly chip: HTMLButtonElement;
12
+ readonly text: HTMLElement;
13
+ capturing(): boolean;
14
+ /** The combination it shows: `""` for none, undefined for mixed ("—"). */
15
+ show(combo: string | undefined): void;
16
+ }
17
+ /**
18
+ * A key cap that records a combination: a click, Enter or Space listens, the next combination is
19
+ * the value, Esc cancels and ⌫ clears. While it listens its row takes the active wash and the cap
20
+ * breathes, "Type keys…" (or the modifiers held so far) in its place; its width follows its text
21
+ * on a spring, its right edge still. Keybind rows and rebindable action rows use it.
22
+ */
23
+ export declare function keyCap(o: {
24
+ readonly doc: Document;
25
+ readonly row: HTMLElement;
26
+ /** What the cap reads with no combination. */
27
+ readonly empty: string;
28
+ name(): string;
29
+ set(combo: string): void;
30
+ wake(): void;
31
+ }): KeyCap;
10
32
  export declare function keybindRow(c: RowContext<KeybindField>): EditorRow;
11
33
  export interface ToolbarRow extends EditorRow {
12
34
  readonly field: ToolbarField;
@@ -14,7 +36,15 @@ export interface ToolbarRow extends EditorRow {
14
36
  match(event: KeyboardEvent, global: boolean): ToolbarItem | undefined;
15
37
  press(item: ToolbarItem): void;
16
38
  swap(field: ToolbarField): void;
39
+ /** Shows the shortcuts as they are now (after a rebinding). */
40
+ refresh(): void;
17
41
  }
18
- export declare function toolbarRow(c: RowContext<ToolbarField>, press: (item: ToolbarItem) => void): ToolbarRow;
42
+ type Shortcut = ReturnType<typeof toolbarShortcut>;
43
+ /**
44
+ * A toolbar: its buttons in equal columns, as many as fit and balanced across the rows (see
45
+ * `toolbarColumns`), each with its icon and label at the start and its shortcut at the end.
46
+ * `keyOf` is each item's shortcut as it is now (the store's rebinding, else its own).
47
+ */
48
+ export declare function toolbarRow(c: RowContext<ToolbarField>, press: (item: ToolbarItem) => void, keyOf?: (item: ToolbarItem) => Shortcut): ToolbarRow;
19
49
  /** The value a vector shows, as `{ x, y }` or `{ x, y, z }`. */
20
50
  export type { Vec, Vec3 };
@@ -0,0 +1,7 @@
1
+ import type { RowOptions } from "./editors.js";
2
+ export declare function createOptionKeys(doc: Document, root: HTMLElement, wake: () => void): {
3
+ /** Shows `options`' digits (undefined hides them); the same ones again just re-place. */
4
+ show(options: RowOptions | undefined): void;
5
+ dispose(): void;
6
+ };
7
+ export type OptionKeys = ReturnType<typeof createOptionKeys>;