@kitlangton/knobs 0.3.0 → 0.4.0-next.1

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 (62) hide show
  1. package/README.md +350 -45
  2. package/dist/dev/{chunk-jx1ktap3.js → chunk-1qzffbpa.js} +497 -422
  3. package/dist/dev/{chunk-7xgkerd2.js → chunk-3ygqgy1x.js} +23 -1
  4. package/dist/dev/{chunk-pmkvb9ep.js → chunk-8mabjeyk.js} +1920 -507
  5. package/dist/dev/{chunk-v8ssbcbk.js → chunk-ayb292zj.js} +13 -11
  6. package/dist/dev/index.js +10 -5
  7. package/dist/index.js +25 -1
  8. package/dist/panel.js +2954 -1291
  9. package/dist/react.js +294 -6
  10. package/dist/solid.js +292 -4
  11. package/dist/themes.js +1509 -7
  12. package/dist/types/core/adapter.d.ts +6 -2
  13. package/dist/types/core/appearance.d.ts +44 -0
  14. package/dist/types/core/glide.d.ts +7 -0
  15. package/dist/types/core/motion.d.ts +66 -0
  16. package/dist/types/core/root.d.ts +13 -0
  17. package/dist/types/core/store.d.ts +18 -5
  18. package/dist/types/core/theme.d.ts +10 -3
  19. package/dist/types/index.d.ts +2 -1
  20. package/dist/types/panel/gpu/color.d.ts +6 -0
  21. package/dist/types/panel/motion.d.ts +1 -1
  22. package/dist/types/panel/styles.d.ts +2 -3
  23. package/dist/types/themes/define.d.ts +104 -0
  24. package/dist/types/themes/index.d.ts +11 -0
  25. package/dist/types/themes/knobs.d.ts +5 -0
  26. package/dist/types/themes/void.d.ts +8 -0
  27. package/dist/types/tokens/color.d.ts +42 -0
  28. package/dist/types/tokens/css.d.ts +13 -0
  29. package/dist/types/tokens/evaluate.d.ts +8 -0
  30. package/dist/types/tokens/index.d.ts +4 -0
  31. package/dist/types/tokens/schema.d.ts +1100 -0
  32. package/dist/types/ui/bento-css.d.ts +1 -1
  33. package/dist/types/ui/compare-css.d.ts +1 -0
  34. package/dist/types/ui/compare-frames.d.ts +41 -0
  35. package/dist/types/ui/compare-gpu.d.ts +58 -0
  36. package/dist/types/ui/compare-model.d.ts +169 -0
  37. package/dist/types/ui/compare-mp4.d.ts +29 -0
  38. package/dist/types/ui/compare-strip.d.ts +28 -0
  39. package/dist/types/ui/compare.d.ts +103 -0
  40. package/dist/types/ui/diff-css.d.ts +1 -0
  41. package/dist/types/ui/diff-model.d.ts +128 -0
  42. package/dist/types/ui/diff-scroll.d.ts +29 -0
  43. package/dist/types/ui/diff.d.ts +158 -0
  44. package/dist/types/ui/index.d.ts +10 -2
  45. package/dist/types/ui/look.d.ts +18 -13
  46. package/dist/types/ui/overlay-css.d.ts +1 -1
  47. package/dist/types/ui/react.d.ts +66 -0
  48. package/dist/types/ui/readout-css.d.ts +1 -1
  49. package/dist/types/ui/readout-model.d.ts +5 -1
  50. package/dist/types/ui/rgba.d.ts +11 -0
  51. package/dist/types/ui/scrollbar.d.ts +2 -2
  52. package/dist/types/ui/solid.d.ts +52 -0
  53. package/dist/types/ui/timeline.d.ts +35 -2
  54. package/dist/types/ui/tree.d.ts +7 -2
  55. package/dist/types/ui/walkthrough-css.d.ts +1 -0
  56. package/dist/types/ui/walkthrough-media.d.ts +29 -0
  57. package/dist/types/ui/walkthrough-model.d.ts +447 -0
  58. package/dist/types/ui/walkthrough.d.ts +63 -0
  59. package/dist/ui-react.js +18352 -8462
  60. package/dist/ui-solid.js +17947 -8148
  61. package/dist/ui.js +19240 -9701
  62. package/package.json +6 -1
