@kitlangton/knobs 0.0.0-stage → 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.
- package/LICENSE +21 -0
- package/README.md +1414 -2
- package/dist/dev/chunk-4km87zfx.js +155 -0
- package/dist/dev/chunk-5cr85vv6.js +17726 -0
- package/dist/dev/chunk-693jd1gj.js +1435 -0
- package/dist/dev/chunk-gezhc24g.js +1132 -0
- package/dist/dev/index.js +1660 -0
- package/dist/index.js +2651 -0
- package/dist/panel.js +26404 -0
- package/dist/react.js +326 -0
- package/dist/solid.js +336 -0
- package/dist/themes.js +235 -0
- package/dist/types/core/adapter.d.ts +21 -0
- package/dist/types/core/code.d.ts +3 -0
- package/dist/types/core/color.d.ts +27 -0
- package/dist/types/core/easing.d.ts +21 -0
- package/dist/types/core/internal.d.ts +18 -0
- package/dist/types/core/keys.d.ts +21 -0
- package/dist/types/core/mutate.d.ts +21 -0
- package/dist/types/core/ramp.d.ts +150 -0
- package/dist/types/core/random.d.ts +62 -0
- package/dist/types/core/readout.d.ts +73 -0
- package/dist/types/core/remote.d.ts +260 -0
- package/dist/types/core/schema.d.ts +550 -0
- package/dist/types/core/spring.d.ts +38 -0
- package/dist/types/core/storage.d.ts +8 -0
- package/dist/types/core/store.d.ts +536 -0
- package/dist/types/core/theme.d.ts +27 -0
- package/dist/types/core/variants.d.ts +25 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/panel/attach.d.ts +5 -0
- package/dist/types/panel/choice.d.ts +56 -0
- package/dist/types/panel/die.d.ts +121 -0
- package/dist/types/panel/editors.d.ts +170 -0
- package/dist/types/panel/flash.d.ts +13 -0
- package/dist/types/panel/folds.d.ts +14 -0
- package/dist/types/panel/give.d.ts +73 -0
- package/dist/types/panel/gpu/atlas.d.ts +40 -0
- package/dist/types/panel/gpu/color.d.ts +128 -0
- package/dist/types/panel/gpu/die.wgsl.d.ts +1 -0
- package/dist/types/panel/gpu/renderer.d.ts +784 -0
- package/dist/types/panel/gpu/shader.wgsl.d.ts +2 -0
- package/dist/types/panel/haptics.d.ts +12 -0
- package/dist/types/panel/has.d.ts +13 -0
- package/dist/types/panel/hints.d.ts +45 -0
- package/dist/types/panel/icons.d.ts +30 -0
- package/dist/types/panel/index.d.ts +11 -0
- package/dist/types/panel/inspector.d.ts +50 -0
- package/dist/types/panel/measure.d.ts +6 -0
- package/dist/types/panel/menu.d.ts +38 -0
- package/dist/types/panel/morph.d.ts +38 -0
- package/dist/types/panel/motion.d.ts +77 -0
- package/dist/types/panel/optkeys.d.ts +7 -0
- package/dist/types/panel/page-hints.d.ts +82 -0
- package/dist/types/panel/page.d.ts +423 -0
- package/dist/types/panel/panel.d.ts +43 -0
- package/dist/types/panel/position.d.ts +5 -0
- package/dist/types/panel/prefs.d.ts +28 -0
- package/dist/types/panel/presets.d.ts +60 -0
- package/dist/types/panel/ramp-morph.d.ts +117 -0
- package/dist/types/panel/ramps.d.ts +14 -0
- package/dist/types/panel/readout-row.d.ts +21 -0
- package/dist/types/panel/reel.d.ts +43 -0
- package/dist/types/panel/reveal.d.ts +15 -0
- package/dist/types/panel/rolling-css.d.ts +2 -0
- package/dist/types/panel/scope.d.ts +19 -0
- package/dist/types/panel/search.d.ts +36 -0
- package/dist/types/panel/seg.d.ts +15 -0
- package/dist/types/panel/session.d.ts +21 -0
- package/dist/types/panel/slider.d.ts +92 -0
- package/dist/types/panel/smear.d.ts +7 -0
- package/dist/types/panel/sound.d.ts +137 -0
- package/dist/types/panel/spin.d.ts +13 -0
- package/dist/types/panel/styles.d.ts +11 -0
- package/dist/types/panel/swipe.d.ts +114 -0
- package/dist/types/panel/switcher.d.ts +20 -0
- package/dist/types/panel/tabs.d.ts +31 -0
- package/dist/types/panel/thumb.d.ts +36 -0
- package/dist/types/panel/toolbar-grid.d.ts +6 -0
- package/dist/types/react/Knobs.d.ts +15 -0
- package/dist/types/react/VariantSwitcher.d.ts +10 -0
- package/dist/types/react/index.d.ts +3 -0
- package/dist/types/react/useControls.d.ts +11 -0
- package/dist/types/solid/index.d.ts +50 -0
- package/dist/types/themes/index.d.ts +1 -0
- package/dist/types/ui/activity-css.d.ts +1 -0
- package/dist/types/ui/activity-model.d.ts +122 -0
- package/dist/types/ui/activity.d.ts +67 -0
- package/dist/types/ui/bento-css.d.ts +1 -0
- package/dist/types/ui/bento-model.d.ts +96 -0
- package/dist/types/ui/bento.d.ts +77 -0
- package/dist/types/ui/commands.d.ts +11 -0
- package/dist/types/ui/confirm.d.ts +13 -0
- package/dist/types/ui/follow.d.ts +17 -0
- package/dist/types/ui/index.d.ts +24 -0
- package/dist/types/ui/layer.d.ts +70 -0
- package/dist/types/ui/look.d.ts +62 -0
- package/dist/types/ui/menu.d.ts +63 -0
- package/dist/types/ui/overlay-css.d.ts +1 -0
- package/dist/types/ui/overlay-model.d.ts +87 -0
- package/dist/types/ui/palette.d.ts +38 -0
- package/dist/types/ui/picker-css.d.ts +1 -0
- package/dist/types/ui/picker-model.d.ts +53 -0
- package/dist/types/ui/picker.d.ts +67 -0
- package/dist/types/ui/popover.d.ts +35 -0
- package/dist/types/ui/react.d.ts +72 -0
- package/dist/types/ui/readout-css.d.ts +1 -0
- package/dist/types/ui/readout-model.d.ts +137 -0
- package/dist/types/ui/readouts.d.ts +85 -0
- package/dist/types/ui/rgba.d.ts +6 -0
- package/dist/types/ui/solid.d.ts +63 -0
- package/dist/types/ui/split-model.d.ts +135 -0
- package/dist/types/ui/split.d.ts +92 -0
- package/dist/types/ui/timeline-model.d.ts +83 -0
- package/dist/types/ui/timeline.d.ts +76 -0
- package/dist/types/ui/toast.d.ts +19 -0
- package/dist/types/ui/tooltip.d.ts +19 -0
- package/dist/types/ui/tree-model.d.ts +125 -0
- package/dist/types/ui/tree.d.ts +95 -0
- package/dist/ui-react.js +9230 -0
- package/dist/ui-solid.js +9162 -0
- package/dist/ui.js +11826 -0
- package/package.json +99 -4
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactNode } from "react";
|
|
2
|
+
import { type Activity as ActivityHandle, type ActivityOptions } from "./activity.js";
|
|
3
|
+
import { type Bento as BentoHandle, type BentoOptions } from "./bento.js";
|
|
4
|
+
import { type OptionPicker as OptionPickerHandle, type OptionPickerOptions, type PickerOption } from "./picker.js";
|
|
5
|
+
import { type Split as SplitHandle, type SplitOptions } from "./split.js";
|
|
6
|
+
import { type Timeline as TimelineHandle, type TimelineOptions } from "./timeline.js";
|
|
7
|
+
import { type Tree as TreeHandle, type TreeOptions } from "./tree.js";
|
|
8
|
+
export type SplitProps = SplitOptions & {
|
|
9
|
+
/** One child per pane, in the panes' order; each renders into its pane's body. */
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
className?: string;
|
|
12
|
+
style?: CSSProperties;
|
|
13
|
+
onReady?: (split: SplitHandle) => void;
|
|
14
|
+
};
|
|
15
|
+
/** `split()` as a component: its children portal into the panes. */
|
|
16
|
+
export declare function Split(props: SplitProps): import("react").JSX.Element;
|
|
17
|
+
export type TreeProps<T> = TreeOptions<T> & {
|
|
18
|
+
className?: string;
|
|
19
|
+
style?: CSSProperties;
|
|
20
|
+
onReady?: (tree: TreeHandle<T>) => void;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* `tree()` as a component. New `items` update it in place (rows glide); `selection` and
|
|
24
|
+
* `filter` follow their props; callbacks always call the latest props.
|
|
25
|
+
*/
|
|
26
|
+
export declare function Tree<T>(props: TreeProps<T>): import("react").JSX.Element;
|
|
27
|
+
export type TimelineProps = TimelineOptions & {
|
|
28
|
+
className?: string;
|
|
29
|
+
style?: CSSProperties;
|
|
30
|
+
onReady?: (timeline: TimelineHandle) => void;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* `timeline()` as a component. `time`, `playing`, `markers`, `ranges`, `duration`, `fps`,
|
|
34
|
+
* `units` and `speed` follow their props (a `time` it reported itself is left alone); callbacks
|
|
35
|
+
* always call the latest props.
|
|
36
|
+
*/
|
|
37
|
+
export declare function Timeline(props: TimelineProps): import("react").JSX.Element;
|
|
38
|
+
export type BentoProps = BentoOptions & {
|
|
39
|
+
className?: string;
|
|
40
|
+
style?: CSSProperties;
|
|
41
|
+
onReady?: (bento: BentoHandle) => void;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* `bento()` as a component. New `items` update it in place (tiles glide); `picks` follows its
|
|
45
|
+
* prop; callbacks always call the latest props.
|
|
46
|
+
*/
|
|
47
|
+
export declare function Bento(props: BentoProps): import("react").JSX.Element;
|
|
48
|
+
export type ActivityProps = ActivityOptions & {
|
|
49
|
+
className?: string;
|
|
50
|
+
style?: CSSProperties;
|
|
51
|
+
onReady?: (activity: ActivityHandle) => void;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* `activity()` as a component. New `events` update it in place (rows keep their identity and
|
|
55
|
+
* glide); `selected` follows its prop; callbacks always call the latest props.
|
|
56
|
+
*/
|
|
57
|
+
export declare function Activity(props: ActivityProps): import("react").JSX.Element;
|
|
58
|
+
export type OptionPickerProps = Omit<OptionPickerOptions, "body"> & {
|
|
59
|
+
/** An option's parameters, portalled under it the first time it opens (a `<Knobs>` panel, say). */
|
|
60
|
+
children?: (option: PickerOption) => ReactNode;
|
|
61
|
+
className?: string;
|
|
62
|
+
style?: CSSProperties;
|
|
63
|
+
onReady?: (picker: OptionPickerHandle) => void;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* `optionPicker()` as a component: `children` renders each opened option's parameters into its
|
|
67
|
+
* place through a portal, so context and state carry through. `options`, `selected` and
|
|
68
|
+
* `chosen` follow their props; callbacks always call the latest props.
|
|
69
|
+
*/
|
|
70
|
+
export declare function OptionPicker(props: OptionPickerProps): import("react").JSX.Element;
|
|
71
|
+
export type { ActivityEvent } from "./activity-model.js";
|
|
72
|
+
export type { PickerOption } from "./picker.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const readoutCss = "\n/* Readouts: live values, sparklines, graphs, meters, tables and logs. */\n:where(.knobs-ui) :is(.knobs-rd-chip,.knobs-rd-copy,.knobs-rd-jump,.knobs-rd-find,.knobs-rd-match){all:revert;box-sizing:border-box;\n font:inherit;color:inherit;letter-spacing:inherit;background:transparent;border:0;border-radius:0;margin:0;padding:0;min-width:0;box-shadow:none;outline:none}\n.knobs-ui.knobs-rd,.knobs-rd *{box-sizing:border-box}\n.knobs-ui.knobs-rd{display:flex;align-items:center;gap:8px;min-width:0;color:var(--ku-text);letter-spacing:normal}\n.knobs-rd .knobs-rd-v{font:var(--ku-font-weight) var(--ku-value-size)/1.25 var(--ku-value-font);font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";white-space:nowrap}\n.knobs-rd .knobs-rd-mono{font:400 var(--ku-text-meta)/1.25 var(--ku-mono);font-variant-numeric:tabular-nums;white-space:pre}\n.knobs-rd .knobs-rd-meta{font:var(--ku-font-weight) var(--ku-text-meta)/1.25 var(--ku-font);white-space:nowrap}\n.knobs-rd .knobs-rd-value{display:inline-flex;justify-content:flex-end;align-items:baseline;flex:none;margin-left:auto;contain:layout paint style}\n.knobs-rd .knobs-rd-num{color:var(--ku-text);text-align:right;transition:color .15s}\n.knobs-rd .knobs-rd-unit{margin-left:var(--k-unit-gap,.28em);color:var(--ku-dim)}\n.knobs-rd .knobs-rd-unit[hidden]{display:none}\n.knobs-rd.knobs-rd-watch{flex:1 1 auto;justify-content:flex-end}\n.knobs-rd.knobs-rd-spark,.knobs-rd.knobs-rd-meter{flex:0 1 64%;margin-left:auto}\n.knobs-rd .knobs-rd-plot{display:block;flex:1 1 0;min-width:24px;height:max(12px,calc(var(--ku-control-h) - 2px))}\n.knobs-rd.knobs-rd-meter>.knobs-rd-plot{height:8px}\n.knobs-rd.knobs-rd-spark[data-hover] .knobs-rd-num{color:var(--ku-label)}\n.knobs-rd .knobs-rd-cap,.knobs-rd .knobs-rd-head>span:first-child{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ku-label);font-weight:var(--ku-label-weight)}\n\n/* Blocks: a head line (the label and what goes with it) over a body. In a panel the row grows to fit. */\n.knobs-panel .knobs-row.knobs-rd-rowblock{display:block;height:auto}\n.knobs-rd.knobs-rd-block{display:flex;flex-direction:column;align-items:stretch;gap:2px;width:100%}\n.knobs-rd .knobs-rd-head{display:flex;align-items:center;gap:0 10px;min-height:var(--ku-row);min-width:0}\n/* The title keeps its words; the legend or tools beside it give way (see fitHead). */\n.knobs-rd .knobs-rd-head>span:first-child{flex:0 0 auto;max-width:100%}\n.knobs-rd .knobs-rd-legend{display:flex;align-items:center;gap:12px;margin-left:auto;min-width:0;overflow:hidden}\n.knobs-rd .knobs-rd-tools{display:flex;align-items:center;gap:10px;margin-left:auto;min-width:0}\n.knobs-rd .knobs-rd-tools>.knobs-rd-find{flex:1 1 56px;min-width:56px;max-width:84px}\n.knobs-rd .knobs-rd-head[data-short] .knobs-rd-legend .knobs-rd-unit{display:none}\n.knobs-rd .knobs-rd-head[data-wrap]{flex-wrap:wrap;padding-bottom:4px}\n.knobs-rd .knobs-rd-head[data-wrap]>span:first-child{display:flex;align-items:center;min-height:var(--ku-row)}\n.knobs-rd .knobs-rd-head[data-wrap]>:is(.knobs-rd-legend,.knobs-rd-tools){flex:1 0 100%;margin-left:0}\n.knobs-rd .knobs-rd-head[data-wrap]>.knobs-rd-tools{flex-wrap:wrap;row-gap:4px}\n.knobs-rd .knobs-rd-key{display:inline-flex;align-items:center;gap:5px;white-space:nowrap}\n.knobs-rd .knobs-rd-swatch{flex:none;width:8px;height:2px;border-radius:1px}\n.knobs-rd .knobs-rd-name{color:var(--ku-label)}\n.knobs-rd .knobs-rd-key>.knobs-rd-value{margin-left:2px}\n.knobs-rd .knobs-rd-plotbox{position:relative;outline:none;margin-bottom:6px}\n.knobs-rd .knobs-rd-plotbox>.knobs-rd-plot{position:absolute;inset:0;width:100%;height:100%;min-width:0}\n.knobs-rd .knobs-rd-tick,.knobs-rd .knobs-rd-mark{position:absolute;color:var(--ku-dim);pointer-events:none;line-height:14px;height:14px}\n.knobs-rd .knobs-rd-tick{left:0}\n.knobs-rd .knobs-rd-tick[data-at=\"top\"]{top:-2px}\n.knobs-rd .knobs-rd-tick[data-at=\"bottom\"]{bottom:0}\n.knobs-rd .knobs-rd-mark{right:0;top:0}\n.knobs-rd .knobs-rd-mark[hidden]{display:none}\n\n.knobs-rd .knobs-rd-grid{display:grid;column-gap:14px;align-items:center;outline:none;margin-bottom:4px}\n.knobs-rd .knobs-rd-tr{display:contents}\n.knobs-rd .knobs-rd-tr>*{height:calc(var(--ku-row) * .72);line-height:calc(var(--ku-row) * .72);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.knobs-rd .knobs-rd-thead>*{height:var(--ku-row);line-height:var(--ku-row)}\n.knobs-rd .knobs-rd-th{color:var(--ku-dim);text-align:right}\n.knobs-rd .knobs-rd-td{color:var(--ku-text)}\n.knobs-rd .knobs-rd-td:first-child{color:var(--ku-label)}\n.knobs-rd .knobs-rd-td[data-num]{text-align:right}\n\n.knobs-rd.knobs-rd-log{--rd-lh:18px}\n.knobs-rd .knobs-rd-chips{display:inline-flex;align-items:center;gap:2px;margin-right:auto}\n.knobs-ui .knobs-rd-chip,.knobs-ui .knobs-rd-copy{height:calc(var(--ku-control-h) - 2px);padding:0 7px;border-radius:min(var(--ku-radius),5px);color:var(--ku-label);cursor:pointer;transition:background .15s,color .15s}\n.knobs-ui .knobs-rd-chip:hover,.knobs-ui .knobs-rd-copy:hover{background:var(--ku-wash);color:var(--ku-text)}\n.knobs-ui .knobs-rd-chip[aria-checked=\"true\"]{background:var(--ku-wash-hi);color:var(--ku-text)}\n.knobs-ui .knobs-rd-find{width:84px;height:calc(var(--ku-control-h) - 2px);padding:0 7px;border-radius:min(var(--ku-radius),5px);\n background:color-mix(in srgb,var(--ku-surface-hi) 60%,transparent);color:var(--ku-text);caret-color:var(--ku-accent)}\n.knobs-rd .knobs-rd-find::placeholder{color:var(--ku-dim)}\n.knobs-rd .knobs-rd-find::-webkit-search-cancel-button{display:none}\n.knobs-rd .knobs-rd-logbody{position:relative;margin-bottom:6px}\n.knobs-rd .knobs-rd-scroll{position:relative;overflow-x:hidden;overflow-y:auto;height:calc(var(--knobs-rd-lines,8) * var(--rd-lh));outline:none;\n background:color-mix(in srgb,var(--ku-surface-hi) 45%,transparent);border-radius:min(var(--ku-radius),6px);scrollbar-width:thin;overscroll-behavior:contain}\n.knobs-rd .knobs-rd-space{position:relative;width:100%}\n.knobs-rd .knobs-rd-line{position:absolute;left:0;right:0;top:0;display:flex;gap:10px;height:var(--rd-lh);line-height:var(--rd-lh);padding:0 8px;contain:layout style;will-change:transform}\n.knobs-rd .knobs-rd-line[hidden]{display:none}\n.knobs-rd .knobs-rd-time{flex:none;color:var(--ku-dim)}\n.knobs-rd .knobs-rd-text{min-width:0;overflow:hidden;text-overflow:ellipsis;color:var(--ku-text)}\n.knobs-rd .knobs-rd-line[data-level=\"warn\"]>.knobs-rd-text{color:var(--ku-warn)}\n.knobs-rd .knobs-rd-line[data-level=\"error\"]>.knobs-rd-text{color:var(--ku-critical)}\n.knobs-rd .knobs-rd-line[data-level=\"debug\"]>.knobs-rd-text{color:var(--ku-dim)}\n.knobs-ui .knobs-rd-match{color:var(--ku-accent)}\n.knobs-ui .knobs-rd-jump{position:absolute;left:50%;bottom:6px;height:20px;padding:0 10px;border-radius:calc(var(--ku-radius) * 999);\n background:var(--ku-accent);color:var(--ku-bg);opacity:0;transform:translate(-50%,6px);cursor:pointer;white-space:nowrap}\n\n/* Under the GPU overlay the readouts' DOM stays for focus, hit-testing and screen readers; the\n overlay draws them. Their filter field sits above it. */\n.knobs-panel[data-gpu-active=\"true\"] .knobs-rd :where(*:not(.knobs-rd-find)),.knobs-panel[data-gpu-active=\"true\"] .knobs-rd{color:transparent!important;background:transparent!important;border-color:transparent!important;box-shadow:none!important}\n.knobs-panel[data-gpu-active=\"true\"] .knobs-rd-plot{visibility:hidden}\n.knobs-panel[data-gpu-active=\"true\"] .knobs-rd-find{position:relative;z-index:2147480003}\n@media(prefers-reduced-motion:reduce){.knobs-rd :where(*){transition:none!important}}\n";
|
|
@@ -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,63 @@
|
|
|
1
|
+
import { type JSX } from "solid-js";
|
|
2
|
+
import { type Activity as ActivityHandle, type ActivityOptions } from "./activity.js";
|
|
3
|
+
import { type Bento as BentoHandle, type BentoOptions } from "./bento.js";
|
|
4
|
+
import { type OptionPicker as OptionPickerHandle, type OptionPickerOptions, type PickerOption } from "./picker.js";
|
|
5
|
+
import { type Split as SplitHandle, type SplitOptions } from "./split.js";
|
|
6
|
+
import { type Timeline as TimelineHandle, type TimelineOptions } from "./timeline.js";
|
|
7
|
+
import { type Tree as TreeHandle, type TreeOptions } from "./tree.js";
|
|
8
|
+
export type SplitProps = SplitOptions & {
|
|
9
|
+
/** One element per pane, in the panes' order; each goes into its pane's body. */
|
|
10
|
+
children?: JSX.Element;
|
|
11
|
+
onReady?: (split: SplitHandle) => void;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* `split()` as a component: each child goes into its pane. The split is made once; children
|
|
15
|
+
* that change move into their panes.
|
|
16
|
+
*/
|
|
17
|
+
export declare function Split(props: SplitProps): JSX.Element;
|
|
18
|
+
export type TreeProps<T> = TreeOptions<T> & {
|
|
19
|
+
onReady?: (tree: TreeHandle<T>) => void;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* `tree()` as a component. New `items` update it in place (rows glide); `selection` and
|
|
23
|
+
* `filter` follow their props; callbacks always call the latest props.
|
|
24
|
+
*/
|
|
25
|
+
export declare function Tree<T>(props: TreeProps<T>): JSX.Element;
|
|
26
|
+
export type TimelineProps = TimelineOptions & {
|
|
27
|
+
onReady?: (timeline: TimelineHandle) => void;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* `timeline()` as a component. `time`, `playing`, `markers`, `ranges`, `duration`, `fps`,
|
|
31
|
+
* `units` and `speed` follow their props (a `time` it reported itself is left alone); callbacks
|
|
32
|
+
* always call the latest props.
|
|
33
|
+
*/
|
|
34
|
+
export declare function Timeline(props: TimelineProps): JSX.Element;
|
|
35
|
+
export type BentoProps = BentoOptions & {
|
|
36
|
+
onReady?: (bento: BentoHandle) => void;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* `bento()` as a component. New `items` update it in place (tiles glide); `picks` follows its
|
|
40
|
+
* prop; callbacks always call the latest props.
|
|
41
|
+
*/
|
|
42
|
+
export declare function Bento(props: BentoProps): JSX.Element;
|
|
43
|
+
export type ActivityProps = ActivityOptions & {
|
|
44
|
+
onReady?: (activity: ActivityHandle) => void;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* `activity()` as a component. New `events` update it in place (rows keep their identity and
|
|
48
|
+
* glide); `selected` follows its prop; callbacks always call the latest props.
|
|
49
|
+
*/
|
|
50
|
+
export declare function Activity(props: ActivityProps): JSX.Element;
|
|
51
|
+
export type OptionPickerProps = Omit<OptionPickerOptions, "body"> & {
|
|
52
|
+
/** An option's parameters, rendered once, the first time it opens (a `KnobsPanel`, say). */
|
|
53
|
+
children?: (option: PickerOption) => JSX.Element;
|
|
54
|
+
onReady?: (picker: OptionPickerHandle) => void;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* `optionPicker()` as a component: `children` renders an option's parameters under it, in this
|
|
58
|
+
* component's context. `options`, `selected` and `chosen` follow their props; callbacks always
|
|
59
|
+
* call the latest props.
|
|
60
|
+
*/
|
|
61
|
+
export declare function OptionPicker(props: OptionPickerProps): JSX.Element;
|
|
62
|
+
export type { ActivityEvent } from "./activity-model.js";
|
|
63
|
+
export type { PickerOption } from "./picker.js";
|
|
@@ -0,0 +1,135 @@
|
|
|
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
|
+
/** How far a folding pane is from its open size (0) to its rail (1), by the size it shows. */
|
|
79
|
+
export declare function foldProgress(size: number, open: number, rail: number): number;
|
|
80
|
+
/** How much of each face a folding pane shows (0…1): its content, open, and its rail, folded. */
|
|
81
|
+
export interface FoldFaces {
|
|
82
|
+
readonly content: number;
|
|
83
|
+
readonly rail: number;
|
|
84
|
+
}
|
|
85
|
+
/** A face that's leaving is gone by this far along the fold. */
|
|
86
|
+
export declare const FACE_OUT = 0.4;
|
|
87
|
+
/** A face that's arriving shows only over this last part of the fold. */
|
|
88
|
+
export declare const FACE_IN = 0.3;
|
|
89
|
+
/**
|
|
90
|
+
* The faces at fold progress `p`, from the last frame's. The content and the rail never show
|
|
91
|
+
* together: the content is gone before 40% and the rail starts past 70% (folding or opening).
|
|
92
|
+
*/
|
|
93
|
+
export declare function foldFaces(prev: FoldFaces, p: number): FoldFaces;
|
|
94
|
+
/**
|
|
95
|
+
* The fold's chevron turn (degrees) at progress `p`: half a turn from the open pane's chevron to
|
|
96
|
+
* the rail's, rolling the way the pane's edge travels.
|
|
97
|
+
*/
|
|
98
|
+
export declare function chevronTurn(p: number, edge: "start" | "end"): number;
|
|
99
|
+
/**
|
|
100
|
+
* The fold's chevron, flipping: its width (scaleX) against the rail's chevron at flip progress
|
|
101
|
+
* `f` (0 open, 1 folded). The arms close in to a flat line halfway and open out the other way.
|
|
102
|
+
*/
|
|
103
|
+
export declare function chevronFlip(f: number): number;
|
|
104
|
+
/** How a pane folds: see `SplitOptions.fold`. */
|
|
105
|
+
export interface FoldStyle {
|
|
106
|
+
readonly chevron: "flip" | "turn";
|
|
107
|
+
readonly title: "travel" | "fade";
|
|
108
|
+
/** Seconds, on a spring with no bounce. */
|
|
109
|
+
readonly duration: number;
|
|
110
|
+
}
|
|
111
|
+
export declare const FOLD_STYLE: FoldStyle;
|
|
112
|
+
export declare function foldStyle(options?: Partial<FoldStyle>): FoldStyle;
|
|
113
|
+
/**
|
|
114
|
+
* Where a header's title and the rail's label sit, each from its own box's start edge and the
|
|
115
|
+
* pane's top (`dy` is the title's height less the label's), as measured with the pane open.
|
|
116
|
+
*/
|
|
117
|
+
export interface TitlePlaces {
|
|
118
|
+
readonly title: number;
|
|
119
|
+
readonly label: number;
|
|
120
|
+
readonly dy: number;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The rail's label travelling as the header's title, at fold progress `p`: the offset that puts it
|
|
124
|
+
* on the title (the rail holds on to the pane's moving edge, the header to the inner content that
|
|
125
|
+
* keeps its open size), and its turn from upright (-90°) to the rail's vertical (0°).
|
|
126
|
+
*/
|
|
127
|
+
export declare function titleTravel(at: TitlePlaces, open: number, rail: number, edge: "start" | "end", p: number): {
|
|
128
|
+
x: number;
|
|
129
|
+
y: number;
|
|
130
|
+
turn: number;
|
|
131
|
+
};
|
|
132
|
+
/** A saved layout, kept only where it names known panes with sane values. */
|
|
133
|
+
export declare function parseLayout(raw: unknown, panes: readonly PaneSpec[]): SplitLayout | undefined;
|
|
134
|
+
/** A layout as saved: sizes rounded to a tenth of a pixel. */
|
|
135
|
+
export declare function serializeLayout(layout: SplitLayout): string;
|
|
@@ -0,0 +1,92 @@
|
|
|
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
|
+
/** How collapsible panes fold to their rails and open again. */
|
|
43
|
+
readonly fold?: SplitFold;
|
|
44
|
+
}
|
|
45
|
+
/** How a pane folds (each part has a default). */
|
|
46
|
+
export interface SplitFold {
|
|
47
|
+
/**
|
|
48
|
+
* The collapse chevron: `"flip"` (default) closes it to a flat line and opens it the other way,
|
|
49
|
+
* quickly, on its own snappy spring; `"turn"` spins it half a turn with the fold.
|
|
50
|
+
*/
|
|
51
|
+
readonly chevron?: "flip" | "turn";
|
|
52
|
+
/**
|
|
53
|
+
* The header's title: `"travel"` (default) turns it and slides it into the rail's label, one
|
|
54
|
+
* element all the way; `"fade"` fades it out with the header as the rail's label fades in.
|
|
55
|
+
*/
|
|
56
|
+
readonly title?: "travel" | "fade";
|
|
57
|
+
/** The fold's spring, in seconds (no bounce): 0.3 unless set. */
|
|
58
|
+
readonly duration?: number;
|
|
59
|
+
}
|
|
60
|
+
export interface SplitPane {
|
|
61
|
+
readonly id: string;
|
|
62
|
+
/** The pane itself. */
|
|
63
|
+
readonly element: HTMLElement;
|
|
64
|
+
/** Where content goes. */
|
|
65
|
+
readonly body: HTMLElement;
|
|
66
|
+
readonly header: HTMLElement | undefined;
|
|
67
|
+
/** Its size along the split (px), as shown now. */
|
|
68
|
+
readonly size: number;
|
|
69
|
+
readonly collapsed: boolean;
|
|
70
|
+
collapse(collapsed?: boolean): void;
|
|
71
|
+
resize(size: number): void;
|
|
72
|
+
title(text: string, count?: string | number): void;
|
|
73
|
+
}
|
|
74
|
+
export interface Split {
|
|
75
|
+
readonly element: HTMLElement;
|
|
76
|
+
pane(id: string): SplitPane;
|
|
77
|
+
layout(): SplitLayout;
|
|
78
|
+
/** Collapses or opens a pane (springing, interruptibly); no argument toggles. */
|
|
79
|
+
collapse(id: string, collapsed?: boolean): void;
|
|
80
|
+
resize(id: string, size: number): void;
|
|
81
|
+
/** Back to the panes' own sizes, one pane or all. */
|
|
82
|
+
reset(id?: string): void;
|
|
83
|
+
dispose(): void;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Resizable panes along a row or a column, divided by hairlines with a wider grip. Drag a divider
|
|
87
|
+
* to resize (each pane slows into its min and max), double-click it to reset, or focus it and use
|
|
88
|
+
* the arrow keys (⇧ for bigger steps) and Enter to collapse. Collapsing springs the pane down to a
|
|
89
|
+
* slim rail, or to its header in a column, which opens it again. Sizes persist and restore before
|
|
90
|
+
* the first paint. Splits nest: call `split` on another split's pane body.
|
|
91
|
+
*/
|
|
92
|
+
export declare function split(container: HTMLElement, options: SplitOptions): Split;
|