@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,137 @@
1
+ import type { Ring } from "../core/readout.js";
2
+ import { type Rgba } from "./rgba.js";
3
+ export type PlotOp = {
4
+ readonly t: "line";
5
+ readonly pts: readonly number[];
6
+ readonly color: Rgba;
7
+ readonly width: number;
8
+ }
9
+ /** The area between a line and `base` (a y), fading to `fade` × its alpha at the base. */
10
+ | {
11
+ readonly t: "area";
12
+ readonly pts: readonly number[];
13
+ readonly base: number;
14
+ readonly color: Rgba;
15
+ readonly fade: number;
16
+ } | {
17
+ readonly t: "rect";
18
+ readonly x: number;
19
+ readonly y: number;
20
+ readonly w: number;
21
+ readonly h: number;
22
+ readonly color: Rgba;
23
+ readonly r?: number;
24
+ } | {
25
+ readonly t: "dot";
26
+ readonly x: number;
27
+ readonly y: number;
28
+ readonly r: number;
29
+ readonly color: Rgba;
30
+ };
31
+ /** The colours readouts draw with, read from the tokens. */
32
+ export interface Palette {
33
+ readonly accent: Rgba;
34
+ readonly text: Rgba;
35
+ readonly label: Rgba;
36
+ readonly dim: Rgba;
37
+ readonly line: Rgba;
38
+ readonly surfaceHi: Rgba;
39
+ readonly warn: Rgba;
40
+ readonly critical: Rgba;
41
+ /** Corner radius (px); 0 squares the bars. */
42
+ readonly radius: number;
43
+ }
44
+ /** Series colours after the first (the accent). */
45
+ export declare const SERIES: string[];
46
+ /** A number as text: `decimals` places, else fewer as it grows (2 under 10, 1 under 100, 0 past). */
47
+ export declare function formatNumber(v: number, decimals?: number): string;
48
+ /** A step of 1, 2 or 5 × 10ⁿ near `span / 4`: a scale snaps to these, so it moves rarely. */
49
+ export declare function niceStep(span: number): number;
50
+ /**
51
+ * The range a history is drawn on: its samples' extent (and `keep`, such as thresholds), snapped
52
+ * out to a nice step, with fixed bounds where given. A flat history gets room around it.
53
+ */
54
+ export declare function fitRange(lo: number, hi: number, fixed?: {
55
+ min?: number;
56
+ max?: number;
57
+ }, keep?: readonly number[]): [number, number];
58
+ /**
59
+ * A scale that eases to each new range on a spring, interruptibly, instead of jumping. The span
60
+ * eases in log space, so a line rescaling by 5× moves at an even pace rather than mostly at once.
61
+ */
62
+ export declare class EasedRange {
63
+ private readonly lo;
64
+ private readonly span;
65
+ private placed;
66
+ private goal;
67
+ /** Sets where it's headed; `snap` places it there at once (the first range, reduced motion). */
68
+ to(lo: number, hi: number, snap?: boolean): void;
69
+ /** Steps both bounds; true while they're still moving. */
70
+ step(dt: number): boolean;
71
+ get value(): [number, number];
72
+ get target(): [number, number];
73
+ }
74
+ /**
75
+ * A history's samples as points across `w`, newest at the right edge; a history that isn't full
76
+ * yet starts part way across. NaN samples break the line: each run is its own list.
77
+ */
78
+ export declare function historyRuns(ring: Ring, capacity: number, range: readonly [number, number], w: number, top: number, bottom: number): number[][];
79
+ /** The sample index under `x` (local px), or -1. */
80
+ export declare function sampleAt(x: number, w: number, capacity: number, length: number): number;
81
+ export interface SparkScene {
82
+ readonly ops: PlotOp[];
83
+ }
84
+ /** A sparkline: a soft area under its line, the newest sample dotted; hovered, its min and max. */
85
+ export declare function sparkScene(o: {
86
+ ring: Ring;
87
+ capacity: number;
88
+ range: readonly [number, number];
89
+ w: number;
90
+ h: number;
91
+ color: Rgba;
92
+ palette: Palette;
93
+ hover: boolean;
94
+ }): PlotOp[];
95
+ /** Dashes along a horizontal line at `y`: 4 px on, 3 off. */
96
+ export declare function dashes(y: number, x0: number, x1: number, color: Rgba, on?: number, off?: number): PlotOp[];
97
+ export interface GraphLine {
98
+ readonly ring: Ring;
99
+ readonly color: Rgba;
100
+ readonly area: boolean;
101
+ }
102
+ /** A graph: thresholds dashed, each series' area and line, a baseline; hovered, a crosshair. */
103
+ export declare function graphScene(o: {
104
+ lines: readonly GraphLine[];
105
+ capacity: number;
106
+ range: readonly [number, number];
107
+ thresholds: readonly {
108
+ value: number;
109
+ color: Rgba;
110
+ }[];
111
+ w: number;
112
+ h: number;
113
+ palette: Palette;
114
+ hover: number;
115
+ }): PlotOp[];
116
+ /** The colour a meter's value takes: the accent, the warning colour from `warn`, critical past `critical`. */
117
+ export declare function levelOf(v: number, warn?: number, critical?: number, prev?: number, band?: number): 0 | 1 | 2;
118
+ export declare function levelColor(p: Palette, level: number): Rgba;
119
+ /** A meter: a slim track with its fill (eased), or cells; indeterminate, a band that sweeps across. */
120
+ export declare function meterScene(o: {
121
+ /** 0–1, as eased. */
122
+ fraction: number;
123
+ /** 0 (ok) to 2 (critical), as eased, for a continuous bar's colour. */
124
+ level: number;
125
+ min: number;
126
+ max: number;
127
+ warn?: number;
128
+ critical?: number;
129
+ segments?: number;
130
+ /** 0–1 through the sweep, or undefined when the value is known. */
131
+ sweep?: number;
132
+ w: number;
133
+ h: number;
134
+ palette: Palette;
135
+ }): PlotOp[];
136
+ /** Paints ops on a 2D canvas (already scaled to CSS px). */
137
+ export declare function paintOps(ctx: CanvasRenderingContext2D, ops: readonly PlotOp[]): void;
@@ -0,0 +1,85 @@
1
+ import type { ReadoutState, Ring } from "../core/readout.js";
2
+ import type { ReadoutField } from "../core/schema.js";
3
+ import { type UiLook } from "./look.js";
4
+ import { type Palette, type PlotOp } from "./readout-model.js";
5
+ import { type Rgba } from "./rgba.js";
6
+ export type ReadoutFont = "value" | "mono" | "meta";
7
+ /** A piece of text the GPU overlay draws in place of the DOM's (which it hides). */
8
+ export interface TextItem {
9
+ readonly el: HTMLElement;
10
+ /** Drawn left to right from the element's left edge, or right-aligned at its right edge. */
11
+ readonly parts: readonly {
12
+ readonly text: string;
13
+ readonly color: Rgba;
14
+ }[];
15
+ readonly font: ReadoutFont;
16
+ readonly align: "left" | "right";
17
+ /** Clipped to this element (a scroller). */
18
+ readonly clip?: HTMLElement;
19
+ }
20
+ /** Ops in px from `el`'s top-left corner. */
21
+ export interface PlotLayer {
22
+ readonly el: HTMLElement;
23
+ readonly ops: readonly PlotOp[];
24
+ }
25
+ export interface ButtonItem {
26
+ readonly el: HTMLButtonElement;
27
+ /** `chip`: a filter chip (pressed shows a wash); `pill`: a filled pill (jump to latest). */
28
+ readonly look: "chip" | "plain" | "pill";
29
+ readonly alpha: number;
30
+ }
31
+ export interface ReadoutView {
32
+ readonly element: HTMLElement;
33
+ readonly field: ReadoutField;
34
+ /** Where the row's ↑↓ stop lands. */
35
+ readonly focusTarget: HTMLElement;
36
+ /** Graphics, text and buttons as the last frame drew them (the GPU overlay draws these). */
37
+ layers(): readonly PlotLayer[];
38
+ texts(): readonly TextItem[];
39
+ buttons(): readonly ButtonItem[];
40
+ /** Something arrived in the readout's state. */
41
+ notify(): void;
42
+ /** A new field of the same kind (a descriptor swap). */
43
+ retune(field: ReadoutField): void;
44
+ /** Paints its canvas again (after a GPU overlay stopped drawing it). */
45
+ refresh(): void;
46
+ dispose(): void;
47
+ }
48
+ export interface ReadoutEnv {
49
+ readonly doc: Document;
50
+ readonly field: ReadoutField;
51
+ readonly state: ReadoutState;
52
+ /** The row's label; block readouts (graph, table, log) put it in their head. */
53
+ readonly label?: HTMLElement;
54
+ /** True while a GPU overlay draws this readout: the canvas stays blank. */
55
+ gpu?(): boolean;
56
+ /** Called after a frame changed what the readout shows. */
57
+ wake?(): void;
58
+ }
59
+ export declare function paletteOf(el: HTMLElement, now?: number): Palette;
60
+ /** A value's text and unit, as the field formats it. */
61
+ export declare function readoutText(field: ReadoutField, v: unknown): {
62
+ num: string;
63
+ unit: string;
64
+ };
65
+ /** A time as the log shows it: a clock for epoch times, else seconds (a `performance.now()`). */
66
+ export declare function logTime(t: number): string;
67
+ /** Builds the view for a readout field. */
68
+ export declare function createReadout(env: ReadoutEnv): ReadoutView;
69
+ export type { Ring };
70
+ export interface Readout {
71
+ readonly element: HTMLElement;
72
+ /** A value, a sample or rows, as `controls.watch` takes them; `null` clears it. */
73
+ push(value: unknown): void;
74
+ /** The latest value pushed. */
75
+ readonly value: unknown;
76
+ dispose(): void;
77
+ }
78
+ /**
79
+ * A readout on its own, outside a panel: `readout(sparkline({ unit: "ms" }))`, put its element
80
+ * anywhere and `push` values to it. It sleeps when nothing arrives, as it does in a panel.
81
+ */
82
+ export declare function readout(field: ReadoutField, options?: UiLook & {
83
+ readonly doc?: Document;
84
+ readonly label?: string;
85
+ }): Readout;
@@ -0,0 +1,6 @@
1
+ export type Rgba = readonly [number, number, number, number];
2
+ /** Any CSS colour (hex, rgb(), oklch(), color-mix(), calc() inside) as RGBA; `fallback` if not one. */
3
+ export declare function parseCssColor(raw: string, fallback?: Rgba): Rgba;
4
+ export declare const withAlpha: (c: Rgba, k: number) => Rgba;
5
+ export declare const mixRgba: (a: Rgba, b: Rgba, t: number) => Rgba;
6
+ export declare const rgbaCss: (c: Rgba) => string;
@@ -0,0 +1,40 @@
1
+ import { type JSX } from "solid-js";
2
+ import { type Bento as BentoHandle, type BentoOptions } from "./bento.js";
3
+ import { type Split as SplitHandle, type SplitOptions } from "./split.js";
4
+ import { type Timeline as TimelineHandle, type TimelineOptions } from "./timeline.js";
5
+ import { type Tree as TreeHandle, type TreeOptions } from "./tree.js";
6
+ export type SplitProps = SplitOptions & {
7
+ /** One element per pane, in the panes' order; each goes into its pane's body. */
8
+ children?: JSX.Element;
9
+ onReady?: (split: SplitHandle) => void;
10
+ };
11
+ /**
12
+ * `split()` as a component: each child goes into its pane. The split is made once; children
13
+ * that change move into their panes.
14
+ */
15
+ export declare function Split(props: SplitProps): JSX.Element;
16
+ export type TreeProps<T> = TreeOptions<T> & {
17
+ onReady?: (tree: TreeHandle<T>) => void;
18
+ };
19
+ /**
20
+ * `tree()` as a component. New `items` update it in place (rows glide); `selection` and
21
+ * `filter` follow their props; callbacks always call the latest props.
22
+ */
23
+ export declare function Tree<T>(props: TreeProps<T>): JSX.Element;
24
+ export type TimelineProps = TimelineOptions & {
25
+ onReady?: (timeline: TimelineHandle) => void;
26
+ };
27
+ /**
28
+ * `timeline()` as a component. `time`, `playing`, `markers`, `ranges`, `duration`, `fps`,
29
+ * `units` and `speed` follow their props (a `time` it reported itself is left alone); callbacks
30
+ * always call the latest props.
31
+ */
32
+ export declare function Timeline(props: TimelineProps): JSX.Element;
33
+ export type BentoProps = BentoOptions & {
34
+ onReady?: (bento: BentoHandle) => void;
35
+ };
36
+ /**
37
+ * `bento()` as a component. New `items` update it in place (tiles glide); `picks` follows its
38
+ * prop; callbacks always call the latest props.
39
+ */
40
+ export declare function Bento(props: BentoProps): JSX.Element;
@@ -0,0 +1,81 @@
1
+ export type SplitDirection = "row" | "column";
2
+ export interface PaneSpec {
3
+ readonly id: string;
4
+ /** Pixels. Panes without one share what the sized panes leave, by their last sizes. */
5
+ readonly size?: number;
6
+ /** Pixels; 48 unless set. */
7
+ readonly min?: number;
8
+ readonly max?: number;
9
+ /** Collapses to its rail (or header) from its divider, header or `collapse()`. */
10
+ readonly collapsible?: boolean;
11
+ /** Starts collapsed (a saved layout wins). */
12
+ readonly collapsed?: boolean;
13
+ }
14
+ /** What a split saves and reports: each pane's size and whether it's collapsed. */
15
+ export interface SplitLayout {
16
+ readonly sizes: Readonly<Record<string, number>>;
17
+ readonly collapsed: Readonly<Record<string, boolean>>;
18
+ }
19
+ export declare const DEFAULT_MIN = 48;
20
+ /** How far past its minimum a collapsible pane is dragged before it collapses. */
21
+ export declare const COLLAPSE_PAST = 40;
22
+ export declare const paneMin: (p: PaneSpec) => number;
23
+ export declare const paneMax: (p: PaneSpec) => number;
24
+ /** Panes that fill: those without a `size`, or else the last one. */
25
+ export declare function flexible(panes: readonly PaneSpec[]): Set<string>;
26
+ /**
27
+ * Approaches a bound without passing it: 1:1 until `knee` px short of it, then easing in, so a
28
+ * drag slows into its limit instead of stopping dead (and never bands past it).
29
+ */
30
+ export declare function softClamp(x: number, min: number, max: number, knee?: number): number;
31
+ export interface SolveInput {
32
+ readonly panes: readonly PaneSpec[];
33
+ /** Last sizes (px); flexible panes use theirs as shares. */
34
+ readonly sizes: Readonly<Record<string, number>>;
35
+ readonly collapsed: Readonly<Record<string, boolean>>;
36
+ /** The container's length along the split, dividers included. */
37
+ readonly total: number;
38
+ /** Room each divider takes. */
39
+ readonly divider?: number;
40
+ /** A collapsed pane's length (its rail or header). */
41
+ readonly rail?: (id: string) => number;
42
+ /** Sizes held exactly, whatever else is true (a pane mid-collapse). */
43
+ readonly fixed?: Readonly<Record<string, number>>;
44
+ }
45
+ /**
46
+ * Lays panes out along the split. Sized panes keep their size (clamped to min and max) and
47
+ * flexible ones share the rest by their last sizes; when there isn't room for every flexible
48
+ * pane's minimum, sized panes give way, the largest first, down to their own minimums.
49
+ */
50
+ export declare function solve(input: SolveInput): Record<string, number>;
51
+ export interface DragResult {
52
+ /** New sizes for the two panes beside the divider. */
53
+ readonly sizes: Record<string, number>;
54
+ /** A collapsible pane dragged well past its minimum collapses; dragged back, it opens. */
55
+ readonly collapse?: {
56
+ readonly id: string;
57
+ readonly collapsed: boolean;
58
+ };
59
+ }
60
+ /**
61
+ * Moves the divider after pane `index` by `delta` px from where the drag began (`start` holds
62
+ * every pane's size then). The panes on either side trade the distance, each slowing into its
63
+ * own bounds.
64
+ */
65
+ export declare function drag(panes: readonly PaneSpec[], start: Readonly<Record<string, number>>, index: number, delta: number, collapsed?: Readonly<Record<string, boolean>>): DragResult;
66
+ /** Steps a divider by keys: the pane before it grows by `delta`, within both panes' bounds. */
67
+ export declare function nudge(panes: readonly PaneSpec[], sizes: Readonly<Record<string, number>>, index: number, delta: number): Record<string, number>;
68
+ /**
69
+ * Which pane a divider collapses (Enter, its button): the sized one beside it, else the
70
+ * collapsible one, preferring the pane at the edge.
71
+ */
72
+ export declare function collapsibleSide(panes: readonly PaneSpec[], index: number): string | undefined;
73
+ /**
74
+ * Which edge a pane collapses toward: panes before the first flexible pane go to the start,
75
+ * the rest to the end.
76
+ */
77
+ export declare function edgeOf(panes: readonly PaneSpec[], id: string): "start" | "end";
78
+ /** A saved layout, kept only where it names known panes with sane values. */
79
+ export declare function parseLayout(raw: unknown, panes: readonly PaneSpec[]): SplitLayout | undefined;
80
+ /** A layout as saved: sizes rounded to a tenth of a pixel. */
81
+ export declare function serializeLayout(layout: SplitLayout): string;
@@ -0,0 +1,75 @@
1
+ import { type StoragePort } from "../core/storage.js";
2
+ import { type UiLook } from "./look.js";
3
+ import { type PaneSpec, type SplitDirection, type SplitLayout } from "./split-model.js";
4
+ export interface PaneAction {
5
+ readonly id?: string;
6
+ readonly label: string;
7
+ /** SVG markup, text, or a node of your own. */
8
+ readonly icon?: string | Node;
9
+ readonly pressed?: boolean;
10
+ /** Shown in its tooltip as key chips (the split doesn't bind it). */
11
+ readonly shortcut?: string;
12
+ run(event: MouseEvent): void;
13
+ }
14
+ export interface PaneOptions extends PaneSpec {
15
+ /** A header with this title (a dock); collapsed, it's the rail's label too. */
16
+ readonly title?: string;
17
+ /** The collapsed rail's label, when it isn't the title. */
18
+ readonly rail?: string;
19
+ /** Beside the title, dimmed: a count or a word. */
20
+ readonly count?: string | number;
21
+ /** Icon buttons at the header's end. */
22
+ readonly actions?: readonly PaneAction[];
23
+ /** The body scrolls (on by default for panes with a header). */
24
+ readonly scroll?: boolean;
25
+ /** Content for the body (or fill `split.pane(id).body` yourself). */
26
+ readonly element?: Node;
27
+ }
28
+ export interface SplitOptions extends UiLook {
29
+ readonly direction?: SplitDirection;
30
+ readonly panes: readonly PaneOptions[];
31
+ /** Saves sizes and collapsed panes under this id (with `storage`). */
32
+ readonly id?: string;
33
+ /** Where layouts are saved: localStorage unless set; `null` saves nothing. */
34
+ readonly storage?: StoragePort | null;
35
+ /** After every change of size or collapse, drag frames and springs included. */
36
+ readonly onLayout?: (layout: SplitLayout) => void;
37
+ /**
38
+ * Header actions, collapse buttons and the rails of collapsed panes take codes in the page's
39
+ * hint mode (`s`), on by default; a code presses its button.
40
+ */
41
+ readonly hints?: boolean;
42
+ }
43
+ export interface SplitPane {
44
+ readonly id: string;
45
+ /** The pane itself. */
46
+ readonly element: HTMLElement;
47
+ /** Where content goes. */
48
+ readonly body: HTMLElement;
49
+ readonly header: HTMLElement | undefined;
50
+ /** Its size along the split (px), as shown now. */
51
+ readonly size: number;
52
+ readonly collapsed: boolean;
53
+ collapse(collapsed?: boolean): void;
54
+ resize(size: number): void;
55
+ title(text: string, count?: string | number): void;
56
+ }
57
+ export interface Split {
58
+ readonly element: HTMLElement;
59
+ pane(id: string): SplitPane;
60
+ layout(): SplitLayout;
61
+ /** Collapses or opens a pane (springing, interruptibly); no argument toggles. */
62
+ collapse(id: string, collapsed?: boolean): void;
63
+ resize(id: string, size: number): void;
64
+ /** Back to the panes' own sizes, one pane or all. */
65
+ reset(id?: string): void;
66
+ dispose(): void;
67
+ }
68
+ /**
69
+ * Resizable panes along a row or a column, divided by hairlines with a wider grip. Drag a divider
70
+ * to resize (each pane slows into its min and max), double-click it to reset, or focus it and use
71
+ * the arrow keys (⇧ for bigger steps) and Enter to collapse. Collapsing springs the pane down to a
72
+ * slim rail, or to its header in a column, which opens it again. Sizes persist and restore before
73
+ * the first paint. Splits nest: call `split` on another split's pane body.
74
+ */
75
+ export declare function split(container: HTMLElement, options: SplitOptions): Split;
@@ -0,0 +1,83 @@
1
+ /** The visible window: `start` seconds at the left edge, `span` seconds across. */
2
+ export interface TimeView {
3
+ readonly start: number;
4
+ readonly span: number;
5
+ }
6
+ export type TimeUnits = "seconds" | "frames";
7
+ export interface Tick {
8
+ readonly t: number;
9
+ readonly label: string;
10
+ }
11
+ export interface Ticks {
12
+ readonly major: readonly Tick[];
13
+ readonly minor: readonly number[];
14
+ /** Seconds between major ticks. */
15
+ readonly step: number;
16
+ }
17
+ /** A frame's time, rounded to the nearest frame. */
18
+ export declare const toFrame: (t: number, fps: number) => number;
19
+ export declare const frameTime: (t: number, fps: number) => number;
20
+ /** "1.5s", "12s", "1:05", "1:02:05" for seconds; the frame number for frames. */
21
+ export declare function timeLabel(t: number, units: TimeUnits, fps: number, step?: number): string;
22
+ /**
23
+ * Ticks for a view `width` px wide: major ticks at least `minGap` px apart on a nice step (whole
24
+ * frames in frame units), labelled; minor ones between them, down to single frames once a frame
25
+ * is `frameGap` px wide.
26
+ */
27
+ export declare function rulerTicks(view: TimeView, width: number, o: {
28
+ fps: number;
29
+ units?: TimeUnits;
30
+ minGap?: number;
31
+ frameGap?: number;
32
+ }): Ticks;
33
+ /** Clamps a view to `[0, duration]` with a little room (`pad` of the span) at either end. */
34
+ export declare function clampView(view: TimeView, duration: number, o?: {
35
+ minSpan?: number;
36
+ pad?: number;
37
+ }): TimeView;
38
+ /** Zooms by `factor` (above 1 zooms in) keeping time `at` under the same point. */
39
+ export declare function zoomView(view: TimeView, factor: number, at: number): TimeView;
40
+ /** The view panned so `t` sits at least `margin` (of the span) inside it, or unchanged. */
41
+ export declare function followView(view: TimeView, t: number, margin?: number): TimeView;
42
+ /**
43
+ * The playhead's soft pull toward a marker while it's scrubbed: within `radius` px it follows the
44
+ * pointer by a curve that's flat at the marker (it sticks there) and meets the pointer at the
45
+ * edge, with no jump. Returns the time to show and the marker it rests on, if any.
46
+ */
47
+ export declare function magnet(raw: number, stops: readonly number[], pxPerSec: number, radius?: number, stick?: number): {
48
+ t: number;
49
+ stop: number | null;
50
+ };
51
+ /**
52
+ * Where a scrub lands: on a marker it's resting on, else on the nearest frame (with `frames`),
53
+ * clamped to `[0, duration]`.
54
+ */
55
+ export declare function scrubTime(raw: number, o: {
56
+ duration: number;
57
+ fps: number;
58
+ pxPerSec: number;
59
+ stops?: readonly number[];
60
+ frames?: boolean;
61
+ radius?: number;
62
+ }): {
63
+ t: number;
64
+ shown: number;
65
+ stop: number | null;
66
+ };
67
+ /** The marker before (`-1`) or after (`1`) time `t`, by a frame's margin, or null. */
68
+ export declare function adjacentMarker<M extends {
69
+ readonly time: number;
70
+ }>(markers: readonly M[], t: number, dir: 1 | -1, fps: number): M | null;
71
+ /** Advances playing time by `dt`, looping inside `loop` (or the whole) when asked. */
72
+ export declare function advance(t: number, dt: number, o: {
73
+ duration: number;
74
+ loop?: {
75
+ start: number;
76
+ end: number;
77
+ } | null;
78
+ repeat?: boolean;
79
+ }): {
80
+ t: number;
81
+ ended: boolean;
82
+ wrapped: boolean;
83
+ };
@@ -0,0 +1,76 @@
1
+ import { type UiLook } from "./look.js";
2
+ import { type TimeUnits, type TimeView } from "./timeline-model.js";
3
+ export interface TimelineMarker {
4
+ readonly id: string;
5
+ /** Seconds. */
6
+ readonly time: number;
7
+ readonly label?: string;
8
+ /** A colour of its own (CSS); the accent otherwise. */
9
+ readonly color?: string;
10
+ }
11
+ export interface TimelineRange {
12
+ readonly id: string;
13
+ readonly start: number;
14
+ readonly end: number;
15
+ readonly label?: string;
16
+ /** Playback loops inside it (`I` / `O` make the `"loop"` range, which loops). */
17
+ readonly loop?: boolean;
18
+ }
19
+ export type SeekSource = "scrub" | "key" | "marker" | "play" | "code";
20
+ export interface TimelineOptions extends UiLook {
21
+ /** Seconds. */
22
+ readonly duration: number;
23
+ readonly time?: number;
24
+ readonly playing?: boolean;
25
+ /** Frames per second (30): frame steps, frame labels, and where scrubs land. */
26
+ readonly fps?: number;
27
+ /** What the ruler counts in (`"seconds"`). */
28
+ readonly units?: TimeUnits;
29
+ readonly markers?: readonly TimelineMarker[];
30
+ readonly ranges?: readonly TimelineRange[];
31
+ /** Scrubs land on frames and stick to markers (`true`); `{ frames, markers }` picks. */
32
+ readonly snap?: boolean | {
33
+ readonly frames?: boolean;
34
+ readonly markers?: boolean;
35
+ };
36
+ /** Playback speed (1). */
37
+ readonly speed?: number;
38
+ /** Playback starts over at the end when there's no loop range (`false`: it stops). */
39
+ readonly repeat?: boolean;
40
+ readonly label?: string;
41
+ /** The time moved: scrubbed, stepped, jumped, played (every frame) or set from code. */
42
+ onSeek?(time: number, source: SeekSource): void;
43
+ onPlay?(playing: boolean): void;
44
+ /** Markers were added, moved, renamed or removed (the whole list). */
45
+ onMarkers?(markers: readonly TimelineMarker[]): void;
46
+ /** Ranges were set, moved or cleared (the whole list). */
47
+ onRanges?(ranges: readonly TimelineRange[]): void;
48
+ /** Playback passed a marker. */
49
+ onCross?(marker: TimelineMarker): void;
50
+ /**
51
+ * Play, each marker and the playhead take codes in the page's hint mode (`s`), on by default:
52
+ * a marker's code jumps to it, the playhead's focuses the timeline.
53
+ */
54
+ readonly hints?: boolean;
55
+ }
56
+ export interface Timeline {
57
+ readonly element: HTMLElement;
58
+ readonly time: number;
59
+ readonly playing: boolean;
60
+ readonly markers: readonly TimelineMarker[];
61
+ readonly ranges: readonly TimelineRange[];
62
+ readonly view: TimeView;
63
+ seek(time: number): void;
64
+ play(): void;
65
+ pause(): void;
66
+ toggle(): void;
67
+ /** New duration, markers, ranges, fps, units or speed; the time and view stay. */
68
+ update(next: Partial<Omit<TimelineOptions, "onSeek" | "onPlay" | "onMarkers" | "onRanges" | "onCross">>): void;
69
+ /** Shows the whole duration (animated). */
70
+ fit(): void;
71
+ /** Zooms by `factor` (above 1 zooms in) around a time (the playhead's by default). */
72
+ zoom(factor: number, at?: number): void;
73
+ focus(): void;
74
+ dispose(): void;
75
+ }
76
+ export declare function timeline(container: HTMLElement, options: TimelineOptions): Timeline;
@@ -0,0 +1,19 @@
1
+ import { type UiLook } from "./look.js";
2
+ export interface ToastOptions {
3
+ /** Ms it stays (3200; errors 6000). */
4
+ readonly linger?: number;
5
+ readonly tone?: "info" | "warn" | "error";
6
+ /** A button in the pill (Undo, say). */
7
+ readonly action?: {
8
+ readonly label: string;
9
+ run(): void;
10
+ };
11
+ readonly look?: UiLook;
12
+ /** Where it lives, to take that element's tokens (a page); the overlay layer by default. */
13
+ readonly within?: HTMLElement;
14
+ readonly doc?: Document;
15
+ }
16
+ export interface Toast {
17
+ dismiss(): void;
18
+ }
19
+ export declare function toast(message: string, options?: ToastOptions): Toast;
@@ -0,0 +1,19 @@
1
+ import type { UiLook } from "./look.js";
2
+ import { type Placement } from "./overlay-model.js";
3
+ export interface TooltipOptions {
4
+ /** A key or combination (`"Mod+K"`, `"Space"`): shown as chips after the text. */
5
+ readonly shortcut?: string;
6
+ /** `top` by default. */
7
+ readonly placement?: Placement;
8
+ /** Ms over the target before it shows (500); a hand-off shows at once. */
9
+ readonly delay?: number;
10
+ readonly look?: UiLook;
11
+ }
12
+ /** Text, a function read each time it shows, or text with options. */
13
+ export type TooltipContent = string | (() => string) | ({
14
+ readonly text: string | (() => string);
15
+ } & TooltipOptions);
16
+ /** Shows `content` over `el` after a pause (or on keyboard focus). Returns the remover. */
17
+ export declare function tooltip(el: HTMLElement, content: TooltipContent, options?: TooltipOptions): () => void;
18
+ /** Turns on `data-tip` (and `data-tip-shortcut`, `data-tip-placement`) tooltips in a document. */
19
+ export declare function tooltips(doc?: Document): void;