@@ -0,0 +1,158 @@
1
+ import { type StoragePort } from "../core/storage.js";
2
+ import { type DiffFile, type DiffSource, type DiffTreeItem } from "./diff-model.js";
3
+ import { type UiLook } from "./look.js";
4
+ import { type Tree } from "./tree.js";
5
+ export type DiffLayout = "split" | "unified";
6
+ /** Lines picked in a diff (`onLines`): a range on one side, or across both. */
7
+ export interface DiffLines {
8
+ readonly start: number;
9
+ readonly end: number;
10
+ readonly side?: "deletions" | "additions";
11
+ readonly endSide?: "deletions" | "additions";
12
+ }
13
+ export interface CodeDiffOptions extends UiLook {
14
+ /** A unified patch: `git diff` or `git show` output, one file or many. */
15
+ readonly patch?: string;
16
+ /** Or files as before and after text (`patch` wins when both are set). */
17
+ readonly files?: readonly DiffSource[];
18
+ /** The file on show (its path); the first unless set. */
19
+ readonly file?: string | null;
20
+ /** Side by side (default) or one column. */
21
+ readonly layout?: DiffLayout;
22
+ /** Long lines wrap instead of scrolling sideways. */
23
+ readonly wrap?: boolean;
24
+ /**
25
+ * `"fill"` (default): the card fills its container and the diff scrolls inside it. `"auto"`:
26
+ * the card is as tall as the file's diff, up to `maxHeight`, and springs to each new height;
27
+ * it holds the height the diff will take from the start, so a page doesn't jump as it loads.
28
+ */
29
+ readonly height?: "fill" | "auto";
30
+ /** Pixels, for `height: "auto"` (640, or 70% of the window when that's less); it scrolls past. */
31
+ readonly maxHeight?: number;
32
+ /**
33
+ * Draws only near the viewport (a screen ahead) and lets go of a diff far behind, keeping its
34
+ * height; on by default with `height: "auto"`, for a long page of them. `ready` waits for it.
35
+ */
36
+ readonly lazy?: boolean;
37
+ /**
38
+ * Every file down one scroller, each under its own header (status, path, counts, Viewed)
39
+ * that sticks to the top until the next file's pushes it off; ‹ › and `show()` glide to a
40
+ * file, and the file on show follows the scroll. Each file draws when it's near the view.
41
+ */
42
+ readonly stack?: boolean;
43
+ /** The files marked viewed; with this or `onViewed`, a Viewed check in the header. */
44
+ readonly viewed?: readonly string[];
45
+ /** The header's Viewed check marked or cleared the file on show. */
46
+ readonly onViewed?: (viewed: readonly string[], file: string) => void;
47
+ /** ‹ 2 of 5 › in the header when there are several files (on by default). */
48
+ readonly nav?: boolean;
49
+ /** Shiki themes for the code, dark and light (Pierre's own unless set). */
50
+ readonly syntax?: {
51
+ readonly dark: string;
52
+ readonly light: string;
53
+ };
54
+ /** Its own controls changed the file (‹ ›); `show()` doesn't call it. */
55
+ readonly onFile?: (name: string) => void;
56
+ readonly onLayout?: (layout: DiffLayout) => void;
57
+ readonly onWrap?: (wrap: boolean) => void;
58
+ /** Lines picked by dragging over line numbers; turns line selection on. */
59
+ readonly onLines?: (lines: DiffLines | null, file: string) => void;
60
+ /** Shown with no files. */
61
+ readonly empty?: string;
62
+ /** An element of your own at the start of the header (diffReview puts its list's toggle here). */
63
+ readonly lead?: HTMLElement;
64
+ }
65
+ export interface CodeDiffUpdate {
66
+ readonly patch?: string;
67
+ readonly files?: readonly DiffSource[];
68
+ readonly file?: string | null;
69
+ readonly layout?: DiffLayout;
70
+ readonly wrap?: boolean;
71
+ readonly viewed?: readonly string[];
72
+ /**
73
+ * Switches between filling the container and its diff's own height, in place: the views and
74
+ * their scroll stay. The new height is taken as it is, not sprung, so a container whose size
75
+ * is moving (a card going full window) is followed rather than fought.
76
+ */
77
+ readonly height?: "fill" | "auto";
78
+ readonly maxHeight?: number;
79
+ }
80
+ export interface CodeDiff {
81
+ readonly element: HTMLElement;
82
+ /** The diff's files, counted, in tree order. */
83
+ readonly files: readonly DiffFile[];
84
+ readonly file: string | null;
85
+ readonly layout: DiffLayout;
86
+ readonly wrap: boolean;
87
+ readonly viewed: readonly string[];
88
+ /** Shows a file: the new one fades in over the last, in place. */
89
+ show(name: string): void;
90
+ /** The file before or after the one on show. */
91
+ step(dir: -1 | 1): void;
92
+ /** New input or settings; a new patch keeps the file on show if it's still there. */
93
+ update(next: CodeDiffUpdate): void;
94
+ /** Settles once the first file is drawn. */
95
+ readonly ready: Promise<void>;
96
+ dispose(): void;
97
+ }
98
+ /**
99
+ * A file's `+12 −3`, coloured, for a row of your own (a tree's `detail`); `bar` adds five
100
+ * squares that show the share added and removed, as GitHub does.
101
+ */
102
+ export declare function diffCounts(file: DiffFile, doc?: Document, options?: {
103
+ readonly bar?: boolean;
104
+ }): HTMLElement;
105
+ /** A file's change as a letter (M, A, D, R), for a tree's `icon`. */
106
+ export declare function diffStatus(file: DiffFile, doc?: Document): HTMLElement;
107
+ /**
108
+ * A code diff in a knobs card: a header with the file's path, its counts and Split | Unified
109
+ * over the diff, drawn by `@pierre/diffs` (Shiki highlighting, word-level changes, hunks that
110
+ * expand when the whole files are known). Takes a unified patch or before and after pairs.
111
+ * Changing the file or the layout fades the new view in over the old, in place; each file
112
+ * keeps its scroll. On another surface (a card, a split's pane, a walkthrough's step) it draws
113
+ * no card of its own. Needs `@pierre/diffs` installed; it loads on first use.
114
+ */
115
+ export declare function codeDiff(container: HTMLElement, options: CodeDiffOptions): CodeDiff;
116
+ export interface DiffReviewOptions extends CodeDiffOptions {
117
+ /**
118
+ * Saves whether the file list is open and the files marked viewed, under this id. A viewed
119
+ * file whose change moves on comes back unviewed.
120
+ */
121
+ readonly id?: string;
122
+ readonly storage?: StoragePort | null;
123
+ /** The file list's title ("Files"). */
124
+ readonly title?: string;
125
+ /** The file list's width in pixels when open (256). */
126
+ readonly listSize?: number;
127
+ /** The file list starts open (the default) or closed; what was last chosen wins, with an id. */
128
+ readonly listOpen?: boolean;
129
+ /** A field over the file list that narrows it by path (on by default). */
130
+ readonly search?: boolean;
131
+ }
132
+ export interface DiffReview {
133
+ readonly element: HTMLElement;
134
+ readonly tree: Tree<DiffTreeItem>;
135
+ readonly diff: CodeDiff;
136
+ /** The files marked viewed. */
137
+ readonly viewed: readonly string[];
138
+ /** Marks files viewed or not (a folder's id marks what's in it). */
139
+ setViewed(names: readonly string[], viewed: boolean): void;
140
+ /** Narrows the file list to paths with every word of `query`. */
141
+ filter(query: string): void;
142
+ /** Whether the file list is open. */
143
+ readonly listOpen: boolean;
144
+ /** Springs the file list open or closed (toggles without an argument). */
145
+ toggleList(open?: boolean): void;
146
+ update(next: CodeDiffUpdate): void;
147
+ dispose(): void;
148
+ }
149
+ /**
150
+ * A diff beside its file list, as a review, in one card: the list is a drawer at its left that
151
+ * springs open and shut (0 to 256px, .4 s, no bounce; the diff reflows as it moves) from a
152
+ * button at the start of the diff's header, or ⌘B / Ctrl+B. In it, a field that narrows the list
153
+ * by path, then the changed files as a compact tree (folders folded into paths), each with its
154
+ * change as a letter (M, A, D, R, coloured), its +/− counts and their bar, and a tick to mark it
155
+ * viewed; one highlight glides to the file on show. Viewed files dim, count in the list's title
156
+ * and, with an `id`, are remembered until their change moves on. `tree` and `diff` are the pieces.
157
+ */
158
+ export declare function diffReview(container: HTMLElement, options: DiffReviewOptions): DiffReview;
@@ -1,4 +1,4 @@
1
- export { injectUiStyles, restyle, type RestyleLook, type UiLook, type UiTheme } from "./look.js";
1
+ export { type Appearance, injectUiStyles, restyle, type RestyleLook, type UiLook, type UiTheme, } from "./look.js";
2
2
  export { commandsFrom, type CommandsFromOptions } from "./commands.js";
3
3
  export { confirm, type ConfirmOptions } from "./confirm.js";
4
4
  export { overlayLook, type Anchor } from "./layer.js";
@@ -12,13 +12,21 @@ export { toast, type Toast, type ToastOptions } from "./toast.js";
12
12
  export { tooltip, tooltips, type TooltipContent, type TooltipOptions } from "./tooltip.js";
13
13
  export { split, type PaneAction, type PaneOptions, type Split, type SplitFold, type SplitOptions, type SplitPane, } from "./split.js";
14
14
  export { COLLAPSE_PAST, drag as dragDivider, parseLayout, softClamp, solve as solveSplit, type PaneSpec, type SplitDirection, type SplitLayout, } from "./split-model.js";
15
- export { timeline, type SeekSource, type Timeline, type TimelineMarker, type TimelineOptions, type TimelineRange, } from "./timeline.js";
15
+ export { timeline, type SeekSource, type Timeline, type TimelineMarker, type TimelineListener, type TimelineOptions, type TimelineRange, } from "./timeline.js";
16
16
  export { adjacentMarker, advance, clampView, followView, frameTime, magnet, rulerTicks, scrubTime, timeLabel, toFrame, zoomView, type Tick, type Ticks, type TimeUnits, type TimeView, } from "./timeline-model.js";
17
+ export { compare, COMPARE_FFMPEG, type Compare, type CompareLoop, type CompareMode, type CompareOptions, type CompareSource, type CompareView, type MediaKind, type ZoomRegion, } from "./compare.js";
18
+ export { compareStrip, type CompareStrip, type CompareStripOptions } from "./compare-strip.js";
19
+ export { compareLayout, frameMidpoint, frameOf, letterbox, mediaKind, meanAbsDiff, requestSeek, seeked, seekTime, stripColumns, stripPeaks, syncAction, wipeAt, wipeGrab, wipeKey, wipeX, zoomAbout, type CompareLayout, type SeekState, type StripColumn, type SyncAction, } from "./compare-model.js";
17
20
  export { tree, type Tree, type TreeOptions, type TreeRowContext, type TreeToggle } from "./tree.js";
18
21
  export { applyMove, plainAccess, dropTarget, flatten, indexTree, matchLabel, navigate, select, stepMove, topmost, typeahead, validMove, virtualWindow, type DropTarget, type FlatRow, type Selection, type TreeAccess, type TreeFilter, type TreeMove, } from "./tree-model.js";
22
+ export { codeDiff, diffCounts, diffReview, diffStatus, type CodeDiff, type CodeDiffOptions, type CodeDiffUpdate, type DiffLayout, type DiffLines, type DiffReview, type DiffReviewOptions, } from "./diff.js";
23
+ export { compareText, comparePaths, countChanges, diffBar, diffFiles, diffFolders, diffStats, diffTree, fileKey, filesUnder, keepFile, matchPath, parsePatch, splitPath, stepFile, type DiffChange, type DiffFile, type DiffInput, type DiffRows, type DiffSource, type DiffTreeItem, } from "./diff-model.js";
19
24
  export { bento, type Bento, type BentoItem, type BentoOptions } from "./bento.js";
