@kitlangton/knobs 0.4.0-next.4 → 0.4.0-next.5

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.
@@ -1 +1 @@
1
- export declare const compareCss = "\n:where(.knobs-compare,.knobs-compare *,.knobs-cmp-strip,.knobs-cmp-strip *){box-sizing:border-box}\n.knobs-ui.knobs-compare{--_ku-cmp-diff:var(--k-compare-diff,var(--_k-ok));--_ku-cmp-line:var(--_k-compare-divider);\n --_ku-cmp-stage:var(--_k-compare-stage);\n display:flex;flex-direction:column;gap:6px;width:100%;height:100%;min-height:160px;outline:none;user-select:none;-webkit-user-select:none;\n font:var(--_k-weight) var(--_k-font-size)/1.35 var(--_k-font);color:var(--_k-fg);letter-spacing:normal;text-align:left}\n:where(.knobs-ui) :is(.knobs-cmp-mode,.knobs-cmp-toggle){all:revert;box-sizing:border-box;font:inherit;color:inherit;letter-spacing:inherit;background:transparent;border:0;margin:0;padding:0;outline:none;box-shadow:none}\n.knobs-cmp-bar{flex:none;display:flex;align-items:center;gap:var(--_k-gap);height:var(--_k-control-h)}\n/* The modes: one fill, the selection pill on the one picked; a mode isn't brighter for being\n picked: the pill's bright copy is. */\n.knobs-cmp-modes{position:relative;display:inline-flex;height:var(--_k-control-h);border-radius:var(--_k-radius);background:var(--_k-fill)}\n.knobs-cmp-modes[hidden]{display:none}\n.knobs-ui .knobs-cmp-mode{display:inline-flex;align-items:center;height:100%;padding:0 8px;border-radius:var(--_k-radius);color:var(--_k-fg-2);cursor:pointer;white-space:nowrap;\n transition:color calc(var(--_k-hover-out) * 1ms)}\n.knobs-ui .knobs-cmp-mode:is(:hover,:focus-visible){color:var(--_k-fg)}\n/* Toggles: icon buttons, the accent's wash when on. */\n.knobs-ui .knobs-cmp-toggle{display:inline-flex;align-items:center;justify-content:center;width:var(--_k-control-h);height:var(--_k-control-h);border-radius:var(--_k-radius);color:var(--_k-fg-3);cursor:pointer;\n transition:color calc(var(--_k-hover-out) * 1ms),background calc(var(--_k-hover-out) * 1ms)}\n.knobs-ui .knobs-cmp-toggle:is(:hover,:focus-visible){color:var(--_k-fg);background:var(--_ku-wash)}\n.knobs-ui .knobs-cmp-toggle[aria-pressed=\"true\"]{color:var(--_k-accent-fg);background:var(--_k-accent-soft)}\n.knobs-cmp-toggle svg{display:block}\n.knobs-cmp-fill{flex:1}\n.knobs-cmp-toggle[hidden]{display:none}\n/* One surface: on a card (a walkthrough's step, a split's pane, anything .knobs-surface) or marked\n data-nested, the stage drops its fill and the sides sit on that card. */\n:where(.knobs-surface,.knobs-pane:not([data-nested])>.knobs-pane-clip) .knobs-ui.knobs-compare,.knobs-ui.knobs-compare[data-nested]{--_ku-cmp-stage:var(--k-compare-stage,transparent)}\n/* In a page's flow (a container with no height), it takes the height its sides need. */\n.knobs-ui.knobs-compare[data-auto]{height:auto}\n.knobs-compare[data-auto] .knobs-cmp-stage{flex:none;min-height:0}\n.knobs-cmp-stage{position:relative;flex:1;min-height:96px;overflow:hidden;border-radius:var(--_k-radius-panel);background:var(--_ku-cmp-stage);touch-action:pan-y;outline:none}\n.knobs-compare[data-frame=\"flat\"] .knobs-cmp-stage{border-radius:0}\n.knobs-compare[data-mode=\"wipe\"] .knobs-cmp-stage{cursor:ew-resize}\n/* Unzoomed, a finger still scrolls the page up and down; zoomed, the stage takes every gesture. */\n.knobs-compare[data-zoomed] .knobs-cmp-stage{cursor:grab;touch-action:none}\n.knobs-compare[data-panning],.knobs-compare[data-panning] *{cursor:grabbing!important}\n/* Wiping, the grip stands in for the cursor. */\n.knobs-compare[data-dragging],.knobs-compare[data-dragging] *{cursor:none!important}\n.knobs-cmp-sizer{position:absolute;left:0;right:0;top:0;height:0;visibility:hidden;pointer-events:none}\n.knobs-cmp-canvas{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none}\n.knobs-cmp-media{position:absolute;left:0;top:0;display:block;max-width:none;margin:0;padding:0;border:0;object-fit:fill;transform-origin:0 0;pointer-events:none;will-change:transform}\n.knobs-compare[data-renderer=\"gpu\"] .knobs-cmp-media{opacity:0}\n/* Glass: what's behind shows through, blurred, on the knobs tokens. */\n.knobs-cmp-label,.knobs-cmp-zoom{background:color-mix(in srgb,var(--_k-bg) 52%,transparent);-webkit-backdrop-filter:blur(14px) saturate(1.6);backdrop-filter:blur(14px) saturate(1.6);\n box-shadow:inset 0 0 0 .5px var(--_k-border),inset 0 1px 0 var(--_k-rim),0 2px 10px rgba(0,0,0,calc(.18 * var(--_k-shadow-k,1)))}\n/* \u2717 Before \u00B7 \u2713 After: Before quieter (fg-3), After in full text. Neutral: no colour of its own. */\n.knobs-cmp-label{position:absolute;left:0;top:0;display:flex;align-items:center;gap:4px;padding:1px 7px 1px 5px;border-radius:999px;font-size:var(--_k-text-meta);line-height:16px;font-weight:var(--_k-weight-label);\n white-space:nowrap;pointer-events:none;will-change:transform}\n.knobs-cmp-label[data-side=\"before\"]{color:var(--_k-fg-3)}\n.knobs-cmp-label[data-side=\"after\"]{color:var(--_k-fg)}\n.knobs-cmp-mark svg{display:block;width:12px;height:12px}\n/* The wipe: a hairline down the line with a raised grip at the pointer's height; a wider strip\n takes the hand and the keys (a slider). It fades with the mode; wiping, line and grip brighten. */\n.knobs-cmp-handle{cursor:ew-resize;position:absolute;left:-12px;top:0;width:24px;height:0;opacity:0;pointer-events:none;outline:none;will-change:transform;transition:opacity .18s cubic-bezier(.4,0,1,1)}\n.knobs-cmp-handle[data-on]{opacity:1;pointer-events:auto;transition:opacity .22s cubic-bezier(.2,.8,.2,1)}\n.knobs-cmp-handle::before{content:\"\";position:absolute;top:0;bottom:0;left:11.5px;width:1px;background:color-mix(in srgb,var(--_ku-cmp-line) 72%,transparent);box-shadow:0 0 0 .5px rgba(0,0,0,.28),0 0 6px rgba(0,0,0,.25)}\n.knobs-compare[data-dragging] .knobs-cmp-handle::before{background:var(--_ku-cmp-line)}\n.knobs-cmp-grip{position:absolute;top:0;left:4px;width:16px;height:28px;border-radius:min(8px,calc(var(--_k-radius) * 999));background:var(--_k-bg-overlay);\n box-shadow:var(--_k-shadow-overlay),inset 0 0 0 1px var(--_k-border),inset 0 1px 0 var(--_k-rim);will-change:transform;transition:box-shadow calc(var(--_k-hover-out) * 1ms)}\n.knobs-cmp-grip::before,.knobs-cmp-grip::after{content:\"\";position:absolute;top:10px;width:1px;height:8px;background:var(--_k-fg-3);transition:background calc(var(--_k-hover-out) * 1ms)}\n.knobs-cmp-grip::before{left:6px}\n.knobs-cmp-grip::after{left:9px}\n.knobs-compare[data-dragging] .knobs-cmp-grip{box-shadow:var(--_k-shadow-overlay),inset 0 0 0 1px var(--_k-border-strong),inset 0 1px 0 var(--_k-rim)}\n.knobs-compare[data-dragging] .knobs-cmp-grip::before,.knobs-compare[data-dragging] .knobs-cmp-grip::after{background:var(--_k-fg)}\n.knobs-cmp-handle:focus-visible .knobs-cmp-grip,.knobs-compare[data-keyboard] .knobs-cmp-handle:focus .knobs-cmp-grip{box-shadow:var(--_k-shadow-overlay),0 0 0 2px var(--_k-accent)}\n/* The split's hairline, down the middle side by side. */\n.knobs-cmp-split{position:absolute;left:0;top:0;width:1px;height:0;margin-left:-.5px;background:color-mix(in srgb,var(--_k-bg) 72%,transparent);pointer-events:none;will-change:transform,opacity}\n/* Zoomed: how far, and a way back, in the stage's corner; it fades in and out, nothing shifts. */\n.knobs-cmp-zoom{position:absolute;right:8px;bottom:8px;display:flex;align-items:center;gap:4px;height:26px;padding:0 3px 0 10px;border-radius:999px;\n color:var(--_k-fg);font-size:var(--_k-text-meta);font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";white-space:nowrap;\n opacity:0;scale:.96;pointer-events:none;transition:opacity .16s cubic-bezier(.4,0,1,1),scale .16s cubic-bezier(.4,0,1,1)}\n.knobs-cmp-zoom[data-on]{opacity:1;scale:1;pointer-events:auto;transition:opacity .2s cubic-bezier(.2,.8,.2,1),scale .2s cubic-bezier(.2,.8,.2,1)}\n:where(.knobs-ui) .knobs-cmp-zoom-reset{all:revert;box-sizing:border-box;font:inherit;letter-spacing:inherit;margin:0;border:0;outline:none;box-shadow:none;\n display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border-radius:999px;background:transparent;color:var(--_k-fg-2);cursor:pointer;transition:color .15s,background .15s}\n:where(.knobs-ui) .knobs-cmp-zoom-reset:is(:hover,:focus-visible){color:var(--_k-fg);background:color-mix(in srgb,var(--_k-fg) 12%,transparent)}\n.knobs-cmp-note{position:absolute;left:50%;top:50%;translate:-50% -50%;color:var(--_k-fg-3);font-size:var(--_k-text-meta);pointer-events:none}\n.knobs-cmp-note:empty{display:none}\n.knobs-cmp-strip-host{flex:none;height:60px;min-height:0}\n.knobs-cmp-timeline-host{flex:none;min-height:0}\n/* The frame strip: Before over After per column, a difference bar under them, a dot where it peaks. */\n.knobs-ui.knobs-cmp-strip{--_ku-cmp-diff:var(--k-compare-diff,var(--_k-ok));position:relative;width:100%;height:100%;min-height:40px;overflow:hidden;cursor:col-resize;touch-action:none;\n font:var(--_k-weight) var(--_k-font-size)/1.35 var(--_k-font);color:var(--_k-fg)}\n.knobs-cmp-strip>canvas{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none}\n@media(prefers-reduced-motion:reduce){:is(.knobs-compare,.knobs-cmp-strip) :where(*,::before,::after){transition-duration:.12s!important}}\n";
1
+ export declare const compareCss = "\n:where(.knobs-compare,.knobs-compare *,.knobs-cmp-strip,.knobs-cmp-strip *){box-sizing:border-box}\n.knobs-ui.knobs-compare{--_ku-cmp-diff:var(--k-compare-diff,var(--_k-ok));--_ku-cmp-line:var(--_k-compare-divider);\n --_ku-cmp-stage:var(--_k-compare-stage);\n display:flex;flex-direction:column;gap:6px;width:100%;height:100%;min-height:160px;outline:none;user-select:none;-webkit-user-select:none;\n font:var(--_k-weight) var(--_k-font-size)/1.35 var(--_k-font);color:var(--_k-fg);letter-spacing:normal;text-align:left}\n:where(.knobs-ui) :is(.knobs-cmp-mode,.knobs-cmp-toggle){all:revert;box-sizing:border-box;font:inherit;color:inherit;letter-spacing:inherit;background:transparent;border:0;margin:0;padding:0;outline:none;box-shadow:none}\n.knobs-cmp-bar{flex:none;display:flex;align-items:center;gap:var(--_k-gap);height:var(--_k-control-h)}\n/* The modes: one fill, the selection pill on the one picked; a mode isn't brighter for being\n picked: the pill's bright copy is. */\n.knobs-cmp-modes{position:relative;display:inline-flex;height:var(--_k-control-h);border-radius:var(--_k-radius);background:var(--_k-fill)}\n.knobs-cmp-modes[hidden]{display:none}\n.knobs-ui .knobs-cmp-mode{display:inline-flex;align-items:center;height:100%;padding:0 8px;border-radius:var(--_k-radius);color:var(--_k-fg-2);cursor:pointer;white-space:nowrap;\n transition:color calc(var(--_k-hover-out) * 1ms)}\n.knobs-ui .knobs-cmp-mode:is(:hover,:focus-visible){color:var(--_k-fg)}\n/* Toggles: icon buttons, the accent's wash when on. */\n.knobs-ui .knobs-cmp-toggle{display:inline-flex;align-items:center;justify-content:center;width:var(--_k-control-h);height:var(--_k-control-h);border-radius:var(--_k-radius);color:var(--_k-fg-3);cursor:pointer;\n transition:color calc(var(--_k-hover-out) * 1ms),background calc(var(--_k-hover-out) * 1ms)}\n.knobs-ui .knobs-cmp-toggle:is(:hover,:focus-visible){color:var(--_k-fg);background:var(--_ku-wash)}\n.knobs-ui .knobs-cmp-toggle[aria-pressed=\"true\"]{color:var(--_k-accent-fg);background:var(--_k-accent-soft)}\n.knobs-cmp-toggle svg{display:block}\n.knobs-cmp-fill{flex:1}\n.knobs-cmp-toggle[hidden]{display:none}\n/* One surface: on a card (a walkthrough's step, a split's pane, anything .knobs-surface) or marked\n data-nested, the stage drops its fill and the sides sit on that card. */\n:where(.knobs-surface,.knobs-pane:not([data-nested])>.knobs-pane-clip) .knobs-ui.knobs-compare,.knobs-ui.knobs-compare[data-nested]{--_ku-cmp-stage:var(--k-compare-stage,transparent)}\n/* In a page's flow (a container with no height), it takes the height its sides need. */\n.knobs-ui.knobs-compare[data-auto]{height:auto}\n.knobs-compare[data-auto] .knobs-cmp-stage{flex:none;min-height:0}\n.knobs-cmp-stage{position:relative;flex:1;min-height:96px;overflow:hidden;border-radius:var(--_k-radius-panel);background:var(--_ku-cmp-stage);touch-action:pan-y;outline:none}\n.knobs-compare[data-frame=\"flat\"] .knobs-cmp-stage{border-radius:0}\n.knobs-compare[data-mode=\"wipe\"] .knobs-cmp-stage{cursor:ew-resize}\n/* Videos: a click on the stage plays and pauses. */\n.knobs-compare[data-video]:not([data-mode=\"wipe\"],[data-zoomed]) .knobs-cmp-stage{cursor:pointer}\n/* Unzoomed, a finger still scrolls the page up and down; zoomed, the stage takes every gesture. */\n.knobs-compare[data-zoomed] .knobs-cmp-stage{cursor:grab;touch-action:none}\n.knobs-compare[data-panning],.knobs-compare[data-panning] *{cursor:grabbing!important}\n/* Wiping, the grip stands in for the cursor. */\n.knobs-compare[data-dragging],.knobs-compare[data-dragging] *{cursor:none!important}\n.knobs-cmp-sizer{position:absolute;left:0;right:0;top:0;height:0;visibility:hidden;pointer-events:none}\n.knobs-cmp-canvas{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none}\n.knobs-cmp-media{position:absolute;left:0;top:0;display:block;max-width:none;margin:0;padding:0;border:0;object-fit:fill;transform-origin:0 0;pointer-events:none;will-change:transform}\n.knobs-compare[data-renderer=\"gpu\"] .knobs-cmp-media{opacity:0}\n/* The media comes in with its first frames: a fade and a 4px blur on the default spring. */\n.knobs-cmp-canvas,.knobs-compare[data-renderer=\"dom\"] .knobs-cmp-media{transition:opacity calc(var(--_k-motion-default-ms) * 1ms) var(--_k-motion-default),filter calc(var(--_k-motion-default-ms) * 1ms) var(--_k-motion-default)}\n.knobs-compare:not([data-ready]) :is(.knobs-cmp-canvas,.knobs-cmp-media){opacity:0;filter:blur(4px)}\n/* Glass: what's behind shows through, blurred, on the knobs tokens. */\n.knobs-cmp-label,.knobs-cmp-zoom{background:color-mix(in srgb,var(--_k-bg) 52%,transparent);-webkit-backdrop-filter:blur(14px) saturate(1.6);backdrop-filter:blur(14px) saturate(1.6);\n box-shadow:inset 0 0 0 .5px var(--_k-border),inset 0 1px 0 var(--_k-rim),0 2px 10px rgba(0,0,0,calc(.18 * var(--_k-shadow-k,1)))}\n/* \u2717 Before \u00B7 \u2713 After: Before quieter (fg-3), After in full text. Neutral: no colour of its own. */\n.knobs-cmp-label{position:absolute;left:0;top:0;display:flex;align-items:center;gap:4px;padding:1px 7px 1px 5px;border-radius:999px;font-size:var(--_k-text-meta);line-height:16px;font-weight:var(--_k-weight-label);\n white-space:nowrap;pointer-events:none;will-change:transform}\n.knobs-cmp-label[data-side=\"before\"]{color:var(--_k-fg-3)}\n.knobs-cmp-label[data-side=\"after\"]{color:var(--_k-fg)}\n/* Fusing, the mark folds away and the label is its name. */\n.knobs-cmp-mark{flex:none;display:flex;justify-content:flex-end;overflow:hidden}\n.knobs-cmp-mark svg{display:block;flex:none;width:12px;height:12px}\n/* The fuse's seam: a blurred strip over where the sides meet, soft at both sides. */\n.knobs-cmp-seam{position:absolute;left:0;top:0;width:8px;height:0;opacity:0;pointer-events:none;will-change:transform,opacity;\n -webkit-mask-image:linear-gradient(to right,transparent,#000 35%,#000 65%,transparent);mask-image:linear-gradient(to right,transparent,#000 35%,#000 65%,transparent)}\n/* The wipe: a hairline down the line with a raised grip in its middle (at the pointer's height\n while dragged); a wider strip takes the hand and the keys (a slider). It fades in with the\n fuse; wiping, line and grip brighten. */\n.knobs-cmp-handle{cursor:ew-resize;position:absolute;left:-12px;top:0;width:24px;height:0;opacity:0;pointer-events:none;outline:none;will-change:transform,opacity}\n.knobs-cmp-handle[data-on]{pointer-events:auto}\n.knobs-cmp-handle::before{content:\"\";position:absolute;top:0;bottom:0;left:11.5px;width:1px;background:color-mix(in srgb,var(--_ku-cmp-line) 72%,transparent);box-shadow:0 0 0 .5px rgba(0,0,0,.28),0 0 6px rgba(0,0,0,.25)}\n.knobs-compare[data-dragging] .knobs-cmp-handle::before{background:var(--_ku-cmp-line)}\n.knobs-cmp-grip{position:absolute;top:0;left:4px;width:16px;height:28px;border-radius:min(8px,calc(var(--_k-radius) * 999));background:var(--_k-bg-overlay);\n box-shadow:var(--_k-shadow-overlay),inset 0 0 0 1px var(--_k-border),inset 0 1px 0 var(--_k-rim);will-change:transform;transition:box-shadow calc(var(--_k-hover-out) * 1ms)}\n.knobs-cmp-grip::before,.knobs-cmp-grip::after{content:\"\";position:absolute;top:10px;width:1px;height:8px;background:var(--_k-fg-3);transition:background calc(var(--_k-hover-out) * 1ms)}\n.knobs-cmp-grip::before{left:6px}\n.knobs-cmp-grip::after{left:9px}\n.knobs-compare[data-dragging] .knobs-cmp-grip{box-shadow:var(--_k-shadow-overlay),inset 0 0 0 1px var(--_k-border-strong),inset 0 1px 0 var(--_k-rim)}\n.knobs-compare[data-dragging] .knobs-cmp-grip::before,.knobs-compare[data-dragging] .knobs-cmp-grip::after{background:var(--_k-fg)}\n.knobs-cmp-handle:focus-visible .knobs-cmp-grip,.knobs-compare[data-keyboard] .knobs-cmp-handle:focus .knobs-cmp-grip{box-shadow:var(--_k-shadow-overlay),0 0 0 2px var(--_k-accent)}\n/* The split's hairline, down the middle side by side. */\n.knobs-cmp-split{position:absolute;left:0;top:0;width:1px;height:0;margin-left:-.5px;background:color-mix(in srgb,var(--_k-bg) 72%,transparent);pointer-events:none;will-change:transform,opacity}\n/* Zoomed: how far, and a way back, in the stage's corner; it fades in and out, nothing shifts. */\n.knobs-cmp-zoom{position:absolute;right:8px;bottom:8px;display:flex;align-items:center;gap:4px;height:26px;padding:0 3px 0 10px;border-radius:999px;\n color:var(--_k-fg);font-size:var(--_k-text-meta);font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";white-space:nowrap;\n opacity:0;scale:.96;pointer-events:none;transition:opacity .16s cubic-bezier(.4,0,1,1),scale .16s cubic-bezier(.4,0,1,1)}\n.knobs-cmp-zoom[data-on]{opacity:1;scale:1;pointer-events:auto;transition:opacity .2s cubic-bezier(.2,.8,.2,1),scale .2s cubic-bezier(.2,.8,.2,1)}\n:where(.knobs-ui) .knobs-cmp-zoom-reset{all:revert;box-sizing:border-box;font:inherit;letter-spacing:inherit;margin:0;border:0;outline:none;box-shadow:none;\n display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border-radius:999px;background:transparent;color:var(--_k-fg-2);cursor:pointer;transition:color .15s,background .15s}\n:where(.knobs-ui) .knobs-cmp-zoom-reset:is(:hover,:focus-visible){color:var(--_k-fg);background:color-mix(in srgb,var(--_k-fg) 12%,transparent)}\n.knobs-cmp-note{position:absolute;left:50%;top:50%;translate:-50% -50%;color:var(--_k-fg-3);font-size:var(--_k-text-meta);pointer-events:none}\n.knobs-cmp-note:empty{display:none}\n.knobs-cmp-strip-host{flex:none;height:60px;min-height:0}\n.knobs-cmp-timeline-host{flex:none;min-height:0}\n/* The frame strip: Before over After per column, a difference bar under them, a dot where it peaks. */\n.knobs-ui.knobs-cmp-strip{--_ku-cmp-diff:var(--k-compare-diff,var(--_k-ok));position:relative;width:100%;height:100%;min-height:40px;overflow:hidden;cursor:col-resize;touch-action:none;\n font:var(--_k-weight) var(--_k-font-size)/1.35 var(--_k-font);color:var(--_k-fg)}\n.knobs-cmp-strip>canvas{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none}\n@media(prefers-reduced-motion:reduce){:is(.knobs-compare,.knobs-cmp-strip) :where(*,::before,::after){transition-duration:.12s!important}}\n";
@@ -14,7 +14,10 @@ export interface CompareDraw {
14
14
  readonly width: number;
15
15
  readonly height: number;
16
16
  readonly dpr: number;
17
- /** The wipe's line (device px) and its soft edge (device px). */
17
+ /**
18
+ * Where Before is cut (device px: the split side by side, the wipe's line fused) and its soft
19
+ * edge either side (device px). Side and Wipe composite the same way; only the cut moves.
20
+ */
18
21
  readonly wipeX: number;
19
22
  readonly soft: number;
20
23
  readonly from: number;
@@ -89,7 +89,10 @@ export interface CompareLayout {
89
89
  readonly after: Rect;
90
90
  readonly split: number;
91
91
  };
92
- /** Overlaid (wipe, difference): both in the same place. */
92
+ /**
93
+ * Overlaid (wipe, difference): both in the same place, one side's size, centred on the split,
94
+ * so fusing never resizes anything.
95
+ */
93
96
  readonly overlay: {
94
97
  readonly before: Rect;
95
98
  readonly after: Rect;
@@ -97,6 +100,38 @@ export interface CompareLayout {
97
100
  }
98
101
  /** Where the two sides sit inside `box`, side by side and overlaid. */
99
102
  export declare function compareLayout(box: Rect, before: Size, after: Size): CompareLayout;
103
+ /** The fuse's feather at its middle, the seam's blur as it starts and the overlap it fades in over (px). */
104
+ export declare const FUSE: {
105
+ readonly feather: 24;
106
+ readonly blur: 4;
107
+ readonly seamFade: 48;
108
+ };
109
+ export interface FuseFrame {
110
+ /** Where Before is cut: the split side by side, the wipe's line fused. */
111
+ readonly cut: number;
112
+ /** The cut's soft edge, px either side of it. */
113
+ readonly feather: number;
114
+ /** The seam's (and the wipe handle's) opacity, 0–1. */
115
+ readonly seam: number;
116
+ /** The seam's blur (px), gone once fused. */
117
+ readonly blur: number;
118
+ }
119
+ /**
120
+ * Kit Motion's Comparison fuse, one frame of it: side by side (`k` 0) the panes glide together
121
+ * into one (`k` 1). Before is cut only where the inner edges overlap, with a feather that swells
122
+ * mid-transition; the seam fades in as they overlap, blurred until they've fused. `before` and
123
+ * `after` are where the sides are drawn this frame.
124
+ */
125
+ export declare function fuseFrame(L: CompareLayout, before: Rect, after: Rect, k: number, wipe: number): FuseFrame;
126
+ /**
127
+ * A label folded `fold` (0 open, 1 folded to its name): its mark's width, opacity and blur (px,
128
+ * up to `blur` as it goes, kit-motion's in-place exit).
129
+ */
130
+ export declare const labelFold: (fold: number, mark: number, blur?: number) => {
131
+ width: number;
132
+ opacity: number;
133
+ blur: number;
134
+ };
100
135
  /** The height a split screen of `w` needs so neither side has margins: half its width, in shape. */
101
136
  export declare const splitHeight: (w: number, media: Size) => number;
102
137
  /**
@@ -46,8 +46,9 @@ export interface CompareOptions extends UiLook {
46
46
  readonly threshold?: number;
47
47
  /**
48
48
  * The clock: an existing `timeline()` (its time drives both sides; while playing it follows
49
- * After), or `true` for a slim one of its own under the viewer (the `"lane"` variant: its
50
- * markers are chapter ticks). Without one, `seek`, `play` and keys still work.
49
+ * After), or `true` for one of its own under the viewer: a player's thin progress line (the
50
+ * `"line"` variant, as `player()` has), its markers chapter segments. Without one, `seek`,
51
+ * `play` and keys still work. Either way a click on the stage plays and pauses (not wiping).
51
52
  */
52
53
  readonly timeline?: Timeline | boolean;
53
54
  /** The frame strip over the timeline (off; one timeline is made if there's none). */
@@ -105,12 +105,11 @@ export declare function diffCounts(file: DiffFile, doc?: Document, options?: {
105
105
  /** A file's change as a letter (M, A, D, R), for a tree's `icon`. */
106
106
  export declare function diffStatus(file: DiffFile, doc?: Document): HTMLElement;
107
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
108
+ * A code diff on the void (no card, fill or ring: a hairline under its header and one under the
109
+ * code): a quiet header with the file's path, its counts and Split | Unified over the diff, drawn by `@pierre/diffs` (Shiki highlighting, word-level changes, hunks that
110
110
  * expand when the whole files are known). Takes a unified patch or before and after pairs.
111
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.
112
+ * keeps its scroll. Needs `@pierre/diffs` installed; it loads on first use.
114
113
  */
115
114
  export declare function codeDiff(container: HTMLElement, options: CodeDiffOptions): CodeDiff;
116
115
  export interface DiffReviewOptions extends CodeDiffOptions {
@@ -147,7 +146,7 @@ export interface DiffReview {
147
146
  dispose(): void;
148
147
  }
149
148
  /**
150
- * A diff beside its file list, as a review, in one card: the list is a drawer at its left that
149
+ * A diff beside its file list, as a review, on the void as the diff is: the list is a drawer at its left that
151
150
  * springs open and shut (0 to 256px, .4 s, no bounce; the diff reflows as it moves) from a
152
151
  * button at the start of the diff's header, or ⌘B / Ctrl+B. In it, a field that narrows the list
153
152
  * by path, then the changed files as a compact tree (folders folded into paths), each with its
@@ -1 +1 @@
1
- export declare const historyCss = "\n:where(.knobs-history,.knobs-history *){box-sizing:border-box}\n.knobs-ui.knobs-history{--hy-gutter:128px;--hy-lane:28px;--hy-bar:18px;--hy-axis:20px;--hy-bandrow:0px;--hy-c:var(--_k-fg-3);\n --hy-fade:linear-gradient(to right,#000 calc(100% - 1.5em),transparent);\n position:relative;display:flex;flex-direction:column;width:100%;min-width:0;outline:none;color:var(--_k-fg);\n font:var(--_k-weight) var(--_k-type-body)/var(--_k-leading-body) var(--_k-font);font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";\n text-align:left;letter-spacing:normal;-webkit-font-smoothing:antialiased;user-select:none;-webkit-user-select:none}\n.knobs-history[data-bands]{--hy-bandrow:var(--_k-leading-small)}\n.knobs-history[data-narrow]{--hy-gutter:0px}\n.knobs-history [hidden]{display:none!important}\n:where(.knobs-ui) :is(.knobs-hy-ev,.knobs-hy-span,.knobs-hy-jump){all:revert;box-sizing:border-box;font:inherit;color:inherit;letter-spacing:inherit;\n background:transparent;border:0;border-radius:0;margin:0;padding:0;min-width:0;box-shadow:none;outline:none}\n.knobs-history [data-tone=\"good\"]{--hy-c:var(--_k-ok)}\n.knobs-history [data-tone=\"warn\"]{--hy-c:var(--_k-warn)}\n.knobs-history [data-tone=\"bad\"]{--hy-c:var(--_k-danger)}\n.knobs-history [data-tone=\"info\"]{--hy-c:var(--_k-info)}\n.knobs-history [data-tone=\"dim\"]{--hy-c:var(--_k-fg-3)}\n\n/* \u2014\u2014\u2014 the chart: lane labels beside the plot (above each lane where it's narrow) \u2014\u2014\u2014 */\n.knobs-hy-chart{position:relative;display:grid;grid-template-columns:var(--hy-gutter) minmax(0,1fr)}\n.knobs-hy-axis{position:relative;grid-column:2;grid-row:1;height:calc(var(--hy-bandrow) + var(--hy-axis))}\n:is(.knobs-hy-tick,.knobs-hy-now,.knobs-hy-blabel){position:absolute;white-space:nowrap;font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3)}\n.knobs-hy-tick{bottom:2px;padding-left:4px}\n.knobs-hy-now{bottom:2px;color:var(--_k-accent-fg);font-weight:var(--_k-weight-label)}\n.knobs-hy-blabel{top:0;padding-left:4px;overflow:hidden;color:var(--_k-fg-2)}\n.knobs-hy-label{grid-column:1;align-self:center;min-width:0;margin-right:var(--_k-space-title);overflow:hidden;white-space:nowrap;\n font-size:var(--_k-type-label);line-height:var(--_k-leading-label);color:var(--_k-fg-2)}\n.knobs-history[data-narrow] .knobs-hy-label{grid-column:2;align-self:end;margin:0;font-size:var(--_k-type-small);line-height:var(--_k-leading-small)}\n:is(.knobs-hy-label,.knobs-hy-blabel)[data-overflow]{-webkit-mask-image:var(--hy-fade);mask-image:var(--hy-fade)}\n.knobs-hy-track{position:relative;grid-column:2;height:var(--hy-lane)}\n.knobs-hy-layer{position:absolute;inset:0;grid-column:2;grid-row:1/-1;pointer-events:none}\n\n/* Bands and month lines behind the lanes; today, the selection and the pointer's line over them. */\n.knobs-hy-band{position:absolute;top:0;bottom:0;background:color-mix(in oklch,var(--_k-fg) 3.5%,transparent)}\n:is(.knobs-hy-grid,.knobs-hy-today,.knobs-hy-hover,.knobs-hy-sel){position:absolute;left:0;top:calc(var(--hy-bandrow) + var(--hy-axis));bottom:0;width:1px;margin-left:-.5px}\n.knobs-hy-grid{background:var(--_k-border-subtle)}\n.knobs-hy-today{top:calc(var(--hy-bandrow) + var(--hy-axis) - 4px);background:var(--_k-accent)}\n.knobs-hy-hover{background:color-mix(in oklch,var(--_k-fg) 32%,transparent);opacity:0;transition:opacity calc(var(--_k-hover-out) * 1ms)}\n.knobs-hy-hover[data-on]{opacity:1;transition-duration:calc(var(--_k-hover-in) * 1ms)}\n.knobs-hy-sel{background:var(--_k-fg-2);opacity:0}\n.knobs-hy-sel[data-on]{opacity:.6}\n\n/* Spans: a soft bar in their state's tone, inferred ones hatched; the tag at the end. */\n.knobs-hy-span{position:absolute;top:calc((var(--hy-lane) - var(--hy-bar)) / 2);height:var(--hy-bar);border-radius:var(--_k-radius-sm);cursor:pointer;\n background:color-mix(in oklch,var(--hy-c) 24%,transparent);transition:background calc(var(--_k-hover-out) * 1ms),box-shadow calc(var(--_k-hover-out) * 1ms)}\n.knobs-hy-span[data-inferred]{background:repeating-linear-gradient(135deg,color-mix(in oklch,var(--hy-c) 34%,transparent) 0 2px,color-mix(in oklch,var(--hy-c) 9%,transparent) 2px 6px)}\n.knobs-hy-span[data-hover]{background-color:color-mix(in oklch,var(--hy-c) 34%,transparent);transition-duration:calc(var(--_k-hover-in) * 1ms)}\n.knobs-hy-span[data-selected]{box-shadow:inset 0 0 0 1px var(--hy-c)}\n.knobs-hy-span[data-clip-start]{border-top-left-radius:0;border-bottom-left-radius:0;-webkit-mask-image:linear-gradient(to right,transparent,#000 16px);mask-image:linear-gradient(to right,transparent,#000 16px)}\n.knobs-hy-span[data-clip-end]{border-top-right-radius:0;border-bottom-right-radius:0;-webkit-mask-image:linear-gradient(to left,transparent,#000 16px);mask-image:linear-gradient(to left,transparent,#000 16px)}\n.knobs-hy-tag{position:absolute;top:0;right:6px;line-height:var(--hy-bar);font-size:var(--_k-type-small);font-weight:var(--_k-weight-label);white-space:nowrap;pointer-events:none;\n color:color-mix(in oklch,var(--hy-c) 55%,var(--_k-fg))}\n.knobs-hy-tag[data-place=\"after\"]{right:auto;left:calc(100% + 6px)}\n.knobs-hy-tag[data-place=\"none\"]{visibility:hidden}\n\n/* Event marks (their kind named in the tooltip): a dot for a commit, a diamond for a release, a cross for a deletion, a square for a\n channel's switch, a triangle for a finding; inferred ones hollow. */\n.knobs-hy-ev{position:absolute;top:0;width:16px;height:var(--hy-lane);margin-left:-8px;cursor:pointer;--hy-c:var(--_k-fg-2);--hy-s:9px}\n.knobs-hy-ev::before{content:\"\";position:absolute;left:50%;top:50%;width:var(--hy-s);height:var(--hy-s);margin:calc(var(--hy-s) / -2) 0 0 calc(var(--hy-s) / -2);\n border-radius:50%;background:var(--hy-c);transition:scale calc(var(--_k-motion-snappy-ms) * 1ms) var(--_k-motion-snappy)}\n.knobs-hy-ev[data-range]{width:calc(var(--hy-w) + 16px)}\n.knobs-hy-ev[data-range]::before{left:calc(8px - var(--hy-s) / 2);right:calc(8px - var(--hy-s) / 2);width:auto;margin-left:0;border-radius:var(--_k-round)}\n.knobs-hy-ev[data-type=\"release\"]{--hy-s:8px}\n.knobs-hy-ev[data-type=\"release\"]::before{border-radius:1.5px;rotate:45deg}\n.knobs-hy-ev[data-type=\"channel\"]{--hy-s:8px}\n.knobs-hy-ev[data-type=\"channel\"]::before{border-radius:2px}\n.knobs-hy-ev[data-type=\"audit\"]{--hy-s:10px}\n.knobs-hy-ev[data-type=\"audit\"]::before{border-radius:0;clip-path:polygon(50% 4%,100% 92%,0 92%)}\n.knobs-hy-ev[data-type=\"deletion\"]::before{border-radius:0;rotate:45deg;\n background:linear-gradient(var(--hy-c),var(--hy-c)) center/100% 1.75px no-repeat,linear-gradient(var(--hy-c),var(--hy-c)) center/1.75px 100% no-repeat}\n.knobs-hy-ev[data-inferred]:not([data-type=\"audit\"],[data-type=\"deletion\"])::before{background:transparent;box-shadow:inset 0 0 0 1.5px var(--hy-c)}\n.knobs-hy-ev[data-inferred]:is([data-type=\"audit\"],[data-type=\"deletion\"])::before{opacity:.5}\n.knobs-hy-ev[data-hover]::before{scale:1.25}\n.knobs-hy-ev[data-selected]::before{scale:1.3}\n.knobs-hy-ev[data-selected]::after{content:\"\";position:absolute;left:50%;top:50%;width:calc(var(--hy-s) + 9px);height:calc(var(--hy-s) + 9px);\n margin:calc((var(--hy-s) + 9px) / -2) 0 0 calc((var(--hy-s) + 9px) / -2);border-radius:50%;box-shadow:0 0 0 1.5px var(--hy-c)}\n.knobs-hy-ev[data-range][data-selected]::after{left:3px;right:3px;width:auto;margin-left:0;border-radius:var(--_k-round)}\n.knobs-history:focus-visible .knobs-hy-ev[data-selected]::after{box-shadow:0 0 0 2px var(--_k-focus)}\n.knobs-history:focus-visible .knobs-hy-span[data-selected]{box-shadow:inset 0 0 0 2px var(--_k-focus)}\n\n/* \u2014\u2014\u2014 legend: only what the chart can't say itself \u2014\u2014\u2014 */\n.knobs-hy-legend{display:flex;flex-wrap:wrap;gap:var(--_k-space-inside) var(--_k-space-title);margin-top:var(--_k-space-sibling);padding-left:var(--hy-gutter);\n font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3)}\n.knobs-hy-key{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}\n.knobs-hy-swatch{flex:none;width:14px;height:8px;border-radius:2px;background:color-mix(in oklch,var(--hy-c) 34%,transparent)}\n.knobs-hy-swatch[data-inferred]{background:repeating-linear-gradient(135deg,color-mix(in oklch,var(--hy-c) 60%,transparent) 0 1.5px,transparent 1.5px 4px)}\n\n/* \u2014\u2014\u2014 the detail: one area under the chart, its height on a spring \u2014\u2014\u2014 */\n.knobs-hy-detail{position:relative;overflow:hidden;user-select:text;-webkit-user-select:text}\n.knobs-hy-detail[data-moving]{-webkit-mask-image:linear-gradient(to bottom,#000 calc(100% - 16px),transparent);mask-image:linear-gradient(to bottom,#000 calc(100% - 16px),transparent)}\n.knobs-hy-dc{padding:var(--_k-space-title) 0 0 var(--hy-gutter);transition:opacity calc(var(--_k-motion-snappy-ms) * 1ms) var(--_k-motion-snappy)}\n.knobs-hy-dc:is([data-enter],[data-leave]){opacity:0}\n.knobs-hy-dc[data-leave]{position:absolute;left:0;right:0;top:0;transition-duration:calc(var(--_k-motion-snappy-ms) * .8ms)}\n.knobs-hy-dmeta{display:flex;flex-wrap:wrap;gap:0 var(--_k-space-sibling);font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3)}\n.knobs-hy-dmeta>[data-tone]{color:var(--hy-c)}\n.knobs-hy-dtitle{margin-top:var(--_k-space-inside);font-weight:var(--_k-weight-title);color:var(--_k-fg);text-wrap:balance}\n.knobs-hy-dtext{margin:var(--_k-space-inside) 0 0;max-width:68ch;color:var(--_k-fg-2);text-wrap:pretty}\n.knobs-hy-dtext code{font:calc(1em - 1px) var(--_k-mono);padding:1px 4px;border-radius:var(--_k-radius-sm);background:var(--_k-fill)}\n.knobs-hy-dfoot{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:var(--_k-space-sibling)}\n.knobs-hy-dfoot:empty{display:none}\n.knobs-hy-evidence{display:inline-flex;align-items:center;height:20px;padding:0 6px;border-radius:var(--_k-radius-sm);background:var(--_k-fill);color:var(--_k-fg-2);\n font:var(--_k-type-small)/1 var(--_k-mono);text-decoration:none;transition:background calc(var(--_k-hover-out) * 1ms),color calc(var(--_k-hover-out) * 1ms)}\na.knobs-hy-evidence:hover{background:var(--_k-fill-hover);color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}\n.knobs-ui .knobs-hy-jump{display:inline-flex;align-items:center;gap:4px;height:20px;padding:0 6px 0 8px;border-radius:var(--_k-radius-sm);color:var(--_k-fg-2);cursor:pointer;\n font-size:var(--_k-type-small);transition:background calc(var(--_k-hover-out) * 1ms),color calc(var(--_k-hover-out) * 1ms)}\n.knobs-ui .knobs-hy-jump:is(:hover,:focus-visible){background:var(--_k-fill);color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}\n.knobs-hy-jump svg{width:12px;height:12px}\n\n/* \u2014\u2014\u2014 the pointer's tooltip: title, date, one line \u2014\u2014\u2014 */\n.knobs-hy-tip{position:absolute;left:0;top:0;max-width:280px;padding:6px 8px;border-radius:var(--_k-radius-md);background:var(--_k-bg-overlay);\n box-shadow:var(--_k-shadow-overlay),0 0 0 1px var(--_k-border);pointer-events:none;opacity:0;transition:opacity calc(var(--_k-hover-out) * 1ms)}\n.knobs-hy-tip[data-on]{opacity:1;transition-duration:calc(var(--_k-hover-in) * 1ms)}\n.knobs-hy-tt{font-size:var(--_k-type-label);line-height:var(--_k-leading-label);font-weight:var(--_k-weight-label);color:var(--_k-fg);text-wrap:balance}\n:is(.knobs-hy-td,.knobs-hy-tl){font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3)}\n.knobs-hy-tl{margin-top:2px;color:var(--_k-fg-2);text-wrap:pretty}\n.knobs-hy-tl:empty{display:none}\n\n@media(prefers-reduced-motion:reduce){\n .knobs-history :where(*,::before,::after){transition-property:opacity,background,color,box-shadow!important}\n .knobs-hy-dc{transition-duration:calc(var(--_k-hover-out) * 1ms)!important}\n}\n";
1
+ export declare const historyCss = "\n:where(.knobs-history,.knobs-history *){box-sizing:border-box}\n.knobs-ui.knobs-history{--hy-gutter:128px;--hy-lane:24px;--hy-bar:16px;--hy-axis:20px;--hy-bandrow:0px;--hy-c:var(--_k-fg-3);\n --hy-fade:linear-gradient(to right,#000 calc(100% - 1.5em),transparent);\n position:relative;display:flex;flex-direction:column;width:100%;min-width:0;outline:none;color:var(--_k-fg);\n font:var(--_k-weight) var(--_k-type-body)/var(--_k-leading-body) var(--_k-font);font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";\n text-align:left;letter-spacing:normal;-webkit-font-smoothing:antialiased;user-select:none;-webkit-user-select:none}\n.knobs-history[data-bands]{--hy-bandrow:var(--_k-leading-small)}\n.knobs-history[data-narrow]{--hy-gutter:0px}\n.knobs-history [hidden]{display:none!important}\n:where(.knobs-ui) :is(.knobs-hy-ev,.knobs-hy-span,.knobs-hy-jump,.knobs-hy-more){all:revert;box-sizing:border-box;font:inherit;color:inherit;letter-spacing:inherit;\n background:transparent;border:0;border-radius:0;margin:0;padding:0;min-width:0;box-shadow:none;outline:none}\n.knobs-history [data-tone=\"good\"]{--hy-c:var(--_k-ok)}\n.knobs-history [data-tone=\"warn\"]{--hy-c:var(--_k-warn)}\n.knobs-history [data-tone=\"bad\"]{--hy-c:var(--_k-danger)}\n.knobs-history [data-tone=\"info\"]{--hy-c:var(--_k-info)}\n.knobs-history [data-tone=\"dim\"]{--hy-c:var(--_k-fg-3)}\n\n/* \u2014\u2014\u2014 the chart: lane labels beside the plot (above each lane where it's narrow) \u2014\u2014\u2014 */\n.knobs-hy-chart{position:relative;display:grid;grid-template-columns:var(--hy-gutter) minmax(0,1fr)}\n.knobs-hy-axis{position:relative;grid-column:2;grid-row:1;height:calc(var(--hy-bandrow) + var(--hy-axis))}\n:is(.knobs-hy-tick,.knobs-hy-now,.knobs-hy-blabel){position:absolute;white-space:nowrap;font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3)}\n.knobs-hy-tick{bottom:2px;padding-left:4px}\n.knobs-hy-now{bottom:2px;color:var(--_k-accent-fg);font-weight:var(--_k-weight-label)}\n.knobs-hy-blabel{top:0;padding-left:4px;overflow:hidden;color:var(--_k-fg-2)}\n.knobs-hy-label{grid-column:1;align-self:center;min-width:0;margin-right:var(--_k-space-title);overflow:hidden;white-space:nowrap;\n font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3)}\n.knobs-history[data-narrow] .knobs-hy-label{grid-column:2;align-self:end;margin:0}\n:is(.knobs-hy-label,.knobs-hy-blabel)[data-overflow]{-webkit-mask-image:var(--hy-fade);mask-image:var(--hy-fade)}\n.knobs-hy-track{position:relative;grid-column:2;height:var(--hy-lane)}\n.knobs-hy-layer{position:absolute;inset:0;grid-column:2;grid-row:1/-1;pointer-events:none}\n\n/* Bands and month lines behind the lanes; today, the selection and the pointer's line over them. */\n.knobs-hy-band{position:absolute;top:0;bottom:0;background:color-mix(in oklch,var(--_k-fg) 3.5%,transparent);box-shadow:inset 1px 0 0 var(--_k-border-subtle)}\n:is(.knobs-hy-grid,.knobs-hy-today,.knobs-hy-hover,.knobs-hy-sel){position:absolute;left:0;top:calc(var(--hy-bandrow) + var(--hy-axis));bottom:0;width:1px;margin-left:-.5px}\n.knobs-hy-grid{background:var(--_k-border-subtle)}\n.knobs-hy-today{top:calc(var(--hy-bandrow) + var(--hy-axis) - 4px);background:var(--_k-accent)}\n.knobs-hy-hover{background:color-mix(in oklch,var(--_k-fg) 32%,transparent);opacity:0;transition:opacity calc(var(--_k-hover-out) * 1ms)}\n.knobs-hy-hover[data-on]{opacity:1;transition-duration:calc(var(--_k-hover-in) * 1ms)}\n.knobs-hy-sel{background:var(--_k-fg-2);opacity:0}\n.knobs-hy-sel[data-on]{opacity:.6}\n\n/* Spans: a soft bar in their state's tone, inferred ones hatched; the tag at the end. */\n.knobs-hy-span{position:absolute;top:calc((var(--hy-lane) - var(--hy-bar)) / 2);height:var(--hy-bar);border-radius:var(--_k-radius-sm);cursor:pointer;\n background:color-mix(in oklch,var(--hy-c) 24%,transparent);transition:background calc(var(--_k-hover-out) * 1ms),box-shadow calc(var(--_k-hover-out) * 1ms)}\n.knobs-hy-span[data-inferred]{background:repeating-linear-gradient(135deg,color-mix(in oklch,var(--hy-c) 34%,transparent) 0 2px,color-mix(in oklch,var(--hy-c) 9%,transparent) 2px 6px)}\n.knobs-hy-span[data-hover]{background-color:color-mix(in oklch,var(--hy-c) 34%,transparent);transition-duration:calc(var(--_k-hover-in) * 1ms)}\n.knobs-hy-span[data-selected]{box-shadow:inset 0 0 0 1px var(--hy-c)}\n.knobs-hy-span[data-clip-start]{border-top-left-radius:0;border-bottom-left-radius:0;-webkit-mask-image:linear-gradient(to right,transparent,#000 16px);mask-image:linear-gradient(to right,transparent,#000 16px)}\n.knobs-hy-span[data-clip-end]{border-top-right-radius:0;border-bottom-right-radius:0;-webkit-mask-image:linear-gradient(to left,transparent,#000 16px);mask-image:linear-gradient(to left,transparent,#000 16px)}\n.knobs-hy-tag{position:absolute;top:0;right:6px;font-size:var(--_k-type-small);line-height:var(--hy-bar);white-space:nowrap;pointer-events:none;\n color:color-mix(in oklch,var(--hy-c) 55%,var(--_k-fg))}\n.knobs-hy-tag[data-place=\"after\"]{right:auto;left:calc(100% + 6px)}\n.knobs-hy-tag[data-place=\"none\"]{visibility:hidden}\n\n/* Event marks (their kind named in the tooltip): a dot for a commit, a diamond for a release, a cross for a deletion, a square for a\n channel's switch, a triangle for a finding; inferred ones hollow. */\n.knobs-hy-ev{position:absolute;top:0;width:16px;height:var(--hy-lane);margin-left:-8px;cursor:pointer;--hy-c:var(--_k-fg-2);--hy-s:9px}\n.knobs-hy-ev::before{content:\"\";position:absolute;left:50%;top:50%;width:var(--hy-s);height:var(--hy-s);margin:calc(var(--hy-s) / -2) 0 0 calc(var(--hy-s) / -2);\n border-radius:50%;background:var(--hy-c);transition:scale calc(var(--_k-motion-snappy-ms) * 1ms) var(--_k-motion-snappy)}\n.knobs-hy-ev[data-range]{width:calc(var(--hy-w) + 16px)}\n.knobs-hy-ev[data-range]::before{left:calc(8px - var(--hy-s) / 2);right:calc(8px - var(--hy-s) / 2);width:auto;margin-left:0;border-radius:var(--_k-round)}\n.knobs-hy-ev[data-type=\"release\"]{--hy-s:8px}\n.knobs-hy-ev[data-type=\"release\"]::before{border-radius:1.5px;rotate:45deg}\n.knobs-hy-ev[data-type=\"channel\"]{--hy-s:8px}\n.knobs-hy-ev[data-type=\"channel\"]::before{border-radius:2px}\n.knobs-hy-ev[data-type=\"audit\"]{--hy-s:10px}\n.knobs-hy-ev[data-type=\"audit\"]::before{border-radius:0;clip-path:polygon(50% 4%,100% 92%,0 92%)}\n.knobs-hy-ev[data-type=\"deletion\"]::before{border-radius:0;rotate:45deg;\n background:linear-gradient(var(--hy-c),var(--hy-c)) center/100% 1.75px no-repeat,linear-gradient(var(--hy-c),var(--hy-c)) center/1.75px 100% no-repeat}\n.knobs-hy-ev[data-inferred]:not([data-type=\"audit\"],[data-type=\"deletion\"])::before{background:transparent;box-shadow:inset 0 0 0 1.5px var(--hy-c)}\n.knobs-hy-ev[data-inferred]:is([data-type=\"audit\"],[data-type=\"deletion\"])::before{opacity:.5}\n/* Held-back marks (commits, by default) come up while the pointer is on their lane, and stay with\n \"Show commits\" or while selected. */\n.knobs-hy-ev[data-quiet]{opacity:0;transition:opacity calc(var(--_k-hover-out) * 1ms)}\n.knobs-hy-track:hover .knobs-hy-ev[data-quiet]{opacity:.6;transition-duration:calc(var(--_k-motion-snappy-ms) * 1ms)}\n:is(.knobs-history[data-all] .knobs-hy-ev[data-quiet],.knobs-hy-ev[data-quiet]:is([data-selected],[data-hover])){opacity:1}\n.knobs-hy-ev[data-hover]::before{scale:1.25}\n.knobs-hy-ev[data-selected]::before{scale:1.3}\n.knobs-hy-ev[data-selected]::after{content:\"\";position:absolute;left:50%;top:50%;width:calc(var(--hy-s) + 9px);height:calc(var(--hy-s) + 9px);\n margin:calc((var(--hy-s) + 9px) / -2) 0 0 calc((var(--hy-s) + 9px) / -2);border-radius:50%;box-shadow:0 0 0 1.5px var(--hy-c)}\n.knobs-hy-ev[data-range][data-selected]::after{left:3px;right:3px;width:auto;margin-left:0;border-radius:var(--_k-round)}\n.knobs-history:focus-visible .knobs-hy-ev[data-selected]::after{box-shadow:0 0 0 2px var(--_k-focus)}\n.knobs-history:focus-visible .knobs-hy-span[data-selected]{box-shadow:inset 0 0 0 2px var(--_k-focus)}\n\n/* \u2014\u2014\u2014 under the chart: \"Show commits\", and a legend only when inferred and verified both appear \u2014\u2014\u2014 */\n.knobs-hy-foot{display:flex;flex-wrap:wrap;align-items:center;gap:var(--_k-space-inside) var(--_k-space-title);margin-top:var(--_k-space-sibling);padding-left:var(--hy-gutter);\n font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3)}\n.knobs-hy-legend{display:flex;flex-wrap:wrap;gap:var(--_k-space-inside) var(--_k-space-title)}\n.knobs-ui .knobs-hy-more{height:20px;margin-left:-6px;padding:0 6px;border-radius:var(--_k-radius-sm);color:var(--_k-fg-3);cursor:pointer;\n transition:background calc(var(--_k-hover-out) * 1ms),color calc(var(--_k-hover-out) * 1ms)}\n.knobs-ui .knobs-hy-more:is(:hover,:focus-visible){background:var(--_k-fill);color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}\n.knobs-hy-key{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}\n.knobs-hy-swatch{flex:none;width:14px;height:8px;border-radius:2px;background:color-mix(in oklch,var(--hy-c) 34%,transparent)}\n.knobs-hy-swatch[data-inferred]{background:repeating-linear-gradient(135deg,color-mix(in oklch,var(--hy-c) 60%,transparent) 0 1.5px,transparent 1.5px 4px)}\n\n/* \u2014\u2014\u2014 the detail: one area under the chart, its height on a spring \u2014\u2014\u2014 */\n.knobs-hy-detail{position:relative;overflow:hidden;user-select:text;-webkit-user-select:text}\n.knobs-hy-detail[data-moving]{-webkit-mask-image:linear-gradient(to bottom,#000 calc(100% - 16px),transparent);mask-image:linear-gradient(to bottom,#000 calc(100% - 16px),transparent)}\n.knobs-hy-dc{padding:var(--_k-space-title) 0 0 var(--hy-gutter);transition:opacity calc(var(--_k-motion-snappy-ms) * 1ms) var(--_k-motion-snappy)}\n.knobs-hy-dc:is([data-enter],[data-leave]){opacity:0}\n.knobs-hy-dc[data-leave]{position:absolute;left:0;right:0;top:0;transition-duration:calc(var(--_k-motion-snappy-ms) * .8ms)}\n.knobs-hy-dmeta{display:flex;flex-wrap:wrap;gap:0 var(--_k-space-sibling);font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3)}\n.knobs-hy-dmeta>[data-tone]{color:var(--hy-c)}\n.knobs-hy-dtitle{margin-top:var(--_k-space-inside);font-size:var(--_k-type-body);line-height:var(--_k-leading-body);font-weight:var(--_k-weight-title);color:var(--_k-fg);text-wrap:balance}\n.knobs-hy-dtext{margin:var(--_k-space-inside) 0 0;max-width:68ch;font-size:var(--_k-type-body);line-height:var(--_k-leading-body);color:var(--_k-fg-2);text-wrap:pretty}\n.knobs-hy-dtext code{font:calc(1em - 1px) var(--_k-mono);padding:1px 4px;border-radius:var(--_k-radius-sm);background:var(--_k-fill)}\n.knobs-hy-dfoot{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:var(--_k-space-sibling)}\n.knobs-hy-dfoot:empty{display:none}\n.knobs-hy-evidence{display:inline-flex;align-items:center;height:20px;padding:0 6px;border-radius:var(--_k-radius-sm);background:var(--_k-fill);color:var(--_k-fg-2);\n font:var(--_k-type-small)/1 var(--_k-mono);text-decoration:none;transition:background calc(var(--_k-hover-out) * 1ms),color calc(var(--_k-hover-out) * 1ms)}\na.knobs-hy-evidence:hover{background:var(--_k-fill-hover);color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}\n.knobs-ui .knobs-hy-jump{display:inline-flex;align-items:center;gap:4px;height:20px;padding:0 6px 0 8px;border-radius:var(--_k-radius-sm);color:var(--_k-fg-2);cursor:pointer;\n font-size:var(--_k-type-small);transition:background calc(var(--_k-hover-out) * 1ms),color calc(var(--_k-hover-out) * 1ms)}\n.knobs-ui .knobs-hy-jump:is(:hover,:focus-visible){background:var(--_k-fill);color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}\n.knobs-hy-jump svg{width:12px;height:12px}\n\n/* \u2014\u2014\u2014 the pointer's tooltip: title, date, one line \u2014\u2014\u2014 */\n.knobs-hy-tip{position:absolute;left:0;top:0;max-width:280px;padding:6px 8px;border-radius:var(--_k-radius-md);background:var(--_k-bg-overlay);\n box-shadow:var(--_k-shadow-overlay),0 0 0 1px var(--_k-border);pointer-events:none;opacity:0;transition:opacity calc(var(--_k-hover-out) * 1ms)}\n.knobs-hy-tip[data-on]{opacity:1;transition-duration:calc(var(--_k-hover-in) * 1ms)}\n.knobs-hy-tt{font-size:var(--_k-type-small);line-height:var(--_k-leading-small);font-weight:var(--_k-weight-label);color:var(--_k-fg);text-wrap:balance}\n:is(.knobs-hy-td,.knobs-hy-tl){font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3)}\n.knobs-hy-tl{margin-top:2px;color:var(--_k-fg-2);text-wrap:pretty}\n.knobs-hy-tl:empty{display:none}\n\n@media(prefers-reduced-motion:reduce){\n .knobs-history :where(*,::before,::after){transition-property:opacity,background,color,box-shadow!important}\n .knobs-hy-dc{transition-duration:calc(var(--_k-hover-out) * 1ms)!important}\n}\n";
@@ -60,7 +60,7 @@ export interface HistoryBand {
60
60
  export interface HistoryState {
61
61
  readonly label?: string;
62
62
  readonly tone?: HistoryTone;
63
- /** `"{d}d broken"` (the default `"{d}d"`), or words of its own (`"v1"`). */
63
+ /** `"{d}d"` (the default), words of its own (`"v1"`), or `""` for none. */
64
64
  readonly tag?: string;
65
65
  }
66
66
  /** The walkthrough's older shape: events under headings. */
@@ -136,7 +136,9 @@ export interface HistoryModel {
136
136
  readonly states: readonly HyState[];
137
137
  /** The event types used, in the order they first appear. */
138
138
  readonly types: readonly string[];
139
+ /** Whether anything is marked inferred, and anything verified: the legend needs both. */
139
140
  readonly inferred: boolean;
141
+ readonly verified: boolean;
140
142
  readonly items: ReadonlyMap<string, HyItem>;
141
143
  }
142
144
  export declare const dayOf: (y: number, m: number, d: number) => Day;
@@ -173,6 +175,15 @@ export declare function fromGroups(groups: readonly HistoryGroup[], today: Day):
173
175
  export declare function iso(day: Day): string;
174
176
  /** The document filled in: whole days, lanes with their spans and events, the axis and the order. */
175
177
  export declare function normalizeHistory(data: HistoryData, now?: Day): HistoryModel;
178
+ /** The marks drawn from the start: the rest wait for the pointer or "Show …". */
179
+ export declare const DEFAULT_MARKS: readonly string[];
180
+ /**
181
+ * The events held back until asked for: those with a type that isn't in `marks`. Untyped events
182
+ * always show, and where holding back would leave no mark at all, none are.
183
+ */
184
+ export declare function quietEvents(events: readonly HyItem[], marks: readonly string[] | "all"): Set<string>;
185
+ /** What the toggle shows: `"commits"`, `"commits and findings"`. */
186
+ export declare function quietLabel(events: readonly HyItem[], quiet: ReadonlySet<string>): string;
176
187
  /** The order ←/→ walk: by date, then lane (top first), then as written. */
177
188
  export declare function eventOrder(events: readonly {
178
189
  item: HyItem;
@@ -183,7 +194,9 @@ export declare function eventOrder(events: readonly {
183
194
  * The event ←/→ land on from `current`: the next or the one before, stopping at the ends; from
184
195
  * nothing (or a span), → the first and ← the last.
185
196
  */
186
- export declare function stepEvent(order: readonly HyItem[], current: string | null, dir: 1 | -1): HyItem | null;
197
+ export declare function stepEvent(order: readonly HyItem[], current: string | null, dir: 1 | -1,
198
+ /** Where the selection is when it isn't in `order` (a held-back mark): carry on from there. */
199
+ at?: Day): HyItem | null;
187
200
  /** Where a day sits across `width` (pixels), and the day at an x. */
188
201
  export declare function dayScale(from: Day, to: Day, width: number): {
189
202
  x: (day: number) => number;
@@ -14,6 +14,11 @@ export interface HistoryOptions extends UiLook {
14
14
  readonly label?: string;
15
15
  /** Under this width (px) lane labels sit above their lanes instead of beside them (560). */
16
16
  readonly narrow?: number;
17
+ /**
18
+ * The event types marked from the start (releases and deletions, or `"all"`); the rest show
19
+ * while the pointer is on their lane, and all of them behind a "Show commits" toggle.
20
+ */
21
+ readonly marks?: readonly string[] | "all";
17
22
  }
18
23
  export interface History {
19
24
  readonly element: HTMLElement;
@@ -15,9 +15,10 @@ export { tooltip, tooltips, type TooltipContent, type TooltipOptions } from "./t
15
15
  export { split, type PaneAction, type PaneOptions, type Split, type SplitFold, type SplitOptions, type SplitPane, } from "./split.js";
16
16
  export { COLLAPSE_PAST, drag as dragDivider, parseLayout, softClamp, solve as solveSplit, type PaneSpec, type SplitDirection, type SplitLayout, } from "./split-model.js";
17
17
  export { timeline, type SeekSource, type Timeline, type TimelineMarker, type TimelineListener, type TimelineOptions, type TimelineRange, } from "./timeline.js";
18
- export { adjacentMarker, advance, CATCH_UP, clampView, currentMarker, followView, frameTime, magnet, moveHead, rulerTicks, scrubTime, timeLabel, toFrame, zoomView, type HeadPosition, type Tick, type Ticks, type TimeUnits, type TimeView, } from "./timeline-model.js";
18
+ export { adjacentMarker, advance, CATCH_UP, clampView, currentMarker, followView, frameTime, magnet, moveHead, rulerTicks, scrubTime, minutes, timeLabel, toFrame, zoomView, type HeadPosition, type Tick, type Ticks, type TimeUnits, type TimeView, } from "./timeline-model.js";
19
19
  export { compare, COMPARE_FFMPEG, type Compare, type CompareLoop, type CompareMode, type CompareOptions, type CompareSource, type CompareView, type MediaKind, type ZoomRegion, } from "./compare.js";
20
20
  export { compareStrip, type CompareStrip, type CompareStripOptions } from "./compare-strip.js";
21
+ export { player, type Player, type PlayerChapter, type PlayerOptions } from "./player.js";
21
22
  export { compareLayout, compareModes, frameMidpoint, frameOf, gripTop, letterbox, mediaKind, meanAbsDiff, modeForKey, requestSeek, seamLabels, seeked, seekTime, stripColumns, stripPeaks, syncAction, wipeAt, wipeGrab, wipeKey, wipeX, zoomAbout, type CompareLayout, type SeekState, type StripColumn, type SyncAction, } from "./compare-model.js";
22
23
  export { tree, type Tree, type TreeOptions, type TreeRowContext, type TreeToggle } from "./tree.js";
23
24
  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";
@@ -0,0 +1 @@
1
+ export declare const playerCss = "\n:where(.knobs-player,.knobs-player *){box-sizing:border-box}\n.knobs-ui.knobs-player{--_kp-stage:var(--_k-compare-stage);display:flex;flex-direction:column;gap:6px;width:100%;outline:none;\n font:var(--_k-weight) var(--_k-font-size)/1.35 var(--_k-font);color:var(--_k-fg);letter-spacing:normal;text-align:left}\n:where(.knobs-surface,.knobs-pane:not([data-nested])>.knobs-pane-clip) .knobs-ui.knobs-player,.knobs-ui.knobs-player[data-nested]{--_kp-stage:var(--k-compare-stage,transparent)}\n.knobs-player-frame{position:relative;width:100%;aspect-ratio:var(--kp-aspect,16/9);overflow:hidden;border-radius:var(--_k-radius-panel);background:var(--_kp-stage);cursor:pointer}\n.knobs-player[data-frame=\"flat\"] .knobs-player-frame{border-radius:0}\n.knobs-player-media{position:absolute;inset:0;display:block;width:100%;height:100%;margin:0;padding:0;border:0;object-fit:contain;\n opacity:0;filter:blur(4px);transition:opacity calc(var(--_k-motion-default-ms) * 1ms) var(--_k-motion-default),filter calc(var(--_k-motion-default-ms) * 1ms) var(--_k-motion-default)}\n.knobs-player[data-ready] .knobs-player-media{opacity:1;filter:none}\n.knobs-player-glyph{position:absolute;left:50%;top:50%;display:grid;place-items:center;width:44px;height:44px;margin:-22px 0 0 -22px;border-radius:50%;color:var(--_k-fg);pointer-events:none;\n opacity:0;filter:blur(4px);scale:.96;\n background:color-mix(in srgb,var(--_k-bg) 52%,transparent);-webkit-backdrop-filter:blur(14px) saturate(1.6);backdrop-filter:blur(14px) saturate(1.6);\n box-shadow:inset 0 0 0 .5px var(--_k-border),inset 0 1px 0 var(--_k-rim);\n transition:opacity calc(var(--_k-motion-snappy-ms) * 1ms) var(--_k-motion-snappy),filter calc(var(--_k-motion-snappy-ms) * 1ms) var(--_k-motion-snappy),scale calc(var(--_k-motion-snappy-ms) * 1ms) var(--_k-motion-snappy)}\n.knobs-player-glyph svg{width:16px;height:16px}\n.knobs-player-glyph[data-flash]{opacity:1;filter:none;scale:1}\n.knobs-player-line{flex:none;min-height:0}\n";
@@ -0,0 +1,60 @@
1
+ import { type UiLook } from "./look.js";
2
+ import { type Timeline } from "./timeline.js";
3
+ /** A chapter: where it starts (seconds) and its name. */
4
+ export interface PlayerChapter {
5
+ readonly label: string;
6
+ readonly start: number;
7
+ readonly id?: string;
8
+ }
9
+ export interface PlayerOptions extends UiLook {
10
+ readonly src: string;
11
+ readonly poster?: string;
12
+ /** A WebVTT file, shown by default. */
13
+ readonly captions?: string;
14
+ /** One segment of the line each, named under the pointer. */
15
+ readonly chapters?: readonly PlayerChapter[];
16
+ /** Starts over at the end (true). */
17
+ readonly loop?: boolean;
18
+ /** True by default. */
19
+ readonly muted?: boolean;
20
+ /** Width over height before the video loads (16/9). */
21
+ readonly aspect?: number;
22
+ /** Frames per second, for stepping with `,` and `.` (30). */
23
+ readonly fps?: number;
24
+ readonly label?: string;
25
+ /** The line's play key and playhead take codes in the page's hint mode (on by default). */
26
+ readonly hints?: boolean;
27
+ /** Played or paused, at `time` (seconds). */
28
+ onPlay?(playing: boolean, time: number): void;
29
+ /** Sought by the hand (the line, a key, a chapter) or code, at rest. */
30
+ onSeek?(time: number): void;
31
+ }
32
+ export interface Player {
33
+ readonly element: HTMLElement;
34
+ readonly video: HTMLVideoElement;
35
+ readonly timeline: Timeline;
36
+ /** Seconds. */
37
+ readonly time: number;
38
+ readonly playing: boolean;
39
+ readonly duration: number;
40
+ play(): void;
41
+ pause(): void;
42
+ toggle(): void;
43
+ /** `glide`: a jump the hand didn't make; the line catches up while the video lands at once. */
44
+ seek(time: number, options?: {
45
+ readonly glide?: boolean;
46
+ }): void;
47
+ seekBy(seconds: number): void;
48
+ focus(): void;
49
+ dispose(): void;
50
+ }
51
+ export declare function player(container: HTMLElement, options: PlayerOptions): Player;
52
+ /**
53
+ * The glyph a play or a pause flashes in the middle of the media (a player's, a compare's stage):
54
+ * the state it went to, blurring in and out.
55
+ */
56
+ export declare function playGlyph(doc: Document): {
57
+ element: HTMLSpanElement;
58
+ flash(playing: boolean): void;
59
+ dispose: () => void;
60
+ };
@@ -104,3 +104,7 @@ export declare function moveHead(head: HeadPosition, t: number, motion: "jump" |
104
104
  export declare function currentMarker<M extends {
105
105
  readonly time: number;
106
106
  }>(markers: readonly M[], t: number, fps: number): M | null;
107
+ /** A time as a player shows it: `m:ss` (`1:05`), `h:mm:ss` past an hour. */
108
+ export declare function minutes(t: number): string;
109
+ /** A line's segment bounds: 0, each chapter start inside the length (once each), the end. */
110
+ export declare function chapterBounds(times: readonly number[], duration: number): number[];
@@ -48,10 +48,15 @@ export interface TimelineOptions extends UiLook {
48
48
  /**
49
49
  * `"editor"` (the default): a ruler with labels over a lane of markers to add, drag and name,
50
50
  * zoomed and panned by the wheel. `"lane"`: one slim lane under a compare or a video, with ticks
51
- * and no labels, markers as faint chapter ticks (the current one in text colour), nothing to
52
- * edit, and the wheel left to the page.
51
+ * and no labels, markers as faint chapter ticks (the current one in text colour) and nothing to
52
+ * edit. A pinch, ⌘/ctrl-wheel or = and - zoom it around the pointer, a sideways scroll pans it,
53
+ * both following the hand 1:1; a double-click or 0 fits it again. An up-and-down wheel is left
54
+ * to the page. `"line"`: a player's progress, a single thin line under a video or a compare (no
55
+ * play button: the media and Space play), one segment per chapter with a small gap between,
56
+ * each filling as the time passes it, thicker under the pointer; click or drag anywhere on it
57
+ * to seek, 1:1, and the chapter and time under the pointer show over it. Zooms as a lane does.
53
58
  */
54
- readonly variant?: "editor" | "lane";
59
+ readonly variant?: "editor" | "lane" | "line";
55
60
  readonly label?: string;
56
61
  /** The time moved: scrubbed, stepped, jumped, played (every frame) or set from code. */
57
62
  onSeek?(time: number, source: SeekSource): void;
@@ -117,3 +122,5 @@ export interface Timeline {
117
122
  dispose(): void;
118
123
  }
119
124
  export declare function timeline(container: HTMLElement, options: TimelineOptions): Timeline;
125
+ export declare const PLAY = "<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><path d=\"M5 3.6v8.8c0 .5.5.8.9.5l6.6-4.4c.4-.3.4-.8 0-1L5.9 3.1c-.4-.3-.9 0-.9.5Z\" fill=\"currentColor\"/></svg>";
126
+ export declare const PAUSE = "<svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><rect x=\"4\" y=\"3.2\" width=\"2.8\" height=\"9.6\" rx=\"1\" fill=\"currentColor\"/><rect x=\"9.2\" y=\"3.2\" width=\"2.8\" height=\"9.6\" rx=\"1\" fill=\"currentColor\"/></svg>";
@@ -59,4 +59,22 @@ export declare function disclosure(host: HTMLElement, options: {
59
59
  open?: boolean;
60
60
  content: HTMLElement;
61
61
  }): Disclosure;
62
+ declare const PR_STATE: {
63
+ readonly merged: "Merged";
64
+ readonly open: "Open";
65
+ readonly draft: "Draft";
66
+ readonly closed: "Closed";
67
+ };
68
+ export interface PRInfo {
69
+ readonly state?: keyof typeof PR_STATE;
70
+ readonly title?: string;
71
+ readonly href?: string;
72
+ }
73
+ /** A pull request as a small chip: its state's icon and number, the title and state on hover. */
74
+ export declare function prChip(doc: Document, n: string, pr: PRInfo): HTMLElement;
75
+ /**
76
+ * Turns the pull requests in some rendered prose into chips: links to one whose words are its
77
+ * number or URL, and bare `#57148`s the page can resolve. Code, buttons and media stay as they are.
78
+ */
79
+ export declare function linkPRs(root: HTMLElement, find: (n: string) => PRInfo | null): void;
62
80
  export {};
@@ -217,7 +217,10 @@ export interface WalkthroughStep {
217
217
  readonly media?: WalkthroughMedia | readonly WalkthroughMedia[];
218
218
  /** Media side by side, this many to a row where there's room; one under another by default. */
219
219
  readonly columns?: 1 | 2 | 3;
220
- /** The media after the first fold away under a quiet toggle with these words ("More clips"). */
220
+ /**
221
+ * The rest, folded away under the caption behind "More" (Markdown; blank lines start new
222
+ * paragraphs). Keep the title the claim and the caption one sentence; everything else goes here.
223
+ */
221
224
  readonly more?: string;
222
225
  /** A taste test: Kit picks one. */
223
226
  readonly options?: readonly WalkthroughOption[];
@@ -244,6 +247,8 @@ export interface WalkthroughSection {
244
247
  readonly kicker?: string | false;
245
248
  readonly status?: WalkthroughStatus;
246
249
  readonly summary?: string;
250
+ /** The rest, folded away under the summary behind "More" (Markdown). */
251
+ readonly more?: string;
247
252
  readonly steps: readonly WalkthroughStep[];
248
253
  }
249
254
  export interface WalkthroughMeta {
@@ -281,6 +286,8 @@ export interface WalkthroughDoc {
281
286
  readonly id: string;
282
287
  readonly title: string;
283
288
  readonly summary?: string;
289
+ /** The rest, folded away under the summary behind "More" (Markdown). */
290
+ readonly more?: string;
284
291
  /** Facts beside the title: branch, commit, date… */
285
292
  readonly meta?: Readonly<Record<string, string>> | readonly WalkthroughMeta[];
286
293
  /** Relative media URLs resolve against this. */
@@ -300,8 +307,22 @@ export interface WalkthroughDoc {
300
307
  readonly kickers?: "pr" | {
301
308
  readonly noun: string;
302
309
  } | false;
310
+ /**
311
+ * The pull requests the page mentions, by number: their state and title. A `#57148` in a title,
312
+ * caption or summary, a link to a pull request, and a section's PR `link` read as a small chip
313
+ * (its state's icon and number; the title on hover).
314
+ */
315
+ readonly prs?: Readonly<Record<string, WalkthroughPR>>;
316
+ /** `owner/name` on GitHub, so a bare `#57148` links to its pull request. */
317
+ readonly repo?: string;
303
318
  readonly sections: readonly WalkthroughSection[];
304
319
  }
320
+ export interface WalkthroughPR {
321
+ readonly state?: "merged" | "open" | "draft" | "closed";
322
+ readonly title?: string;
323
+ /** Its URL, when neither `repo` nor a link on the page gives it. */
324
+ readonly href?: string;
325
+ }
305
326
  type CompareMedia = Extract<WalkthroughMedia, {
306
327
  kind: "compare";
307
328
  }>;
@@ -417,6 +438,7 @@ export interface WalkSection {
417
438
  readonly kicker?: string;
418
439
  readonly status?: WalkthroughStatus;
419
440
  readonly summary?: string;
441
+ readonly more?: string;
420
442
  readonly steps: readonly WalkStep[];
421
443
  }
422
444
  export interface WalkVerdict {
@@ -440,6 +462,7 @@ export interface WalkPage {
440
462
  readonly id: string;
441
463
  readonly title: string;
442
464
  readonly summary?: string;
465
+ readonly more?: string;
443
466
  readonly meta: readonly WalkthroughMeta[];
444
467
  readonly verdicts: readonly WalkVerdict[];
445
468
  readonly sections: readonly WalkSection[];
@@ -447,6 +470,10 @@ export interface WalkPage {
447
470
  readonly steps: readonly WalkStep[];
448
471
  /** Updates and answers, newest first. */
449
472
  readonly feed: readonly FeedItem[];
473
+ /** The pull requests the page knows, by number, each with its URL when one is known. */
474
+ readonly prs: Readonly<Record<string, WalkthroughPR>>;
475
+ /** `owner/name`, when the doc gave one. */
476
+ readonly repo?: string;
450
477
  }
451
478
  export declare const DEFAULT_VERDICTS: readonly ["ship", "change", "unsure"];
452
479
  /** A URL-safe id from a title: "Retired model (HTTP 410)" → "retired-model-http-410". */
@@ -514,6 +541,10 @@ export declare function linkLabel(href: string): string;
514
541
  * ask, options lettered, media URLs resolved against `base`, and the verdicts labelled.
515
542
  */
516
543
  export declare function normalizeWalkthrough(doc: WalkthroughDoc): WalkPage;
544
+ /** The number in a pull request's URL (`…/pull/57148`), or null. */
545
+ export declare const prNumber: (href: string) => string | null;
546
+ /** A pull request by number: what the page knows, or (with a repo) its URL. */
547
+ export declare function findPR(prs: Readonly<Record<string, WalkthroughPR>>, n: string, repo?: string): WalkthroughPR | null;
517
548
  /** Whether a link is a pull (or merge) request: GitHub's `/pull/123`, GitLab's merge requests. */
518
549
  export declare const isPullRequest: (href: string) => boolean;
519
550
  /**
@@ -11,6 +11,8 @@ export interface TasteTestOptions extends UiLook {
11
11
  readonly lazy?: boolean;
12
12
  /** The page's steps, for media that name one. */
13
13
  readonly steps?: StepLinks;
14
+ /** Options can be picked (on by default); off, they sit side by side under their labels. */
15
+ readonly pickable?: boolean;
14
16
  }
15
17
  export interface TasteTest {
16
18
  readonly element: HTMLElement;
@@ -35,8 +37,18 @@ export interface WalkthroughOptions extends UiLook {
35
37
  readonly onFeedback?: (feedback: WalkthroughFeedback) => void;
36
38
  /** The page's keys (on by default). */
37
39
  readonly keys?: boolean;
38
- /** The contents and progress rail (on by default), beside the column on the right or left. */
39
- readonly rail?: boolean | "right" | "left";
40
+ /**
41
+ * Kit's review tools (off by default): progress, notes (typed, voice, ink) and their bar,
42
+ * Copy feedback, the key hints, and picking between options. Off, the page only shows: options
43
+ * sit side by side, and j / k, f and the arrows still work.
44
+ */
45
+ readonly review?: boolean;
46
+ /**
47
+ * The contents and progress rail (on by default): sticky at the page's left edge, the column
48
+ * centred in the room beside it; `"right"` mirrors it. Under 960 px it hides (with review
49
+ * on, a bar on top keeps the progress and the export).
50
+ */
51
+ readonly rail?: boolean | "left" | "right";
40
52
  /** Each step on a card of its own (off by default: steps sit on the page, hairlines between). */
41
53
  readonly cards?: boolean;
42
54
  /**