20
25
  export { fitRect, FIT_MAX, motionBlur, neighbour as bentoNeighbour, planBento, rowTarget, windowOf as bentoWindow, type BentoDirection, type BentoLayout, type BentoMode, type BentoPlan, type BentoRect, } from "./bento-model.js";
21
26
  export { activity, type Activity, type ActivityOptions, } from "./activity.js";
22
27
  export { diffActivity, flattenActivity, formatDuration, indexActivity, keepSelection as keepActivitySelection, navigateActivity, railSegments, showsSelection, stackRows, type ActivityEvent, type ActivityInput, type ActivityKey, type ActivityKind, type ActivityRow, type RailNode, } from "./activity-model.js";
23
28
  export { optionPicker, type OptionPicker, type OptionPickerOptions, type PickerOption, } from "./picker.js";
24
29
  export { claimsDigit, digitPick, keepPick, stepPick } from "./picker-model.js";
30
+ export { tasteTest, walkthrough, type TasteTest, type TasteTestOptions, type Walkthrough, type WalkthroughOptions, } from "./walkthrough.js";
31
+ export { mediaSlot, type MediaSlot, type MediaSlotOptions } from "./walkthrough-media.js";
32
+ export { collectFeedback, feedbackMarkdown, inlineMarkdown, normalizeWalkthrough, readState as readWalkthroughState, walkthroughProgress, type ContextEvent, type FeedbackItem, type FeedbackNote, type FeedItem, type MediaContext, type NoteContext, type WalkInk, type WalkNote, type WalkVoice, type WalkthroughQA, type WalkthroughUpdate, type WalkPage, type WalkStep, type WalkthroughDoc, type WalkthroughFeedback, type WalkthroughMedia, type WalkthroughMeta, type WalkthroughOption, type WalkthroughSection, type WalkthroughSource, type WalkthroughState, type WalkthroughStatus, type WalkthroughStep, } from "./walkthrough-model.js";
@@ -1,14 +1,18 @@
1
- import { type TokenPreset } from "../core/theme.js";
2
- export type UiTheme = "dark" | "light" | "auto";
3
- /** What every piece takes to match the panels beside it. */
4
- export interface UiLook {
5
- /** A token preset (`labPreset`): the same tokens a panel takes. */
6
- readonly preset?: TokenPreset;
7
- readonly theme?: UiTheme;
8
- }
1
+ import { type Appearance, type LookOptions, type ThemeMode } from "../core/appearance.js";
2
+ /** @deprecated The old appearance option: use `appearance` (`auto` is `system`). */
3
+ export type UiTheme = ThemeMode;
4
+ export type { Appearance };
5
+ /**
6
+ * What every piece takes to match the panels beside it: a theme (`themes.void`, or your own from
7
+ * `defineTheme`) and an appearance (dark | light | system | inherit, the default). The old
8
+ * `theme: "dark" | "light" | "auto"` and `preset` still work.
9
+ */
10
+ export type UiLook = LookOptions;
11
+ /** The pieces' stylesheet, as `injectUiStyles` writes it (after the token sheet). */
12
+ export declare const uiCss: () => string;
9
13
  /** Adds the pieces' stylesheet once per document. */
10
14
  export declare function injectUiStyles(doc: Document): void;
11
- /** Styles, preset and theme for a piece's root. */
15
+ /** Styles, theme, appearance and preset for a piece's root. */
12
16
  export declare function applyLook(root: HTMLElement, look: UiLook): void;
13
17
  /**
14
18
  * `--k-frame: flat` (a preset's, or a host's on any ancestor) marks the piece `data-frame`. Read
@@ -21,16 +25,17 @@ export interface RestyleLook extends UiLook {
21
25
  }
22
26
  /**
23
27
  * Changes the look of what's already on screen: every knobs/ui piece and panel inside `target`
24
- * (the whole document by default) takes `look`'s preset (none for knobs' own), theme and, for
25
- * panels, density. Panels' tokens glide to the new values; splits and trees measure again. On a
26
- * document, overlays opened afterwards take it too (`overlayLook`).
28
+ * (the whole document by default) takes `look`'s theme and preset (knobs' own where it gives
29
+ * none) and, where it gives them, its appearance and a panel's density. Tokens glide to the new
30
+ * values; splits and trees measure again. On a document, overlays opened afterwards take it too
31
+ * (`overlayLook`).
27
32
  */
28
33
  export declare function restyle(target: Document | Element, look: RestyleLook): void;
29
34
  /** The look overlays take unless given their own (`overlayLook`, `restyle(document)`). */
30
35
  export declare const lookDefaults: {
31
36
  current: UiLook;
32
37
  };
33
- export declare const reducedMotion: (win: Window | null) => boolean;
38
+ export { reducedMotion } from "../core/motion.js";
34
39
  export declare const tune: <T extends {
35
40
  omega: number;
36
41
  zeta: number;
@@ -1 +1 @@
1
- export declare const overlayCss = "\n.knobs-layers{position:fixed;inset:0;z-index:2147480005;pointer-events:none}\n.knobs-layers>.knobs-layer,.knobs-ui.knobs-toast{position:fixed;left:0;top:0;box-sizing:border-box;pointer-events:auto;outline:none;\n font:var(--ku-font-weight) var(--ku-font-size)/1.4 var(--ku-font);color:var(--ku-text);letter-spacing:normal;text-align:left;text-transform:none;\n -webkit-font-smoothing:antialiased;will-change:transform,opacity,filter}\n/* The overlays' own elements stop a host's element rules; what you put in a popover is yours. */\n:where(.knobs-layers,.knobs-toast) :is(.knobs-kbd,.knobs-mitem,.knobs-menu-sep,.knobs-palette-input,.knobs-palette-label>mark,.knobs-confirm-title,.knobs-confirm-body,.knobs-confirm-btn,.knobs-toast-action){all:revert;box-sizing:border-box;font:inherit;color:inherit;letter-spacing:inherit;\n background:transparent;border:0;margin:0;padding:0;outline:none;box-shadow:none}\n.knobs-layers .knobs-layer *{box-sizing:border-box}\n/* A floating surface: frosted, a hairline rim, a soft shadow. */\n.knobs-layers>:is(.knobs-popover-layer,.knobs-palette,.knobs-confirm,.knobs-menu-layer){background:color-mix(in srgb,var(--ku-bg) 88%,transparent);\n -webkit-backdrop-filter:blur(28px) saturate(1.7);backdrop-filter:blur(28px) saturate(1.7);border-radius:var(--ku-panel-radius);\n box-shadow:0 0 0 .5px var(--ku-line-hi),inset 0 1px 0 var(--ku-rim),0 12px 40px rgba(0,0,0,.36),0 2px 8px rgba(0,0,0,.18)}\n.knobs-layers>.knobs-popover-layer{min-width:120px;max-width:min(420px,calc(100vw - 16px));padding:6px}\n.knobs-popover-layer .knobs-popover-text{padding:4px 6px;color:var(--ku-label)}\n\n/* Menus: rows of the panel's control height, a soft wash on the active one (never an outline),\n concentric corners inside the surface. */\n.knobs-layers>.knobs-menu-layer{display:flex;flex-direction:column;min-width:184px;max-width:min(340px,calc(100vw - 16px));max-height:calc(100vh - 16px);overflow:auto;padding:5px;\n scrollbar-width:thin;overscroll-behavior:contain;user-select:none;-webkit-user-select:none}\n.knobs-menu-layer .knobs-mitem{position:relative;display:flex;align-items:center;gap:8px;flex:none;height:max(22px,calc(var(--ku-row) - 6px));padding:0 8px;\n border-radius:max(0px,calc(var(--ku-panel-radius) - 5px));color:var(--ku-text);cursor:default;white-space:nowrap}\n.knobs-menu-layer .knobs-mitem[data-active],.knobs-menu-layer .knobs-mitem[data-open]{background:var(--ku-wash-hi)}\n.knobs-menu-layer .knobs-mitem[data-destructive]{color:var(--ku-critical)}\n.knobs-menu-layer .knobs-mitem[data-destructive][data-active]{background:color-mix(in srgb,var(--ku-critical) 16%,transparent)}\n.knobs-menu-layer .knobs-mitem[aria-disabled=\"true\"]{opacity:.38}\n.knobs-menu-layer .knobs-menu-lead{flex:none;display:flex;align-items:center;justify-content:center;width:14px;margin-left:-2px;color:var(--ku-label);font-size:calc(var(--ku-font-size) - 1px)}\n.knobs-menu-layer .knobs-mitem[data-destructive] .knobs-menu-lead,.knobs-menu-layer .knobs-mitem[aria-checked=\"true\"] .knobs-menu-lead{color:inherit}\n.knobs-menu-layer .knobs-menu-lead svg{width:13px;height:13px}\n.knobs-menu-layer .knobs-menu-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}\n.knobs-menu-layer .knobs-menu-hint{flex:none;color:var(--ku-dim)}\n.knobs-menu-layer .knobs-menu-keys{display:inline-flex;gap:2px;flex:none;margin-left:12px}\n.knobs-menu-layer .knobs-mitem[data-active] .knobs-kbd{background:color-mix(in srgb,var(--ku-text) 14%,transparent);color:var(--ku-text)}\n.knobs-menu-layer .knobs-menu-caret{flex:none;display:flex;margin-left:8px;margin-right:-3px;color:var(--ku-dim)}\n.knobs-menu-layer .knobs-menu-caret svg{width:10px;height:10px}\n.knobs-menu-layer .knobs-menu-sep{flex:none;height:1px;margin:4px 8px;background:var(--ku-line)}\n\n.knobs-layers>.knobs-tooltip{display:inline-flex;align-items:center;gap:7px;max-width:300px;padding:4px 8px;pointer-events:none;white-space:nowrap;\n font-size:calc(var(--ku-font-size) - .5px);background:var(--ku-bg);color:var(--ku-text);border-radius:min(var(--ku-radius),6px);\n box-shadow:0 0 0 .5px var(--ku-line-hi),0 4px 14px rgba(0,0,0,.3)}\n.knobs-tooltip .knobs-tooltip-text{overflow:hidden;text-overflow:ellipsis}\n.knobs-layer :is(.knobs-tooltip-keys,.knobs-palette-keys){display:inline-flex;gap:2px;flex:none}\n.knobs-layer .knobs-tooltip-keys[hidden]{display:none}\n.knobs-layer .knobs-kbd{display:inline-flex;align-items:center;justify-content:center;min-width:17px;height:17px;padding:0 4px;\n border-radius:min(var(--ku-radius),4px);background:var(--ku-surface-hi);color:var(--ku-label);font:500 calc(var(--ku-font-size) - 1.5px)/1 var(--ku-font)}\n\n.knobs-layers>.knobs-palette{left:50%;top:max(48px,15vh);translate:-50% 0;transform-origin:50% 0;display:flex;flex-direction:column;\n width:min(580px,calc(100vw - 32px));max-height:min(460px,72vh);overflow:hidden}\n.knobs-palette .knobs-palette-field{display:flex;align-items:center;gap:10px;flex:none;height:46px;padding:0 16px;border-bottom:1px solid var(--ku-line);color:var(--ku-dim)}\n.knobs-palette .knobs-palette-field svg{width:15px;height:15px;flex:none}\n.knobs-palette .knobs-palette-input{flex:1;min-width:0;height:100%;font-size:calc(var(--ku-font-size) + 3px);color:var(--ku-text);caret-color:var(--ku-accent)}\n.knobs-palette .knobs-palette-input::placeholder{color:var(--ku-dim)}\n.knobs-palette .knobs-palette-list{position:relative;flex:1;min-height:0;overflow:auto;padding:6px;scrollbar-width:thin;overscroll-behavior:contain}\n.knobs-palette .knobs-palette-hl{position:absolute;left:6px;right:6px;top:0;border-radius:max(0px,calc(var(--ku-panel-radius) - 6px));background:var(--ku-wash-hi);opacity:0;pointer-events:none;\n transition:transform var(--ku-ease-ms) var(--ku-ease),height var(--ku-ease-ms) var(--ku-ease),opacity .12s}\n.knobs-palette .knobs-palette-section{padding:10px 10px 4px;color:var(--ku-dim);font-size:calc(var(--ku-font-size) - 1px);font-weight:var(--ku-label-weight);user-select:none}\n.knobs-palette .knobs-palette-section:first-of-type{padding-top:4px}\n.knobs-palette .knobs-palette-item{position:relative;display:flex;align-items:center;gap:8px;height:max(32px,var(--ku-row));padding:0 10px;\n color:var(--ku-label);cursor:default;user-select:none}\n.knobs-palette .knobs-palette-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ku-text)}\n/* Searching, the label dims and its matched letters light up. */\n.knobs-palette[data-query] .knobs-palette-label{color:var(--ku-label)}\n.knobs-palette .knobs-palette-label mark{color:var(--ku-accent);font-weight:var(--ku-title-weight)}\n.knobs-palette[data-query] .knobs-palette-item[data-active] .knobs-palette-label{color:var(--ku-text)}\n.knobs-palette .knobs-palette-hint{flex:none;color:var(--ku-dim)}\n.knobs-palette .knobs-palette-fill{flex:1}\n.knobs-palette .knobs-palette-tag{flex:none;color:var(--ku-dim);font-size:calc(var(--ku-font-size) - 1px);white-space:nowrap}\n.knobs-palette .knobs-palette-item[aria-disabled=\"true\"]{opacity:.4}\n.knobs-palette .knobs-palette-empty{padding:18px 10px;color:var(--ku-dim);text-align:center}\n\n.knobs-layers>.knobs-scrim{inset:0;width:100%;height:100%;background:rgba(0,0,0,.3)}\n.knobs-layers>.knobs-confirm{left:50%;top:42%;translate:-50% -50%;width:min(340px,calc(100vw - 32px));padding:18px 18px 14px}\n.knobs-confirm .knobs-confirm-title{margin:0 0 6px;font-size:calc(var(--ku-font-size) + 1.5px);font-weight:var(--ku-title-weight);color:var(--ku-text);line-height:1.3}\n.knobs-confirm .knobs-confirm-body{margin:0;color:var(--ku-label);line-height:1.45}\n.knobs-confirm .knobs-confirm-buttons{display:flex;justify-content:flex-end;gap:8px;margin-top:18px}\n.knobs-confirm .knobs-confirm-btn{height:28px;padding:0 14px;border-radius:min(var(--ku-radius),7px);background:var(--ku-surface-hi);color:var(--ku-text);\n font-weight:var(--ku-label-weight);cursor:pointer;transition:background .15s,filter .15s}\n.knobs-confirm .knobs-confirm-cancel:is(:hover,:focus-visible){background:color-mix(in srgb,var(--ku-text) 18%,transparent)}\n.knobs-confirm .knobs-confirm-ok{background:var(--ku-accent);color:var(--ku-bg)}\n.knobs-confirm .knobs-confirm-ok[data-destructive]{background:var(--ku-critical);color:#fff}\n.knobs-confirm .knobs-confirm-ok:is(:hover,:focus-visible){filter:brightness(1.14)}\n\n.knobs-ui.knobs-toast{left:50%;top:auto;bottom:24px;z-index:2147480005;translate:-50% 0;display:inline-flex;align-items:center;gap:10px;\n max-width:min(480px,80vw);padding:8px 16px;background:var(--ku-bg);color:var(--ku-text);border-radius:calc(var(--ku-radius) * 999);\n box-shadow:0 0 0 .5px var(--ku-line-hi),0 8px 28px rgba(0,0,0,.32)}\n.knobs-toast .knobs-toast-text{overflow-wrap:anywhere}\n.knobs-toast[data-tone=\"warn\"]::before,.knobs-toast[data-tone=\"error\"]::before{content:\"\";flex:none;width:7px;height:7px;border-radius:50%;background:var(--ku-warn)}\n.knobs-toast[data-tone=\"error\"]::before{background:var(--ku-critical)}\n.knobs-toast .knobs-toast-action{flex:none;color:var(--ku-accent);font-weight:var(--ku-title-weight);cursor:pointer}\n.knobs-toast .knobs-toast-action[hidden]{display:none}\n";
1
+ export declare const overlayCss = "\n.knobs-layers{position:fixed;inset:0;z-index:2147480005;pointer-events:none}\n.knobs-layers>.knobs-layer,.knobs-ui.knobs-toast{position:fixed;left:0;top:0;box-sizing:border-box;pointer-events:auto;outline:none;\n font:var(--_k-weight) var(--_k-font-size)/1.4 var(--_k-font);color:var(--_k-fg);letter-spacing:normal;text-align:left;text-transform:none;\n -webkit-font-smoothing:antialiased;will-change:transform,opacity,filter}\n/* The overlays' own elements stop a host's element rules; what you put in a popover is yours. */\n:where(.knobs-layers,.knobs-toast) :is(.knobs-kbd,.knobs-mitem,.knobs-menu-sep,.knobs-palette-input,.knobs-palette-label>mark,.knobs-confirm-title,.knobs-confirm-body,.knobs-confirm-btn,.knobs-toast-action){all:revert;box-sizing:border-box;font:inherit;color:inherit;letter-spacing:inherit;\n background:transparent;border:0;margin:0;padding:0;outline:none;box-shadow:none}\n.knobs-layers .knobs-layer *{box-sizing:border-box}\n/* A floating surface: frosted, a hairline rim, a soft shadow. */\n.knobs-layers>:is(.knobs-popover-layer,.knobs-palette,.knobs-confirm,.knobs-menu-layer){background:color-mix(in srgb,var(--_k-bg) 88%,transparent);\n -webkit-backdrop-filter:blur(28px) saturate(1.7);backdrop-filter:blur(28px) saturate(1.7);border-radius:var(--_k-radius-panel);\n box-shadow:0 0 0 .5px var(--_k-border-strong),inset 0 1px 0 var(--_k-rim),0 12px 40px rgba(0,0,0,calc(.36 * var(--_k-shadow-k,1))),0 2px 8px rgba(0,0,0,calc(.18 * var(--_k-shadow-k,1)))}\n.knobs-layers>.knobs-popover-layer{min-width:120px;max-width:min(420px,calc(100vw - 16px));padding:6px}\n.knobs-popover-layer .knobs-popover-text{padding:4px 6px;color:var(--_k-fg-2)}\n\n/* Menus: rows of the panel's control height, a soft wash on the active one (never an outline),\n concentric corners inside the surface. */\n.knobs-layers>.knobs-menu-layer{display:flex;flex-direction:column;min-width:184px;max-width:min(340px,calc(100vw - 16px));max-height:calc(100vh - 16px);overflow:auto;padding:5px;\n scrollbar-width:thin;overscroll-behavior:contain;user-select:none;-webkit-user-select:none}\n.knobs-menu-layer .knobs-mitem{position:relative;display:flex;align-items:center;gap:8px;flex:none;height:max(22px,calc(var(--_k-row) - 6px));padding:0 8px;\n border-radius:max(0px,calc(var(--_k-radius-panel) - 5px));color:var(--_k-fg);cursor:default;white-space:nowrap}\n.knobs-menu-layer .knobs-mitem[data-active],.knobs-menu-layer .knobs-mitem[data-open]{background:var(--_ku-wash-hi)}\n.knobs-menu-layer .knobs-mitem[data-destructive]{color:var(--_k-danger)}\n.knobs-menu-layer .knobs-mitem[data-destructive][data-active]{background:color-mix(in srgb,var(--_k-danger) 16%,transparent)}\n.knobs-menu-layer .knobs-mitem[aria-disabled=\"true\"]{opacity:.38}\n.knobs-menu-layer .knobs-menu-lead{flex:none;display:flex;align-items:center;justify-content:center;width:14px;margin-left:-2px;color:var(--_k-fg-2);font-size:calc(var(--_k-font-size) - 1px)}\n.knobs-menu-layer .knobs-mitem[data-destructive] .knobs-menu-lead,.knobs-menu-layer .knobs-mitem[aria-checked=\"true\"] .knobs-menu-lead{color:inherit}\n.knobs-menu-layer .knobs-menu-lead svg{width:13px;height:13px}\n.knobs-menu-layer .knobs-menu-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}\n.knobs-menu-layer .knobs-menu-hint{flex:none;color:var(--_k-fg-3)}\n.knobs-menu-layer .knobs-menu-keys{display:inline-flex;gap:2px;flex:none;margin-left:12px}\n.knobs-menu-layer .knobs-mitem[data-active] .knobs-kbd{background:color-mix(in srgb,var(--_k-fg) 14%,transparent);color:var(--_k-fg)}\n.knobs-menu-layer .knobs-menu-caret{flex:none;display:flex;margin-left:8px;margin-right:-3px;color:var(--_k-fg-3)}\n.knobs-menu-layer .knobs-menu-caret svg{width:10px;height:10px}\n.knobs-menu-layer .knobs-menu-sep{flex:none;height:1px;margin:4px 8px;background:var(--_k-border)}\n\n.knobs-layers>.knobs-tooltip{display:inline-flex;align-items:center;gap:7px;max-width:300px;padding:4px 8px;pointer-events:none;white-space:nowrap;\n font-size:calc(var(--_k-font-size) - .5px);background:var(--_k-bg);color:var(--_k-fg);border-radius:min(var(--_k-radius),6px);\n box-shadow:0 0 0 .5px var(--_k-border-strong),0 4px 14px rgba(0,0,0,calc(.3 * var(--_k-shadow-k,1)))}\n.knobs-tooltip .knobs-tooltip-text{overflow:hidden;text-overflow:ellipsis}\n.knobs-layer :is(.knobs-tooltip-keys,.knobs-palette-keys){display:inline-flex;gap:2px;flex:none}\n.knobs-layer .knobs-tooltip-keys[hidden]{display:none}\n.knobs-layer .knobs-kbd{display:inline-flex;align-items:center;justify-content:center;min-width:17px;height:17px;padding:0 4px;\n border-radius:min(var(--_k-radius),4px);background:var(--_k-fill-selected);color:var(--_k-fg-2);font:500 calc(var(--_k-font-size) - 1.5px)/1 var(--_k-font)}\n\n.knobs-layers>.knobs-palette{left:50%;top:max(48px,15vh);translate:-50% 0;transform-origin:50% 0;display:flex;flex-direction:column;\n width:min(580px,calc(100vw - 32px));max-height:min(460px,72vh);overflow:hidden}\n.knobs-palette .knobs-palette-field{display:flex;align-items:center;gap:10px;flex:none;height:46px;padding:0 16px;border-bottom:1px solid var(--_k-border);color:var(--_k-fg-3)}\n.knobs-palette .knobs-palette-field svg{width:15px;height:15px;flex:none}\n.knobs-palette .knobs-palette-input{flex:1;min-width:0;height:100%;font-size:calc(var(--_k-font-size) + 3px);color:var(--_k-fg);caret-color:var(--_k-accent)}\n.knobs-palette .knobs-palette-input::placeholder{color:var(--_k-fg-3)}\n.knobs-palette .knobs-palette-list{position:relative;flex:1;min-height:0;overflow:auto;padding:6px;scrollbar-width:thin;overscroll-behavior:contain}\n.knobs-palette .knobs-palette-hl{position:absolute;left:6px;right:6px;top:0;border-radius:max(0px,calc(var(--_k-radius-panel) - 6px));background:var(--_ku-wash-hi);opacity:0;pointer-events:none;\n transition:transform var(--_ku-ease-ms) var(--_ku-ease),height var(--_ku-ease-ms) var(--_ku-ease),opacity .12s}\n.knobs-palette .knobs-palette-section{padding:10px 10px 4px;color:var(--_k-fg-3);font-size:calc(var(--_k-font-size) - 1px);font-weight:var(--_k-weight-label);user-select:none}\n.knobs-palette .knobs-palette-section:first-of-type{padding-top:4px}\n.knobs-palette .knobs-palette-item{position:relative;display:flex;align-items:center;gap:8px;height:max(32px,var(--_k-row));padding:0 10px;\n color:var(--_k-fg-2);cursor:default;user-select:none}\n.knobs-palette .knobs-palette-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--_k-fg)}\n/* Searching, the label dims and its matched letters light up. */\n.knobs-palette[data-query] .knobs-palette-label{color:var(--_k-fg-2)}\n.knobs-palette .knobs-palette-label mark{color:var(--_k-accent);font-weight:var(--_k-weight-title)}\n.knobs-palette[data-query] .knobs-palette-item[data-active] .knobs-palette-label{color:var(--_k-fg)}\n.knobs-palette .knobs-palette-hint{flex:none;color:var(--_k-fg-3)}\n.knobs-palette .knobs-palette-fill{flex:1}\n.knobs-palette .knobs-palette-tag{flex:none;color:var(--_k-fg-3);font-size:calc(var(--_k-font-size) - 1px);white-space:nowrap}\n.knobs-palette .knobs-palette-item[aria-disabled=\"true\"]{opacity:.4}\n.knobs-palette .knobs-palette-empty{padding:18px 10px;color:var(--_k-fg-3);text-align:center}\n\n.knobs-layers>.knobs-scrim{inset:0;width:100%;height:100%;background:var(--_k-scrim)}\n.knobs-layers>.knobs-confirm{left:50%;top:42%;translate:-50% -50%;width:min(340px,calc(100vw - 32px));padding:18px 18px 14px}\n.knobs-confirm .knobs-confirm-title{margin:0 0 6px;font-size:calc(var(--_k-font-size) + 1.5px);font-weight:var(--_k-weight-title);color:var(--_k-fg);line-height:1.3}\n.knobs-confirm .knobs-confirm-body{margin:0;color:var(--_k-fg-2);line-height:1.45}\n.knobs-confirm .knobs-confirm-buttons{display:flex;justify-content:flex-end;gap:8px;margin-top:18px}\n.knobs-confirm .knobs-confirm-btn{height:28px;padding:0 14px;border-radius:min(var(--_k-radius),7px);background:var(--_k-fill-selected);color:var(--_k-fg);\n font-weight:var(--_k-weight-label);cursor:pointer;transition:background .15s,filter .15s}\n.knobs-confirm .knobs-confirm-cancel:is(:hover,:focus-visible){background:color-mix(in srgb,var(--_k-fg) 18%,transparent)}\n.knobs-confirm .knobs-confirm-ok{background:var(--_k-accent);color:var(--_k-bg)}\n.knobs-confirm .knobs-confirm-ok[data-destructive]{background:var(--_k-danger);color:#fff}\n.knobs-confirm .knobs-confirm-ok:is(:hover,:focus-visible){filter:brightness(1.14)}\n\n.knobs-ui.knobs-toast{left:50%;top:auto;bottom:24px;z-index:2147480005;translate:-50% 0;display:inline-flex;align-items:center;gap:10px;\n max-width:min(480px,80vw);padding:8px 16px;background:var(--_k-bg);color:var(--_k-fg);border-radius:calc(var(--_k-radius) * 999);\n box-shadow:0 0 0 .5px var(--_k-border-strong),0 8px 28px rgba(0,0,0,calc(.32 * var(--_k-shadow-k,1)))}\n.knobs-toast .knobs-toast-text{overflow-wrap:anywhere}\n.knobs-toast[data-tone=\"warn\"]::before,.knobs-toast[data-tone=\"error\"]::before{content:\"\";flex:none;width:7px;height:7px;border-radius:50%;background:var(--_k-warn)}\n.knobs-toast[data-tone=\"error\"]::before{background:var(--_k-danger)}\n.knobs-toast .knobs-toast-action{flex:none;color:var(--_k-accent);font-weight:var(--_k-weight-title);cursor:pointer}\n.knobs-toast .knobs-toast-action[hidden]{display:none}\n";
@@ -1,10 +1,15 @@
1
1
  import { type CSSProperties, type ReactNode } from "react";
2
2
  import { type Activity as ActivityHandle, type ActivityOptions } from "./activity.js";
3
3
  import { type Bento as BentoHandle, type BentoOptions } from "./bento.js";
4
+ import { type CodeDiff as CodeDiffHandle, type CodeDiffOptions, type DiffReview as DiffReviewHandle, type DiffReviewOptions } from "./diff.js";
4
5
  import { type OptionPicker as OptionPickerHandle, type OptionPickerOptions, type PickerOption } from "./picker.js";
5
6
  import { type Split as SplitHandle, type SplitOptions } from "./split.js";
6
7
  import { type Timeline as TimelineHandle, type TimelineOptions } from "./timeline.js";
8
+ import { type Compare as CompareHandle, type CompareOptions } from "./compare.js";
9
+ import { type CompareStrip as CompareStripHandle, type CompareStripOptions } from "./compare-strip.js";
7
10
  import { type Tree as TreeHandle, type TreeOptions } from "./tree.js";
11
+ import { type TasteTest as TasteTestHandle, type TasteTestOptions, type Walkthrough as WalkthroughHandle, type WalkthroughOptions } from "./walkthrough.js";
12
+ import { type MediaSlot as MediaSlotHandle, type MediaSlotOptions } from "./walkthrough-media.js";
8
13
  export type SplitProps = SplitOptions & {
9
14
  /** One child per pane, in the panes' order; each renders into its pane's body. */
10
15
  children?: ReactNode;
@@ -24,6 +29,24 @@ export type TreeProps<T> = TreeOptions<T> & {
24
29
  * `filter` follow their props; callbacks always call the latest props.
25
30
  */
26
31
  export declare function Tree<T>(props: TreeProps<T>): import("react").JSX.Element;
32
+ export type CodeDiffProps = CodeDiffOptions & {
33
+ className?: string;
34
+ style?: CSSProperties;
35
+ onReady?: (diff: CodeDiffHandle) => void;
36
+ };
37
+ /**
38
+ * `codeDiff()` as a component. `patch`, `files`, `file`, `layout`, `wrap`, `viewed`, `height` and
39
+ * `maxHeight` follow their
40
+ * props; callbacks always call the latest props.
41
+ */
42
+ export declare function CodeDiff(props: CodeDiffProps): import("react").JSX.Element;
43
+ export type DiffReviewProps = DiffReviewOptions & {
44
+ className?: string;
45
+ style?: CSSProperties;
46
+ onReady?: (review: DiffReviewHandle) => void;
47
+ };
48
+ /** `diffReview()` as a component: the file tree beside the diff; follows as `CodeDiff` does. */
49
+ export declare function DiffReview(props: DiffReviewProps): import("react").JSX.Element;
27
50
  export type TimelineProps = TimelineOptions & {
28
51
  className?: string;
29
52
  style?: CSSProperties;
@@ -35,6 +58,24 @@ export type TimelineProps = TimelineOptions & {
35
58
  * always call the latest props.
36
59
  */
37
60
  export declare function Timeline(props: TimelineProps): import("react").JSX.Element;
61
+ export type CompareProps = CompareOptions & {
62
+ className?: string;
63
+ style?: CSSProperties;
64
+ onReady?: (compare: CompareHandle) => void;
65
+ };
66
+ /**
67
+ * `compare()` as a component. `before`, `after` (by `src`), `mode`, `wipe`, `loupe`, `threshold`
68
+ * and `fps` follow their props; a new `timeline` handle, `strip` or `renderer` makes it afresh;
69
+ * callbacks always call the latest props.
70
+ */
71
+ export declare function Compare(props: CompareProps): import("react").JSX.Element;
72
+ export type CompareStripProps = CompareStripOptions & {
73
+ className?: string;
74
+ style?: CSSProperties;
75
+ onReady?: (strip: CompareStripHandle) => void;
76
+ };
77
+ /** `compareStrip()` as a component, on a timeline handle; new sources (by `src`) follow. */
78
+ export declare function CompareStrip(props: CompareStripProps): import("react").JSX.Element;
38
79
  export type BentoProps = BentoOptions & {
39
80
  className?: string;
40
81
  style?: CSSProperties;
@@ -70,3 +111,28 @@ export type OptionPickerProps = Omit<OptionPickerOptions, "body"> & {
70
111
  export declare function OptionPicker(props: OptionPickerProps): import("react").JSX.Element;
71
112
  export type { ActivityEvent } from "./activity-model.js";
72
113
  export type { PickerOption } from "./picker.js";
114
+ export type WalkthroughProps = WalkthroughOptions & {
115
+ className?: string;
116
+ style?: CSSProperties;
117
+ onReady?: (walkthrough: WalkthroughHandle) => void;
118
+ };
119
+ /**
120
+ * `walkthrough()` as a component: a new `doc` is a new draft (answers carry over by step key);
121
+ * `onFeedback` always calls the latest prop.
122
+ */
123
+ export declare function Walkthrough(props: WalkthroughProps): import("react").JSX.Element;
124
+ export type TasteTestProps = TasteTestOptions & {
125
+ className?: string;
126
+ style?: CSSProperties;
127
+ onReady?: (test: TasteTestHandle) => void;
128
+ };
129
+ /** `tasteTest()` as a component: `picked` follows its prop; new `options` make it afresh. */
130
+ export declare function TasteTest(props: TasteTestProps): import("react").JSX.Element;
131
+ export type MediaSlotProps = MediaSlotOptions & {
132
+ className?: string;
133
+ style?: CSSProperties;
134
+ onReady?: (slot: MediaSlotHandle) => void;
135
+ };
136
+ /** `mediaSlot()` as a component: one walkthrough media slot; new `media` mounts afresh. */
137
+ export declare function MediaSlot(props: MediaSlotProps): import("react").JSX.Element;
138
+ export type { WalkthroughDoc, WalkthroughFeedback } from "./walkthrough-model.js";
@@ -1 +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";
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(--_k-fg);letter-spacing:normal}\n.knobs-rd .knobs-rd-v{font:var(--_k-weight) var(--_k-value-size)/1.25 var(--_k-value-font);font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";white-space:nowrap}\n.knobs-rd .knobs-rd-mono{font:400 var(--_k-text-meta)/1.25 var(--_k-mono);font-variant-numeric:tabular-nums;white-space:pre}\n.knobs-rd .knobs-rd-meta{font:var(--_k-weight) var(--_k-text-meta)/1.25 var(--_k-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(--_k-fg);text-align:right;transition:color .15s}\n.knobs-rd .knobs-rd-unit{margin-left:var(--k-unit-gap,.28em);color:var(--_k-fg-3)}\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(--_k-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(--_k-fg-2)}\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(--_k-fg-2);font-weight:var(--_k-weight-label)}\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(--_k-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(--_k-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(--_k-fg-2)}\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(--_k-fg-3);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(--_k-row) * .72);line-height:calc(var(--_k-row) * .72);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.knobs-rd .knobs-rd-thead>*{height:var(--_k-row);line-height:var(--_k-row)}\n.knobs-rd .knobs-rd-th{color:var(--_k-fg-3);text-align:right}\n.knobs-rd .knobs-rd-td{color:var(--_k-fg)}\n.knobs-rd .knobs-rd-td:first-child{color:var(--_k-fg-2)}\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(--_k-control-h) - 2px);padding:0 7px;border-radius:min(var(--_k-radius),5px);color:var(--_k-fg-2);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(--_k-fg)}\n.knobs-ui .knobs-rd-chip[aria-checked=\"true\"]{background:var(--_ku-wash-hi);color:var(--_k-fg)}\n.knobs-ui .knobs-rd-find{width:84px;height:calc(var(--_k-control-h) - 2px);padding:0 7px;border-radius:min(var(--_k-radius),5px);\n background:color-mix(in srgb,var(--_k-fill-selected) 60%,transparent);color:var(--_k-fg);caret-color:var(--_k-accent)}\n.knobs-rd .knobs-rd-find::placeholder{color:var(--_k-fg-3)}\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(--_k-fill-selected) 45%,transparent);border-radius:min(var(--_k-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(--_k-fg-3)}\n.knobs-rd .knobs-rd-text{min-width:0;overflow:hidden;text-overflow:ellipsis;color:var(--_k-fg)}\n.knobs-rd .knobs-rd-line[data-level=\"warn\"]>.knobs-rd-text{color:var(--_k-warn)}\n.knobs-rd .knobs-rd-line[data-level=\"error\"]>.knobs-rd-text{color:var(--_k-danger)}\n.knobs-rd .knobs-rd-line[data-level=\"debug\"]>.knobs-rd-text{color:var(--_k-fg-3)}\n.knobs-ui .knobs-rd-match{color:var(--_k-accent)}\n.knobs-ui .knobs-rd-jump{position:absolute;left:50%;bottom:6px;height:20px;padding:0 10px;border-radius:calc(var(--_k-radius) * 999);\n background:var(--_k-accent);color:var(--_k-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";
@@ -38,10 +38,14 @@ export interface Palette {
38
38
  readonly surfaceHi: Rgba;
39
39
  readonly warn: Rgba;
40
40
  readonly critical: Rgba;
41
+ /** Series colours after the first (the accent): the theme's `--k-series-*`. */
42
+ readonly series: readonly Rgba[];
41
43
  /** Corner radius (px); 0 squares the bars. */
42
44
  readonly radius: number;
43
45
  }
44
- /** Series colours after the first (the accent). */
46
+ /** The theme's chart series, read for the series after the first (the accent). */
47
+ export declare const SERIES_TOKENS: readonly ["--_k-series-1", "--_k-series-2", "--_k-series-3", "--_k-series-4", "--_k-series-5", "--_k-series-6"];
48
+ /** Series colours where no token sheet has loaded. */
45
49
  export declare const SERIES: string[];
46
50
  /** A number as text: `decimals` places, else fewer as it grows (2 under 10, 1 under 100, 0 past). */
47
51
  export declare function formatNumber(v: number, decimals?: number): string;
@@ -4,3 +4,14 @@ export declare function parseCssColor(raw: string, fallback?: Rgba): Rgba;
4
4
  export declare const withAlpha: (c: Rgba, k: number) => Rgba;
5
5
  export declare const mixRgba: (a: Rgba, b: Rgba, t: number) => Rgba;
6
6
  export declare const rgbaCss: (c: Rgba) => string;
7
+ /**
8
+ * A token's computed value with each `light-dark(a, b)` in it (as theme colours arrive in custom
9
+ * properties) replaced by the side `light` picks, so a canvas can parse it.
10
+ */
11
+ export declare function pickScheme(raw: string, light: boolean): string;
12
+ /** Whether a knobs root (or anything in one) shows its light appearance. */
13
+ export declare const isLightStyle: (style: CSSStyleDeclaration) => boolean;
14
+ /** A colour token (`--kv-fg`, `--_k-warn`) as `style`'s element shows it. */
15
+ export declare function tokenColor(style: CSSStyleDeclaration, name: string, fallback: Rgba): Rgba;
16
+ /** A colour token as CSS a canvas takes, or `fallback`. */
17
+ export declare function tokenCss(style: CSSStyleDeclaration, name: string, fallback: string): string;
@@ -2,8 +2,8 @@
2
2
  * A thin scrollbar drawn over a scroller's content, in place of the native one beside it. A native
3
3
  * scrollbar that's always shown (as a system may be set) takes its width from the rows the moment
4
4
  * the content overflows, so every row jumps sideways, and gives it back when it stops; this one
5
- * takes no room. It shows while the content scrolls and while the pointer is over the scroller,
6
- * and drags. `content` is the element whose height makes the scroll: it's watched for changes.
5
+ * takes no room. It shows while the pointer is over the scroller, while you scroll it and while
6
+ * you drag it; a scroll from code (a reveal, a restored position) doesn't flash it. `content` is the element whose height makes the scroll: it's watched for changes.
7
7
  * The scroller hides its own (`scrollbar-width: none`) and is positioned.
8
8
  */
9
9
  export declare function overlayScrollbar(scroller: HTMLElement, content: Element): {
@@ -1,10 +1,15 @@
1
1
  import { type JSX } from "solid-js";
2
2
  import { type Activity as ActivityHandle, type ActivityOptions } from "./activity.js";
3
3
  import { type Bento as BentoHandle, type BentoOptions } from "./bento.js";
4
+ import { type CodeDiff as CodeDiffHandle, type CodeDiffOptions, type DiffReview as DiffReviewHandle, type DiffReviewOptions } from "./diff.js";
4
5
  import { type OptionPicker as OptionPickerHandle, type OptionPickerOptions, type PickerOption } from "./picker.js";
5
6
  import { type Split as SplitHandle, type SplitOptions } from "./split.js";
6
7
  import { type Timeline as TimelineHandle, type TimelineOptions } from "./timeline.js";
8
+ import { type Compare as CompareHandle, type CompareOptions } from "./compare.js";
9
+ import { type CompareStrip as CompareStripHandle, type CompareStripOptions } from "./compare-strip.js";
7
10
  import { type Tree as TreeHandle, type TreeOptions } from "./tree.js";
11
+ import { type TasteTest as TasteTestHandle, type TasteTestOptions, type Walkthrough as WalkthroughHandle, type WalkthroughOptions } from "./walkthrough.js";
12
+ import { type MediaSlot as MediaSlotHandle, type MediaSlotOptions } from "./walkthrough-media.js";
8
13
  export type SplitProps = SplitOptions & {
9
14
  /** One element per pane, in the panes' order; each goes into its pane's body. */
10
15
  children?: JSX.Element;
@@ -23,6 +28,20 @@ export type TreeProps<T> = TreeOptions<T> & {
23
28
  * `filter` follow their props; callbacks always call the latest props.
24
29
  */
25
30
  export declare function Tree<T>(props: TreeProps<T>): JSX.Element;
31
+ export type CodeDiffProps = CodeDiffOptions & {
32
+ onReady?: (diff: CodeDiffHandle) => void;
33
+ };
34
+ /**
35
+ * `codeDiff()` as a component. `patch`, `files`, `file`, `layout`, `wrap`, `viewed`, `height` and
36
+ * `maxHeight` follow their
37
+ * props; callbacks always call the latest props.
38
+ */
39
+ export declare function CodeDiff(props: CodeDiffProps): JSX.Element;
40
+ export type DiffReviewProps = DiffReviewOptions & {
41
+ onReady?: (review: DiffReviewHandle) => void;
42
+ };
43
+ /** `diffReview()` as a component: the file tree beside the diff; follows as `CodeDiff` does. */
44
+ export declare function DiffReview(props: DiffReviewProps): JSX.Element;
26
45
  export type TimelineProps = TimelineOptions & {
27
46
  onReady?: (timeline: TimelineHandle) => void;
28
47
  };
@@ -32,6 +51,20 @@ export type TimelineProps = TimelineOptions & {
32
51
  * always call the latest props.
33
52
  */
34
53
  export declare function Timeline(props: TimelineProps): JSX.Element;
54
+ export type CompareProps = CompareOptions & {
55
+ onReady?: (compare: CompareHandle) => void;
56
+ };
57
+ /**
58
+ * `compare()` as a component, made once (with the `timeline`, `strip` and `renderer` it starts
59
+ * with). `before`, `after` (by `src`), `mode`, `wipe`, `loupe`, `threshold` and `fps` follow
60
+ * their props; callbacks always call the latest props.
61
+ */
62
+ export declare function Compare(props: CompareProps): JSX.Element;
63
+ export type CompareStripProps = CompareStripOptions & {
64
+ onReady?: (strip: CompareStripHandle) => void;
65
+ };
66
+ /** `compareStrip()` as a component, on a timeline handle; new sources (by `src`) follow. */
67
+ export declare function CompareStrip(props: CompareStripProps): JSX.Element;
35
68
  export type BentoProps = BentoOptions & {
36
69
  onReady?: (bento: BentoHandle) => void;
37
70
  };
@@ -61,3 +94,22 @@ export type OptionPickerProps = Omit<OptionPickerOptions, "body"> & {
61
94
  export declare function OptionPicker(props: OptionPickerProps): JSX.Element;
62
95
  export type { ActivityEvent } from "./activity-model.js";
63
96
  export type { PickerOption } from "./picker.js";
97
+ export type WalkthroughProps = WalkthroughOptions & {
98
+ onReady?: (walkthrough: WalkthroughHandle) => void;
99
+ };
100
+ /**
101
+ * `walkthrough()` as a component: a new `doc` is a new draft (answers carry over by step key);
102
+ * `onFeedback` always calls the latest prop.
103
+ */
104
+ export declare function Walkthrough(props: WalkthroughProps): JSX.Element;
105
+ export type TasteTestProps = TasteTestOptions & {
106
+ onReady?: (test: TasteTestHandle) => void;
107
+ };
108
+ /** `tasteTest()` as a component: `picked` follows its prop. */
109
+ export declare function TasteTest(props: TasteTestProps): JSX.Element;
110
+ export type MediaSlotProps = MediaSlotOptions & {
111
+ onReady?: (slot: MediaSlotHandle) => void;
112
+ };
113
+ /** `mediaSlot()` as a component: one walkthrough media slot. */
114
+ export declare function MediaSlot(props: MediaSlotProps): JSX.Element;
115
+ export type { WalkthroughDoc, WalkthroughFeedback } from "./walkthrough-model.js";
@@ -37,6 +37,14 @@ export interface TimelineOptions extends UiLook {
37
37
  readonly speed?: number;
38
38
  /** Playback starts over at the end when there's no loop range (`false`: it stops). */
39
39
  readonly repeat?: boolean;
40
+ /**
41
+ * What ←/→ move: frames (one, ten with ⇧) by default, or seconds (`{ step: 0.5, shift: 2 }`),
42
+ * the playhead gliding there; `,` and `.` step a frame either way.
43
+ */
44
+ readonly arrows?: "frames" | {
45
+ readonly step: number;
46
+ readonly shift: number;
47
+ };
40
48
  readonly label?: string;
41
49
  /** The time moved: scrubbed, stepped, jumped, played (every frame) or set from code. */
42
50
  onSeek?(time: number, source: SeekSource): void;
@@ -47,29 +55,54 @@ export interface TimelineOptions extends UiLook {
47
55
  onRanges?(ranges: readonly TimelineRange[]): void;
48
56
  /** Playback passed a marker. */
49
57
  onCross?(marker: TimelineMarker): void;
58
+ /** The visible window moved: a zoom, a pan, a fit or a follow. */
59
+ onView?(view: TimeView): void;
50
60
  /**
51
61
  * Play, each marker and the playhead take codes in the page's hint mode (`s`), on by default:
52
62
  * a marker's code jumps to it, the playhead's focuses the timeline.
53
63
  */
54
64
  readonly hints?: boolean;
55
65
  }
66
+ /** What `listen` reports: the same as the options' callbacks, for pieces bound to a timeline. */
67
+ export interface TimelineListener {
68
+ onSeek?(time: number, source: SeekSource): void;
69
+ onPlay?(playing: boolean): void;
70
+ onView?(view: TimeView): void;
71
+ /** A scrub of the playhead started (`true`) or let go (`false`). */
72
+ onScrub?(active: boolean): void;
73
+ /** Ranges were set, moved or cleared (the whole list). */
74
+ onRanges?(ranges: readonly TimelineRange[]): void;
75
+ }
56
76
  export interface Timeline {
57
77
  readonly element: HTMLElement;
58
78
  readonly time: number;
59
79
  readonly playing: boolean;
80
+ readonly duration: number;
81
+ readonly fps: number;
60
82
  readonly markers: readonly TimelineMarker[];
61
83
  readonly ranges: readonly TimelineRange[];
62
84
  readonly view: TimeView;
63
- seek(time: number): void;
85
+ /** `glide`: the playhead springs there (the time, and what it drives, land at once). */
86
+ seek(time: number, options?: {
87
+ readonly glide?: boolean;
88
+ }): void;
64
89
  play(): void;
65
90
  pause(): void;
66
91
  toggle(): void;
67
92
  /** 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;
93
+ update(next: Partial<Omit<TimelineOptions, "onSeek" | "onPlay" | "onMarkers" | "onRanges" | "onCross" | "onView">>): void;
69
94
  /** Shows the whole duration (animated). */
70
95
  fit(): void;
71
96
  /** Zooms by `factor` (above 1 zooms in) around a time (the playhead's by default). */
72
97
  zoom(factor: number, at?: number): void;
98
+ /** Hears seeks, play and pause, and view moves alongside the options' callbacks; returns a stop. */
99
+ listen(listener: TimelineListener): () => void;
100
+ /**
101
+ * While playing, time follows `clock` (seconds, `Infinity` once it has run out: a video's
102
+ * `currentTime`, say) instead of the timeline's own frame clock, so the two never drift; loops
103
+ * and the end still apply. `null` hands time back.
104
+ */
105
+ drive(clock: (() => number) | null): void;
73
106
  focus(): void;
74
107
  dispose(): void;
75
108
  }