@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.
- package/README.md +25 -14
- package/dist/dev/{chunk-0w1xk9jc.js → chunk-dyq4m24m.js} +3 -3
- package/dist/dev/{chunk-76frnh2j.js → chunk-qc6szek7.js} +58 -9
- package/dist/dev/{chunk-rem3qrhz.js → chunk-y2axkmz3.js} +2 -2
- package/dist/dev/index.js +3 -3
- package/dist/panel.js +60 -11
- package/dist/themes.js +3 -3
- package/dist/types/tokens/schema.d.ts +3 -3
- package/dist/types/ui/compare-css.d.ts +1 -1
- package/dist/types/ui/compare-gpu.d.ts +4 -1
- package/dist/types/ui/compare-model.d.ts +36 -1
- package/dist/types/ui/compare.d.ts +3 -2
- package/dist/types/ui/diff.d.ts +4 -5
- package/dist/types/ui/history-css.d.ts +1 -1
- package/dist/types/ui/history-model.d.ts +15 -2
- package/dist/types/ui/history.d.ts +5 -0
- package/dist/types/ui/index.d.ts +2 -1
- package/dist/types/ui/player-css.d.ts +1 -0
- package/dist/types/ui/player.d.ts +60 -0
- package/dist/types/ui/timeline-model.d.ts +4 -0
- package/dist/types/ui/timeline.d.ts +10 -3
- package/dist/types/ui/walkthrough-blocks.d.ts +18 -0
- package/dist/types/ui/walkthrough-model.d.ts +32 -1
- package/dist/types/ui/walkthrough.d.ts +14 -2
- package/dist/ui-react.js +586 -209
- package/dist/ui-solid.js +586 -209
- package/dist/ui.js +756 -209
- package/package.json +1 -1
package/dist/ui.js
CHANGED
|
@@ -595,6 +595,8 @@ var compareCss = `
|
|
|
595
595
|
.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}
|
|
596
596
|
.knobs-compare[data-frame="flat"] .knobs-cmp-stage{border-radius:0}
|
|
597
597
|
.knobs-compare[data-mode="wipe"] .knobs-cmp-stage{cursor:ew-resize}
|
|
598
|
+
/* Videos: a click on the stage plays and pauses. */
|
|
599
|
+
.knobs-compare[data-video]:not([data-mode="wipe"],[data-zoomed]) .knobs-cmp-stage{cursor:pointer}
|
|
598
600
|
/* Unzoomed, a finger still scrolls the page up and down; zoomed, the stage takes every gesture. */
|
|
599
601
|
.knobs-compare[data-zoomed] .knobs-cmp-stage{cursor:grab;touch-action:none}
|
|
600
602
|
.knobs-compare[data-panning],.knobs-compare[data-panning] *{cursor:grabbing!important}
|
|
@@ -604,6 +606,9 @@ var compareCss = `
|
|
|
604
606
|
.knobs-cmp-canvas{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none}
|
|
605
607
|
.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}
|
|
606
608
|
.knobs-compare[data-renderer="gpu"] .knobs-cmp-media{opacity:0}
|
|
609
|
+
/* The media comes in with its first frames: a fade and a 4px blur on the default spring. */
|
|
610
|
+
.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)}
|
|
611
|
+
.knobs-compare:not([data-ready]) :is(.knobs-cmp-canvas,.knobs-cmp-media){opacity:0;filter:blur(4px)}
|
|
607
612
|
/* Glass: what's behind shows through, blurred, on the knobs tokens. */
|
|
608
613
|
.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);
|
|
609
614
|
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)))}
|
|
@@ -612,11 +617,17 @@ var compareCss = `
|
|
|
612
617
|
white-space:nowrap;pointer-events:none;will-change:transform}
|
|
613
618
|
.knobs-cmp-label[data-side="before"]{color:var(--_k-fg-3)}
|
|
614
619
|
.knobs-cmp-label[data-side="after"]{color:var(--_k-fg)}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
.knobs-cmp-
|
|
620
|
+
/* Fusing, the mark folds away and the label is its name. */
|
|
621
|
+
.knobs-cmp-mark{flex:none;display:flex;justify-content:flex-end;overflow:hidden}
|
|
622
|
+
.knobs-cmp-mark svg{display:block;flex:none;width:12px;height:12px}
|
|
623
|
+
/* The fuse's seam: a blurred strip over where the sides meet, soft at both sides. */
|
|
624
|
+
.knobs-cmp-seam{position:absolute;left:0;top:0;width:8px;height:0;opacity:0;pointer-events:none;will-change:transform,opacity;
|
|
625
|
+
-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)}
|
|
626
|
+
/* The wipe: a hairline down the line with a raised grip in its middle (at the pointer's height
|
|
627
|
+
while dragged); a wider strip takes the hand and the keys (a slider). It fades in with the
|
|
628
|
+
fuse; wiping, line and grip brighten. */
|
|
629
|
+
.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}
|
|
630
|
+
.knobs-cmp-handle[data-on]{pointer-events:auto}
|
|
620
631
|
.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)}
|
|
621
632
|
.knobs-compare[data-dragging] .knobs-cmp-handle::before{background:var(--_ku-cmp-line)}
|
|
622
633
|
.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);
|
|
@@ -648,6 +659,27 @@ var compareCss = `
|
|
|
648
659
|
@media(prefers-reduced-motion:reduce){:is(.knobs-compare,.knobs-cmp-strip) :where(*,::before,::after){transition-duration:.12s!important}}
|
|
649
660
|
`;
|
|
650
661
|
|
|
662
|
+
// src/ui/player-css.ts
|
|
663
|
+
var playerCss = `
|
|
664
|
+
:where(.knobs-player,.knobs-player *){box-sizing:border-box}
|
|
665
|
+
.knobs-ui.knobs-player{--_kp-stage:var(--_k-compare-stage);display:flex;flex-direction:column;gap:6px;width:100%;outline:none;
|
|
666
|
+
font:var(--_k-weight) var(--_k-font-size)/1.35 var(--_k-font);color:var(--_k-fg);letter-spacing:normal;text-align:left}
|
|
667
|
+
: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)}
|
|
668
|
+
.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}
|
|
669
|
+
.knobs-player[data-frame="flat"] .knobs-player-frame{border-radius:0}
|
|
670
|
+
.knobs-player-media{position:absolute;inset:0;display:block;width:100%;height:100%;margin:0;padding:0;border:0;object-fit:contain;
|
|
671
|
+
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)}
|
|
672
|
+
.knobs-player[data-ready] .knobs-player-media{opacity:1;filter:none}
|
|
673
|
+
.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;
|
|
674
|
+
opacity:0;filter:blur(4px);scale:.96;
|
|
675
|
+
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);
|
|
676
|
+
box-shadow:inset 0 0 0 .5px var(--_k-border),inset 0 1px 0 var(--_k-rim);
|
|
677
|
+
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)}
|
|
678
|
+
.knobs-player-glyph svg{width:16px;height:16px}
|
|
679
|
+
.knobs-player-glyph[data-flash]{opacity:1;filter:none;scale:1}
|
|
680
|
+
.knobs-player-line{flex:none;min-height:0}
|
|
681
|
+
`;
|
|
682
|
+
|
|
651
683
|
// src/ui/overlay-css.ts
|
|
652
684
|
var overlayCss = `
|
|
653
685
|
.knobs-layers{position:fixed;inset:0;z-index:2147480005;pointer-events:none}
|
|
@@ -1512,7 +1544,7 @@ var init_schema = __esm(() => {
|
|
|
1512
1544
|
type: "length",
|
|
1513
1545
|
group: "Type",
|
|
1514
1546
|
source: value("20px"),
|
|
1515
|
-
doc: "
|
|
1547
|
+
doc: "A page's title, weight 500."
|
|
1516
1548
|
},
|
|
1517
1549
|
{
|
|
1518
1550
|
name: "leading-heading",
|
|
@@ -1528,7 +1560,7 @@ var init_schema = __esm(() => {
|
|
|
1528
1560
|
type: "length",
|
|
1529
1561
|
group: "Type",
|
|
1530
1562
|
source: value("16px"),
|
|
1531
|
-
doc: "
|
|
1563
|
+
doc: "Section titles (under their kicker), weight 500."
|
|
1532
1564
|
},
|
|
1533
1565
|
{
|
|
1534
1566
|
name: "leading-title",
|
|
@@ -1560,7 +1592,7 @@ var init_schema = __esm(() => {
|
|
|
1560
1592
|
type: "length",
|
|
1561
1593
|
group: "Type",
|
|
1562
1594
|
source: value("12px"),
|
|
1563
|
-
doc: "
|
|
1595
|
+
doc: "Mono inside body text: code and paths."
|
|
1564
1596
|
},
|
|
1565
1597
|
{
|
|
1566
1598
|
name: "leading-label",
|
|
@@ -3082,8 +3114,6 @@ var css2 = `
|
|
|
3082
3114
|
.knobs-tl-hover{position:absolute;left:0;top:0;bottom:0;width:1px;margin-left:-.5px;background:var(--_k-border-strong);transform:translateX(-100px);pointer-events:none;opacity:0;transition:opacity .15s}
|
|
3083
3115
|
.knobs-tl-track:hover .knobs-tl-hover{opacity:1}
|
|
3084
3116
|
.knobs-timeline[data-scrubbing] .knobs-tl-hover{opacity:0}
|
|
3085
|
-
@media(prefers-reduced-motion:reduce){.knobs-timeline :where(*,::before,::after){transition:none!important}}
|
|
3086
|
-
@media(prefers-reduced-motion:reduce){:is(.knobs-tree,.knobs-split>.knobs-divider,.knobs-pane-head,.knobs-pane-rail) :where(*,::before,::after){transition:none!important}}
|
|
3087
3117
|
.knobs-tl-tag{position:absolute;left:0;bottom:2px;font-size:var(--_k-text-meta);line-height:16px;color:var(--_k-fg);font-variant-numeric:tabular-nums;font-feature-settings:"tnum";white-space:nowrap;pointer-events:none;opacity:0}
|
|
3088
3118
|
.knobs-tl-tag[data-on]{opacity:1}
|
|
3089
3119
|
/* The lane (variant: "lane"): one slim track of ticks under a compare or a video; markers are
|
|
@@ -3099,7 +3129,26 @@ var css2 = `
|
|
|
3099
3129
|
.knobs-timeline[data-variant="lane"] .knobs-tl-head::before{top:0}
|
|
3100
3130
|
.knobs-timeline[data-variant="lane"] .knobs-tl-head::after{content:none}
|
|
3101
3131
|
.knobs-timeline[data-variant="lane"] .knobs-tl-tag{top:2px;bottom:auto}
|
|
3102
|
-
|
|
3132
|
+
/* The line (variant: "line"): a player's progress under its media. One 2px line, fg-3 with fg
|
|
3133
|
+
filling it, a segment per chapter; it thickens under the pointer and while scrubbing. The
|
|
3134
|
+
chapter and time under the pointer float over it; nothing else shows. */
|
|
3135
|
+
.knobs-ui.knobs-timeline[data-variant="line"]{height:auto;min-height:0;padding:0;gap:0}
|
|
3136
|
+
.knobs-timeline[data-variant="line"] .knobs-tl-transport{display:none}
|
|
3137
|
+
.knobs-ui.knobs-timeline[data-variant="line"] .knobs-tl-track{height:16px;overflow:visible;border-radius:var(--_k-radius);background:transparent;box-shadow:none;cursor:pointer}
|
|
3138
|
+
.knobs-timeline[data-variant="line"] :is(.knobs-tl-ruler,.knobs-tl-lane,.knobs-tl-hover,.knobs-tl-head,.knobs-tl-rlabel){display:none}
|
|
3139
|
+
.knobs-tl-segs{position:absolute;left:0;right:0;top:50%;height:2px;margin-top:-1px;pointer-events:none;transition:scale calc(var(--_k-hover-out) * 1ms) var(--_ku-ease)}
|
|
3140
|
+
.knobs-tl-seg{position:absolute;left:0;top:0;height:100%;overflow:hidden;border-radius:1px;background:var(--_k-fg-3);will-change:transform}
|
|
3141
|
+
.knobs-tl-seg-fill{position:absolute;inset:0;background:var(--_k-fg);transform-origin:0 0;will-change:transform}
|
|
3142
|
+
.knobs-timeline[data-variant="line"] .knobs-tl-track:hover .knobs-tl-segs,.knobs-timeline[data-variant="line"][data-scrubbing] .knobs-tl-segs{scale:1 2}
|
|
3143
|
+
.knobs-timeline[data-variant="line"] .knobs-tl-range{top:3px;bottom:3px;border-radius:3px}
|
|
3144
|
+
.knobs-timeline[data-variant="line"]:focus-visible .knobs-tl-track,.knobs-timeline[data-variant="line"][data-keyboard]:focus .knobs-tl-track{background:transparent;box-shadow:0 0 0 2px color-mix(in srgb,var(--_k-accent) 60%,transparent)}
|
|
3145
|
+
.knobs-timeline[data-variant="line"] .knobs-tl-tag{top:auto;bottom:calc(100% + 2px);display:flex;gap:6px;padding:0 6px;border-radius:calc(var(--_k-radius) * 999);background:color-mix(in srgb,var(--_k-bg) 72%,transparent);
|
|
3146
|
+
-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:inset 0 0 0 .5px var(--_k-border)}
|
|
3147
|
+
.knobs-tl-tag-label{color:var(--_k-fg-2)}
|
|
3148
|
+
.knobs-tl-tag-label:empty{display:none}
|
|
3149
|
+
@media(prefers-reduced-motion:reduce){.knobs-timeline :where(*,::before,::after){transition:none!important}}
|
|
3150
|
+
@media(prefers-reduced-motion:reduce){:is(.knobs-tree,.knobs-split>.knobs-divider,.knobs-pane-head,.knobs-pane-rail) :where(*,::before,::after){transition:none!important}}
|
|
3151
|
+
`, uiCss = () => aliased(css2 + readoutCss + overlayCss + bentoCss + activityCss + pickerCss + compareCss + playerCss + selectionPillCss) + rolling_css_default, RESTYLED = ".knobs-ui,.knobs-panel,.knobs-chip,.knobs-notice", lookDefaults, tune = (s, m) => {
|
|
3103
3152
|
s.omega = m.omega;
|
|
3104
3153
|
s.zeta = m.zeta;
|
|
3105
3154
|
return s;
|
|
@@ -5478,6 +5527,17 @@ function currentMarker(markers, t, fps) {
|
|
|
5478
5527
|
best = m;
|
|
5479
5528
|
return best;
|
|
5480
5529
|
}
|
|
5530
|
+
function minutes(t) {
|
|
5531
|
+
const s = Math.max(0, Math.floor(t + 0.000001));
|
|
5532
|
+
const h = Math.floor(s / 3600);
|
|
5533
|
+
const m = Math.floor(s % 3600 / 60);
|
|
5534
|
+
const ss = String(s % 60).padStart(2, "0");
|
|
5535
|
+
return h > 0 ? `${h}:${String(m).padStart(2, "0")}:${ss}` : `${m}:${ss}`;
|
|
5536
|
+
}
|
|
5537
|
+
function chapterBounds(times, duration) {
|
|
5538
|
+
const inside = times.filter((t) => t > 0.000001 && t < duration - 0.000001);
|
|
5539
|
+
return [0, ...[...new Set(inside)].sort((a, b) => a - b), duration];
|
|
5540
|
+
}
|
|
5481
5541
|
var SECOND_STEPS, FRAME_STEPS, toFrame = (t, fps) => Math.round(t * fps), frameTime = (t, fps) => Math.round(t * fps) / fps, round = (t) => Math.round(t * 1e6) / 1e6, CATCH_UP;
|
|
5482
5542
|
var init_timeline_model = __esm(() => {
|
|
5483
5543
|
SECOND_STEPS = [0.1, 0.2, 0.25, 0.5, 1, 2, 5, 10, 15, 30, 60, 120, 300, 600, 900, 1800, 3600];
|
|
@@ -5495,7 +5555,8 @@ function timeline(container, options) {
|
|
|
5495
5555
|
let speed = options.speed ?? 1;
|
|
5496
5556
|
let repeat = options.repeat ?? true;
|
|
5497
5557
|
const arrows = options.arrows ?? "frames";
|
|
5498
|
-
const
|
|
5558
|
+
const line = options.variant === "line";
|
|
5559
|
+
const lane = line || options.variant === "lane";
|
|
5499
5560
|
let time = Math.min(duration, Math.max(0, options.time ?? 0));
|
|
5500
5561
|
let playing = false;
|
|
5501
5562
|
let markers = [...options.markers ?? []];
|
|
@@ -5530,7 +5591,7 @@ function timeline(container, options) {
|
|
|
5530
5591
|
root.setAttribute("aria-keyshortcuts", "Space ArrowLeft ArrowRight , . Home End [ ] I O M");
|
|
5531
5592
|
applyLook2(root, options);
|
|
5532
5593
|
if (lane)
|
|
5533
|
-
root.dataset.variant = "lane";
|
|
5594
|
+
root.dataset.variant = line ? "line" : "lane";
|
|
5534
5595
|
const transport = el("div", "knobs-tl-transport");
|
|
5535
5596
|
const playBtn = el("button", "knobs-tl-play");
|
|
5536
5597
|
playBtn.type = "button";
|
|
@@ -5543,6 +5604,10 @@ function timeline(container, options) {
|
|
|
5543
5604
|
const hover = el("div", "knobs-tl-hover");
|
|
5544
5605
|
const tag = el("span", "knobs-tl-tag");
|
|
5545
5606
|
tag.setAttribute("aria-hidden", "true");
|
|
5607
|
+
const tagLabel = el("span", "knobs-tl-tag-label");
|
|
5608
|
+
const tagTime = el("span", "knobs-tl-tag-time");
|
|
5609
|
+
tag.append(tagLabel, tagTime);
|
|
5610
|
+
const segs = el("div", "knobs-tl-segs");
|
|
5546
5611
|
const head = el("div", "knobs-tl-head");
|
|
5547
5612
|
head.id = `${id}-head`;
|
|
5548
5613
|
head.setAttribute("role", "slider");
|
|
@@ -5550,11 +5615,13 @@ function timeline(container, options) {
|
|
|
5550
5615
|
head.setAttribute("aria-valuemin", "0");
|
|
5551
5616
|
head.setAttribute("aria-valuemax", String(duration));
|
|
5552
5617
|
track.append(canvas, markerLane, hover, head, tag);
|
|
5618
|
+
if (line)
|
|
5619
|
+
track.insertBefore(segs, markerLane);
|
|
5553
5620
|
root.append(transport, track);
|
|
5554
5621
|
container.append(root);
|
|
5555
5622
|
let width = 1;
|
|
5556
5623
|
let height = 1;
|
|
5557
|
-
const fitView = () => clampView({ start: 0, span: duration }, duration, { pad: 0.02 });
|
|
5624
|
+
const fitView = () => clampView({ start: 0, span: duration }, duration, { pad: line ? 0 : 0.02 });
|
|
5558
5625
|
let target = fitView();
|
|
5559
5626
|
const vStart = new Spring(target.start, INSTANT.omega, 1, 0.00001);
|
|
5560
5627
|
const vSpan = new Spring(target.span, INSTANT.omega, 1, 0.00001);
|
|
@@ -5563,13 +5630,13 @@ function timeline(container, options) {
|
|
|
5563
5630
|
const xOf = (t) => (t - vStart.x) * pxPerSec();
|
|
5564
5631
|
const tOf = (x) => vStart.x + x / pxPerSec();
|
|
5565
5632
|
const minSpan = () => Math.max(4 / fps, 0.05);
|
|
5566
|
-
const retarget = (next, motion = INSTANT) => {
|
|
5567
|
-
target = clampView(next, duration, { pad: 0.04, minSpan: minSpan() });
|
|
5633
|
+
const retarget = (next, motion = INSTANT, hand = false) => {
|
|
5634
|
+
target = clampView(next, duration, { pad: line ? 0 : 0.04, minSpan: minSpan() });
|
|
5568
5635
|
for (const s of [vStart, vSpan]) {
|
|
5569
5636
|
s.omega = motion.omega;
|
|
5570
5637
|
s.zeta = motion.zeta;
|
|
5571
5638
|
}
|
|
5572
|
-
if (reducedMotion(win) && motion === CALM) {
|
|
5639
|
+
if (hand || reducedMotion(win) && motion === CALM) {
|
|
5573
5640
|
vStart.snap(target.start);
|
|
5574
5641
|
vSpan.snap(target.span);
|
|
5575
5642
|
} else {
|
|
@@ -5595,6 +5662,8 @@ function timeline(container, options) {
|
|
|
5595
5662
|
};
|
|
5596
5663
|
let drawn = "";
|
|
5597
5664
|
const drawRuler = () => {
|
|
5665
|
+
if (line)
|
|
5666
|
+
return;
|
|
5598
5667
|
const v = view();
|
|
5599
5668
|
const key = `${v.start.toFixed(6)}:${v.span.toFixed(6)}:${width}:${height}:${units}:${fps}`;
|
|
5600
5669
|
if (key === drawn)
|
|
@@ -5682,6 +5751,8 @@ function timeline(container, options) {
|
|
|
5682
5751
|
n.title = `${m.label ? `${m.label} · ` : ""}${clock(m.time)}`;
|
|
5683
5752
|
}
|
|
5684
5753
|
placeMarkers();
|
|
5754
|
+
if (line)
|
|
5755
|
+
syncSegments();
|
|
5685
5756
|
};
|
|
5686
5757
|
const placeMarkers = () => {
|
|
5687
5758
|
for (const m of markers) {
|
|
@@ -5690,6 +5761,36 @@ function timeline(container, options) {
|
|
|
5690
5761
|
n.style.transform = `translateX(${xOf(m.time).toFixed(2)}px)`;
|
|
5691
5762
|
}
|
|
5692
5763
|
};
|
|
5764
|
+
let bounds = [];
|
|
5765
|
+
const segEls = [];
|
|
5766
|
+
const syncSegments = () => {
|
|
5767
|
+
bounds = chapterBounds(markers.map((m) => m.time), duration);
|
|
5768
|
+
while (segEls.length > bounds.length - 1)
|
|
5769
|
+
segEls.pop().el.remove();
|
|
5770
|
+
while (segEls.length < bounds.length - 1) {
|
|
5771
|
+
const n = el("i", "knobs-tl-seg");
|
|
5772
|
+
const fill = el("b", "knobs-tl-seg-fill");
|
|
5773
|
+
n.append(fill);
|
|
5774
|
+
segs.append(n);
|
|
5775
|
+
segEls.push({ el: n, fill });
|
|
5776
|
+
}
|
|
5777
|
+
placeSegments();
|
|
5778
|
+
};
|
|
5779
|
+
const placeSegments = () => {
|
|
5780
|
+
if (!line)
|
|
5781
|
+
return;
|
|
5782
|
+
const last = segEls.length - 1;
|
|
5783
|
+
segEls.forEach((s, i) => {
|
|
5784
|
+
const a = bounds[i];
|
|
5785
|
+
const b = bounds[i + 1];
|
|
5786
|
+
const x0 = xOf(a) + (i > 0 ? SEG_GAP / 2 : 0);
|
|
5787
|
+
const x1 = xOf(b) - (i < last ? SEG_GAP / 2 : 0);
|
|
5788
|
+
s.el.style.transform = `translateX(${x0.toFixed(2)}px)`;
|
|
5789
|
+
s.el.style.width = `${Math.max(0, x1 - x0).toFixed(2)}px`;
|
|
5790
|
+
const k = Math.min(1, Math.max(0, (shownHead - a) / Math.max(0.000000001, b - a)));
|
|
5791
|
+
s.fill.style.transform = `scaleX(${k.toFixed(4)})`;
|
|
5792
|
+
});
|
|
5793
|
+
};
|
|
5693
5794
|
const syncRanges = () => {
|
|
5694
5795
|
const keep = new Set(ranges.map((r) => r.id));
|
|
5695
5796
|
for (const [k, n] of rangeEls)
|
|
@@ -5706,7 +5807,7 @@ function timeline(container, options) {
|
|
|
5706
5807
|
b.dataset.edge = "end";
|
|
5707
5808
|
const label = el("span", "knobs-tl-rlabel");
|
|
5708
5809
|
n.append(label, a, b);
|
|
5709
|
-
track.insertBefore(n, markerLane);
|
|
5810
|
+
track.insertBefore(n, line ? segs : markerLane);
|
|
5710
5811
|
rangeEls.set(r.id, n);
|
|
5711
5812
|
if (built)
|
|
5712
5813
|
enter(n);
|
|
@@ -5758,6 +5859,7 @@ function timeline(container, options) {
|
|
|
5758
5859
|
let currentId = null;
|
|
5759
5860
|
const renderHead = () => {
|
|
5760
5861
|
head.style.transform = `translateX(${xOf(shownHead).toFixed(2)}px)`;
|
|
5862
|
+
placeSegments();
|
|
5761
5863
|
head.setAttribute("aria-valuenow", time.toFixed(3));
|
|
5762
5864
|
head.setAttribute("aria-valuetext", `${time.toFixed(2)} seconds, frame ${toFrame(time, fps)}`);
|
|
5763
5865
|
const cur = currentMarker(markers, time, fps)?.id ?? null;
|
|
@@ -6021,14 +6123,25 @@ function timeline(container, options) {
|
|
|
6021
6123
|
setTime(s.t, "scrub", s.shown, press);
|
|
6022
6124
|
root.toggleAttribute("data-stuck", s.stop !== null);
|
|
6023
6125
|
};
|
|
6126
|
+
let pointerX = null;
|
|
6127
|
+
const zoomAtHand = (factor) => zoom(factor, pointerX === null ? time : target.start + pointerX / width * target.span);
|
|
6024
6128
|
const showTag = (x) => {
|
|
6025
6129
|
const at = Math.min(duration, Math.max(0, tOf(x)));
|
|
6026
|
-
const text = `${at.toFixed(2)} s`;
|
|
6027
|
-
if (
|
|
6028
|
-
|
|
6130
|
+
const text = line ? minutes(at) : `${at.toFixed(2)} s`;
|
|
6131
|
+
if (tagTime.textContent !== text)
|
|
6132
|
+
tagTime.textContent = text;
|
|
6133
|
+
const label = line ? currentMarker(markers, at, fps)?.label ?? "" : "";
|
|
6134
|
+
if (tagLabel.textContent !== label)
|
|
6135
|
+
tagLabel.textContent = label;
|
|
6029
6136
|
const cx = Math.min(width, Math.max(0, x));
|
|
6030
|
-
|
|
6031
|
-
|
|
6137
|
+
if (line) {
|
|
6138
|
+
const w = tag.offsetWidth;
|
|
6139
|
+
const left = Math.min(Math.max(0, width - w), Math.max(0, cx - w / 2));
|
|
6140
|
+
tag.style.transform = `translateX(${left.toFixed(1)}px)`;
|
|
6141
|
+
} else {
|
|
6142
|
+
const flip = cx > width - tag.offsetWidth - 12;
|
|
6143
|
+
tag.style.transform = `translateX(${cx.toFixed(1)}px) translateX(${flip ? "calc(-100% - 4px)" : "4px"})`;
|
|
6144
|
+
}
|
|
6032
6145
|
tag.dataset.on = "";
|
|
6033
6146
|
};
|
|
6034
6147
|
const hideTag = () => delete tag.dataset.on;
|
|
@@ -6089,6 +6202,7 @@ function timeline(container, options) {
|
|
|
6089
6202
|
});
|
|
6090
6203
|
track.addEventListener("pointermove", (e) => {
|
|
6091
6204
|
const x = localX(e);
|
|
6205
|
+
pointerX = x;
|
|
6092
6206
|
hover.style.transform = `translateX(${x.toFixed(1)}px)`;
|
|
6093
6207
|
if (!drag || drag.kind === "scrub")
|
|
6094
6208
|
showTag(x);
|
|
@@ -6162,6 +6276,7 @@ function timeline(container, options) {
|
|
|
6162
6276
|
track.addEventListener("pointercancel", endDrag);
|
|
6163
6277
|
track.addEventListener("lostpointercapture", endDrag);
|
|
6164
6278
|
track.addEventListener("pointerleave", () => {
|
|
6279
|
+
pointerX = null;
|
|
6165
6280
|
if (drag)
|
|
6166
6281
|
return;
|
|
6167
6282
|
hover.style.transform = "translateX(-100px)";
|
|
@@ -6169,7 +6284,7 @@ function timeline(container, options) {
|
|
|
6169
6284
|
});
|
|
6170
6285
|
track.addEventListener("dblclick", (e) => {
|
|
6171
6286
|
if (lane)
|
|
6172
|
-
return;
|
|
6287
|
+
return fit();
|
|
6173
6288
|
const markerEl = e.target.closest(".knobs-tl-marker");
|
|
6174
6289
|
if (markerEl)
|
|
6175
6290
|
return rename(markerEl.dataset.id);
|
|
@@ -6180,18 +6295,40 @@ function timeline(container, options) {
|
|
|
6180
6295
|
rename(m.id);
|
|
6181
6296
|
});
|
|
6182
6297
|
track.addEventListener("wheel", (e) => {
|
|
6183
|
-
|
|
6298
|
+
const zooming = e.ctrlKey || e.metaKey;
|
|
6299
|
+
if (zooming && pinching)
|
|
6300
|
+
return e.preventDefault();
|
|
6301
|
+
const sideways = Math.abs(e.deltaX) > Math.abs(e.deltaY);
|
|
6302
|
+
if (lane && !zooming && !sideways)
|
|
6184
6303
|
return;
|
|
6185
6304
|
e.preventDefault();
|
|
6186
6305
|
const scale = e.deltaMode === 1 ? 16 : e.deltaMode === 2 ? height : 1;
|
|
6187
|
-
if (
|
|
6306
|
+
if (zooming) {
|
|
6188
6307
|
const at = target.start + localX(e) / width * target.span;
|
|
6189
|
-
|
|
6308
|
+
const k = Math.exp(-e.deltaY * scale * (e.ctrlKey ? 0.01 : 0.002));
|
|
6309
|
+
retarget(zoomView(target, k, at), INSTANT, lane);
|
|
6190
6310
|
} else {
|
|
6191
|
-
const d = (
|
|
6192
|
-
retarget({ ...target, start: target.start + d / width * target.span });
|
|
6311
|
+
const d = (sideways ? e.deltaX : e.deltaY) * scale;
|
|
6312
|
+
retarget({ ...target, start: target.start + d / width * target.span }, INSTANT, lane);
|
|
6193
6313
|
}
|
|
6194
6314
|
}, { passive: false });
|
|
6315
|
+
let pinchScale = 1;
|
|
6316
|
+
let pinching = false;
|
|
6317
|
+
const onGesture = (e) => {
|
|
6318
|
+
const g = e;
|
|
6319
|
+
e.preventDefault();
|
|
6320
|
+
if (e.type === "gesturestart")
|
|
6321
|
+
pinchScale = 1;
|
|
6322
|
+
pinching = e.type !== "gestureend";
|
|
6323
|
+
if (e.type !== "gesturechange")
|
|
6324
|
+
return;
|
|
6325
|
+
const s = g.scale ?? 1;
|
|
6326
|
+
const x = g.clientX !== undefined ? localX({ clientX: g.clientX }) : width / 2;
|
|
6327
|
+
retarget(zoomView(target, s / pinchScale, target.start + x / width * target.span), INSTANT, true);
|
|
6328
|
+
pinchScale = s;
|
|
6329
|
+
};
|
|
6330
|
+
for (const type of ["gesturestart", "gesturechange", "gestureend"])
|
|
6331
|
+
track.addEventListener(type, onGesture);
|
|
6195
6332
|
root.addEventListener("keydown", (e) => {
|
|
6196
6333
|
if (e.target !== root || e.metaKey || e.ctrlKey) {
|
|
6197
6334
|
if ((e.metaKey || e.ctrlKey) && (e.key === "=" || e.key === "-" || e.key === "0") && e.target === root) {
|
|
@@ -6199,7 +6336,7 @@ function timeline(container, options) {
|
|
|
6199
6336
|
if (e.key === "0")
|
|
6200
6337
|
fit();
|
|
6201
6338
|
else
|
|
6202
|
-
|
|
6339
|
+
zoomAtHand(e.key === "=" ? 1.5 : 1 / 1.5);
|
|
6203
6340
|
}
|
|
6204
6341
|
return;
|
|
6205
6342
|
}
|
|
@@ -6281,18 +6418,12 @@ function timeline(container, options) {
|
|
|
6281
6418
|
return;
|
|
6282
6419
|
case "=":
|
|
6283
6420
|
case "+":
|
|
6284
|
-
if (lane)
|
|
6285
|
-
return;
|
|
6286
6421
|
claim();
|
|
6287
|
-
return
|
|
6422
|
+
return zoomAtHand(1.5);
|
|
6288
6423
|
case "-":
|
|
6289
|
-
if (lane)
|
|
6290
|
-
return;
|
|
6291
6424
|
claim();
|
|
6292
|
-
return
|
|
6425
|
+
return zoomAtHand(1 / 1.5);
|
|
6293
6426
|
case "0":
|
|
6294
|
-
if (lane)
|
|
6295
|
-
return;
|
|
6296
6427
|
claim();
|
|
6297
6428
|
return fit();
|
|
6298
6429
|
}
|
|
@@ -6508,7 +6639,7 @@ function timeline(container, options) {
|
|
|
6508
6639
|
};
|
|
6509
6640
|
return api;
|
|
6510
6641
|
}
|
|
6511
|
-
var RULER = 22, INSTANT, CALM, GLIDE, uid2 = 0, 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>', 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>';
|
|
6642
|
+
var RULER = 22, SEG_GAP = 3, INSTANT, CALM, GLIDE, uid2 = 0, 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>', 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>';
|
|
6512
6643
|
var init_timeline = __esm(() => {
|
|
6513
6644
|
init_rgba();
|
|
6514
6645
|
init_motion();
|
|
@@ -6580,15 +6711,28 @@ function compareLayout(box, before, after) {
|
|
|
6580
6711
|
const shape = after.w > 0 && after.h > 0 ? after : before;
|
|
6581
6712
|
const pair = letterbox({ w: shape.w * 2, h: shape.h }, box);
|
|
6582
6713
|
const half = pair.w / 2;
|
|
6714
|
+
const fused = { x: pair.x + half / 2, y: pair.y, w: half, h: pair.h };
|
|
6583
6715
|
return {
|
|
6584
6716
|
side: {
|
|
6585
6717
|
before: letterbox(before, { x: pair.x, y: pair.y, w: half, h: pair.h }),
|
|
6586
6718
|
after: letterbox(after, { x: pair.x + half, y: pair.y, w: half, h: pair.h }),
|
|
6587
6719
|
split: pair.x + half
|
|
6588
6720
|
},
|
|
6589
|
-
overlay: { before: letterbox(before,
|
|
6721
|
+
overlay: { before: letterbox(before, fused), after: letterbox(after, fused) }
|
|
6590
6722
|
};
|
|
6591
6723
|
}
|
|
6724
|
+
function fuseFrame(L, before, after, k, wipe) {
|
|
6725
|
+
const t = Math.min(1, Math.max(0, k));
|
|
6726
|
+
const o = L.overlay;
|
|
6727
|
+
const left = Math.min(o.before.x, o.after.x);
|
|
6728
|
+
const line = wipeX({ x: left, y: 0, w: Math.max(o.before.x + o.before.w, o.after.x + o.after.w) - left, h: 0 }, wipe);
|
|
6729
|
+
const cut = L.side.split + (line - L.side.split) * t;
|
|
6730
|
+
const contact = Math.max(0, Math.min(cut - after.x, before.x + before.w - cut));
|
|
6731
|
+
const feather = Math.min(contact, 1 + FUSE.feather * Math.sin(Math.PI * t));
|
|
6732
|
+
const overlap = before.x + before.w - after.x;
|
|
6733
|
+
const seam = t > 0.001 && overlap >= 0 ? Math.min(1, Math.max(0, overlap / FUSE.seamFade)) * Math.min(1, t * 4) : 0;
|
|
6734
|
+
return { cut, feather, seam, blur: t < 0.995 ? FUSE.blur * (1 - t) : 0 };
|
|
6735
|
+
}
|
|
6592
6736
|
function clampZoom(r) {
|
|
6593
6737
|
const w = Math.min(1, Math.max(1 / ZOOM_MAX, r.w));
|
|
6594
6738
|
const u = Math.min(1 - w, Math.max(0, r.u));
|
|
@@ -6674,7 +6818,15 @@ function stripPeaks(diffs, o = {}) {
|
|
|
6674
6818
|
});
|
|
6675
6819
|
return out;
|
|
6676
6820
|
}
|
|
6677
|
-
var ALL_MODES, MODE_KEYS, modeKey = (mode) => MODE_KEYS[mode].toUpperCase(), VIDEO, frameOf = (t, fps) => Math.max(0, Math.floor(t * fps + 0.0001)), lastFrame = (duration, fps) => Math.max(0, Math.ceil(duration * fps - 0.0001) - 1), seekTime = (t, fps, duration) => frameMidpoint(frameOf(t, fps), fps, duration), sameFrame = (a, b, fps) => frameOf(a, fps) === frameOf(b, fps), SEEK_IDLE,
|
|
6821
|
+
var ALL_MODES, MODE_KEYS, modeKey = (mode) => MODE_KEYS[mode].toUpperCase(), VIDEO, frameOf = (t, fps) => Math.max(0, Math.floor(t * fps + 0.0001)), lastFrame = (duration, fps) => Math.max(0, Math.ceil(duration * fps - 0.0001) - 1), seekTime = (t, fps, duration) => frameMidpoint(frameOf(t, fps), fps, duration), sameFrame = (a, b, fps) => frameOf(a, fps) === frameOf(b, fps), SEEK_IDLE, FUSE, labelFold = (fold, mark, blur = 3) => {
|
|
6822
|
+
const f = Math.min(1, Math.max(0, fold));
|
|
6823
|
+
const open = 1 - f;
|
|
6824
|
+
return {
|
|
6825
|
+
width: mark * open,
|
|
6826
|
+
opacity: Math.min(1, Math.max(0, (open - 0.2) / 0.8)),
|
|
6827
|
+
blur: blur * Math.min(1, Math.max(0, (f - 0.25) / 0.75))
|
|
6828
|
+
};
|
|
6829
|
+
}, splitHeight = (w, media) => media.w > 0 && media.h > 0 ? w / 2 * (media.h / media.w) : w / 2 * (9 / 16), ZOOM_FIT, ZOOM_MAX = 16, panBy = (r, dx, dy, base) => clampZoom({
|
|
6678
6830
|
u: r.u - dx / Math.max(1, base.w) * r.w,
|
|
6679
6831
|
v: r.v - dy / Math.max(1, base.h) * r.h,
|
|
6680
6832
|
w: r.w
|
|
@@ -6697,6 +6849,7 @@ var init_compare_model = __esm(() => {
|
|
|
6697
6849
|
MODE_KEYS = { side: "s", wipe: "w", difference: "d" };
|
|
6698
6850
|
VIDEO = /\.(mp4|m4v|mov|webm|ogv|mkv)$/i;
|
|
6699
6851
|
SEEK_IDLE = { seeking: false, pending: null };
|
|
6852
|
+
FUSE = { feather: 24, blur: 4, seamFade: 48 };
|
|
6700
6853
|
ZOOM_FIT = { u: 0, v: 0, w: 1, h: 1 };
|
|
6701
6854
|
});
|
|
6702
6855
|
|
|
@@ -7591,11 +7744,6 @@ fn pm(c: vec4f) -> vec4f { return vec4f(c.rgb * c.a, c.a); }
|
|
|
7591
7744
|
|
|
7592
7745
|
fn composite(mode: u32, p: vec2f, a: vec4f, b: vec4f) -> vec4f {
|
|
7593
7746
|
switch mode {
|
|
7594
|
-
case 1u: {
|
|
7595
|
-
let s = max(u.wipe.y, 0.5);
|
|
7596
|
-
let m = smoothstep(u.wipe.x - s, u.wipe.x + s, p.x);
|
|
7597
|
-
return pm(vec4f(mix(a.rgb, b.rgb, m), mix(a.a, b.a, m)));
|
|
7598
|
-
}
|
|
7599
7747
|
case 2u: {
|
|
7600
7748
|
let d = abs(a.rgb - b.rgb);
|
|
7601
7749
|
let h = smoothstep(u.diff.x, u.diff.x + 0.06, max(d.r, max(d.g, d.b)) * u.diff.y) * min(a.a, b.a);
|
|
@@ -7604,8 +7752,10 @@ fn composite(mode: u32, p: vec2f, a: vec4f, b: vec4f) -> vec4f {
|
|
|
7604
7752
|
}
|
|
7605
7753
|
default: {}
|
|
7606
7754
|
}
|
|
7607
|
-
// Side by side
|
|
7608
|
-
|
|
7755
|
+
// Side by side, fusing and wiping: Before over After, cut at the seam with its feather.
|
|
7756
|
+
let s = max(u.wipe.y, 0.5);
|
|
7757
|
+
let ka = a.a * (1.0 - smoothstep(u.wipe.x - s, u.wipe.x + s, p.x));
|
|
7758
|
+
return vec4f(a.rgb * ka + b.rgb * b.a * (1.0 - ka), ka + b.a * (1.0 - ka));
|
|
7609
7759
|
}
|
|
7610
7760
|
|
|
7611
7761
|
@fragment fn fs(@builtin(position) q: vec4f) -> @location(0) vec4f {
|
|
@@ -8007,6 +8157,196 @@ var init_compare_strip = __esm(() => {
|
|
|
8007
8157
|
DIFF = { w: 32, h: 18 };
|
|
8008
8158
|
});
|
|
8009
8159
|
|
|
8160
|
+
// src/ui/player.ts
|
|
8161
|
+
function player(container, options) {
|
|
8162
|
+
const doc = container.ownerDocument;
|
|
8163
|
+
const el = (tag, cls) => {
|
|
8164
|
+
const n = doc.createElement(tag);
|
|
8165
|
+
n.className = cls;
|
|
8166
|
+
return n;
|
|
8167
|
+
};
|
|
8168
|
+
const look = {
|
|
8169
|
+
...options.preset ? { preset: options.preset } : {},
|
|
8170
|
+
...options.theme ? { theme: options.theme } : {},
|
|
8171
|
+
...options.appearance ? { appearance: options.appearance } : {}
|
|
8172
|
+
};
|
|
8173
|
+
const root = el("div", "knobs-player");
|
|
8174
|
+
root.setAttribute("role", "group");
|
|
8175
|
+
root.setAttribute("aria-label", options.label ?? "Video");
|
|
8176
|
+
applyLook2(root, options);
|
|
8177
|
+
const frame = el("div", "knobs-player-frame");
|
|
8178
|
+
frame.style.setProperty("--kp-aspect", String(options.aspect ?? 16 / 9));
|
|
8179
|
+
const v = el("video", "knobs-player-media");
|
|
8180
|
+
v.muted = options.muted ?? true;
|
|
8181
|
+
v.defaultMuted = v.muted;
|
|
8182
|
+
v.playsInline = true;
|
|
8183
|
+
v.preload = "metadata";
|
|
8184
|
+
v.setAttribute("playsinline", "");
|
|
8185
|
+
if (options.poster)
|
|
8186
|
+
v.poster = options.poster;
|
|
8187
|
+
if (options.captions) {
|
|
8188
|
+
const track = doc.createElement("track");
|
|
8189
|
+
track.kind = "captions";
|
|
8190
|
+
track.label = "Captions";
|
|
8191
|
+
track.srclang = doc.documentElement.lang || "en";
|
|
8192
|
+
track.default = true;
|
|
8193
|
+
track.src = options.captions;
|
|
8194
|
+
v.append(track);
|
|
8195
|
+
}
|
|
8196
|
+
const glyph = playGlyph(doc);
|
|
8197
|
+
frame.append(v, glyph.element);
|
|
8198
|
+
const host = el("div", "knobs-player-line");
|
|
8199
|
+
root.append(frame, host);
|
|
8200
|
+
container.append(root);
|
|
8201
|
+
const markers = (options.chapters ?? []).map((c, i) => ({
|
|
8202
|
+
id: c.id ?? `c${i}`,
|
|
8203
|
+
time: c.start,
|
|
8204
|
+
label: c.label
|
|
8205
|
+
}));
|
|
8206
|
+
const tl = timeline(host, {
|
|
8207
|
+
...look,
|
|
8208
|
+
variant: "line",
|
|
8209
|
+
duration: 1,
|
|
8210
|
+
fps: options.fps ?? 30,
|
|
8211
|
+
markers,
|
|
8212
|
+
repeat: options.loop ?? true,
|
|
8213
|
+
arrows: { step: 5, shift: 10 },
|
|
8214
|
+
label: options.label ? `${options.label} time` : "Video time",
|
|
8215
|
+
...options.hints === false ? { hints: false } : {}
|
|
8216
|
+
});
|
|
8217
|
+
const onFrame = () => root.setAttribute("data-ready", "");
|
|
8218
|
+
let pending = null;
|
|
8219
|
+
const seekVideo = (t) => {
|
|
8220
|
+
if (v.readyState < 1)
|
|
8221
|
+
return;
|
|
8222
|
+
if (v.seeking)
|
|
8223
|
+
pending = t;
|
|
8224
|
+
else
|
|
8225
|
+
v.currentTime = t;
|
|
8226
|
+
};
|
|
8227
|
+
let reported = 0;
|
|
8228
|
+
const onSeeked = () => {
|
|
8229
|
+
if (pending !== null) {
|
|
8230
|
+
const t = pending;
|
|
8231
|
+
pending = null;
|
|
8232
|
+
v.currentTime = t;
|
|
8233
|
+
return;
|
|
8234
|
+
}
|
|
8235
|
+
if (!v.paused || tl.playing || Math.abs(v.currentTime - reported) < 0.1)
|
|
8236
|
+
return;
|
|
8237
|
+
reported = v.currentTime;
|
|
8238
|
+
options.onSeek?.(reported);
|
|
8239
|
+
};
|
|
8240
|
+
const onMeta = () => {
|
|
8241
|
+
onFrame();
|
|
8242
|
+
if (v.videoWidth)
|
|
8243
|
+
frame.style.setProperty("--kp-aspect", String(v.videoWidth / v.videoHeight));
|
|
8244
|
+
if (Number.isFinite(v.duration))
|
|
8245
|
+
tl.update({ duration: v.duration });
|
|
8246
|
+
};
|
|
8247
|
+
v.addEventListener("loadedmetadata", onMeta);
|
|
8248
|
+
v.addEventListener("loadeddata", onFrame);
|
|
8249
|
+
v.addEventListener("seeked", onSeeked);
|
|
8250
|
+
const unlisten = tl.listen({
|
|
8251
|
+
onSeek(t, source) {
|
|
8252
|
+
if (source === "play") {
|
|
8253
|
+
if (Math.abs(v.currentTime - t) > 0.25)
|
|
8254
|
+
v.currentTime = t;
|
|
8255
|
+
return;
|
|
8256
|
+
}
|
|
8257
|
+
if (Math.abs(v.currentTime - t) < 1 / (options.fps ?? 30))
|
|
8258
|
+
return;
|
|
8259
|
+
seekVideo(t);
|
|
8260
|
+
},
|
|
8261
|
+
onPlay(on) {
|
|
8262
|
+
if (on)
|
|
8263
|
+
v.play().catch(() => tl.pause());
|
|
8264
|
+
else {
|
|
8265
|
+
v.pause();
|
|
8266
|
+
reported = tl.time;
|
|
8267
|
+
}
|
|
8268
|
+
root.toggleAttribute("data-playing", on);
|
|
8269
|
+
options.onPlay?.(on, tl.time);
|
|
8270
|
+
},
|
|
8271
|
+
onScrub(on) {
|
|
8272
|
+
if (on)
|
|
8273
|
+
v.pause();
|
|
8274
|
+
}
|
|
8275
|
+
});
|
|
8276
|
+
tl.drive(() => {
|
|
8277
|
+
if (v.readyState < 1)
|
|
8278
|
+
return Number.NaN;
|
|
8279
|
+
if (v.seeking)
|
|
8280
|
+
return tl.time;
|
|
8281
|
+
return v.ended ? Number.POSITIVE_INFINITY : v.currentTime;
|
|
8282
|
+
});
|
|
8283
|
+
const onClick = (e) => {
|
|
8284
|
+
if (e.button !== 0)
|
|
8285
|
+
return;
|
|
8286
|
+
tl.toggle();
|
|
8287
|
+
glyph.flash(tl.playing);
|
|
8288
|
+
tl.focus();
|
|
8289
|
+
};
|
|
8290
|
+
frame.addEventListener("click", onClick);
|
|
8291
|
+
v.src = options.src;
|
|
8292
|
+
const api = {
|
|
8293
|
+
element: root,
|
|
8294
|
+
video: v,
|
|
8295
|
+
timeline: tl,
|
|
8296
|
+
get time() {
|
|
8297
|
+
return tl.time;
|
|
8298
|
+
},
|
|
8299
|
+
get playing() {
|
|
8300
|
+
return tl.playing;
|
|
8301
|
+
},
|
|
8302
|
+
get duration() {
|
|
8303
|
+
return Number.isFinite(v.duration) ? v.duration : 0;
|
|
8304
|
+
},
|
|
8305
|
+
play: () => tl.play(),
|
|
8306
|
+
pause: () => tl.pause(),
|
|
8307
|
+
toggle: () => tl.toggle(),
|
|
8308
|
+
seek: (t, o) => tl.seek(t, o?.glide ? { glide: true } : undefined),
|
|
8309
|
+
seekBy: (d) => tl.seek(Math.min(tl.duration, Math.max(0, tl.time + d)), { glide: true }),
|
|
8310
|
+
focus: () => tl.focus(),
|
|
8311
|
+
dispose() {
|
|
8312
|
+
unlisten();
|
|
8313
|
+
glyph.dispose();
|
|
8314
|
+
tl.drive(null);
|
|
8315
|
+
tl.dispose();
|
|
8316
|
+
v.pause();
|
|
8317
|
+
v.removeEventListener("loadedmetadata", onMeta);
|
|
8318
|
+
v.removeEventListener("loadeddata", onFrame);
|
|
8319
|
+
v.removeEventListener("seeked", onSeeked);
|
|
8320
|
+
v.removeAttribute("src");
|
|
8321
|
+
v.load();
|
|
8322
|
+
root.remove();
|
|
8323
|
+
}
|
|
8324
|
+
};
|
|
8325
|
+
return api;
|
|
8326
|
+
}
|
|
8327
|
+
function playGlyph(doc) {
|
|
8328
|
+
const element = doc.createElement("span");
|
|
8329
|
+
element.className = "knobs-player-glyph";
|
|
8330
|
+
element.setAttribute("aria-hidden", "true");
|
|
8331
|
+
let timer = 0;
|
|
8332
|
+
const win = doc.defaultView;
|
|
8333
|
+
return {
|
|
8334
|
+
element,
|
|
8335
|
+
flash(playing) {
|
|
8336
|
+
element.innerHTML = playing ? PLAY : PAUSE;
|
|
8337
|
+
element.setAttribute("data-flash", "");
|
|
8338
|
+
win.clearTimeout(timer);
|
|
8339
|
+
timer = win.setTimeout(() => element.removeAttribute("data-flash"), FLASH_MS);
|
|
8340
|
+
},
|
|
8341
|
+
dispose: () => win.clearTimeout(timer)
|
|
8342
|
+
};
|
|
8343
|
+
}
|
|
8344
|
+
var FLASH_MS = 450;
|
|
8345
|
+
var init_player = __esm(() => {
|
|
8346
|
+
init_look();
|
|
8347
|
+
init_timeline();
|
|
8348
|
+
});
|
|
8349
|
+
|
|
8010
8350
|
// src/ui/compare.ts
|
|
8011
8351
|
function compare(container, options) {
|
|
8012
8352
|
const doc = container.ownerDocument;
|
|
@@ -8114,6 +8454,8 @@ function compare(container, options) {
|
|
|
8114
8454
|
if (name.textContent !== text)
|
|
8115
8455
|
name.textContent = text;
|
|
8116
8456
|
};
|
|
8457
|
+
const seam = el("div", "knobs-cmp-seam");
|
|
8458
|
+
seam.setAttribute("aria-hidden", "true");
|
|
8117
8459
|
const handle = el("div", "knobs-cmp-handle");
|
|
8118
8460
|
handle.setAttribute("role", "slider");
|
|
8119
8461
|
handle.setAttribute("aria-label", "Wipe");
|
|
@@ -8123,7 +8465,8 @@ function compare(container, options) {
|
|
|
8123
8465
|
grip.setAttribute("aria-hidden", "true");
|
|
8124
8466
|
handle.append(grip);
|
|
8125
8467
|
const note = el("div", "knobs-cmp-note");
|
|
8126
|
-
|
|
8468
|
+
const glyph = playGlyph(doc);
|
|
8469
|
+
stage.append(canvas, split, seam, labels[0], labels[1], handle, zoomPill, note, glyph.element);
|
|
8127
8470
|
root.append(bar, stage);
|
|
8128
8471
|
const wantsStrip = options.strip === true;
|
|
8129
8472
|
let tl = bound;
|
|
@@ -8138,7 +8481,7 @@ function compare(container, options) {
|
|
|
8138
8481
|
root.append(host);
|
|
8139
8482
|
tl = timeline(host, {
|
|
8140
8483
|
...look,
|
|
8141
|
-
variant: "
|
|
8484
|
+
variant: "line",
|
|
8142
8485
|
duration: 1,
|
|
8143
8486
|
fps: fps(),
|
|
8144
8487
|
arrows: { step: 0.5, shift: 2 },
|
|
@@ -8159,12 +8502,13 @@ function compare(container, options) {
|
|
|
8159
8502
|
let toMode = mode;
|
|
8160
8503
|
const wipeLine = new Spring(wipe, WIPE_CATCH.omega, WIPE_CATCH.zeta, 0.0001);
|
|
8161
8504
|
let wipeCatching = false;
|
|
8162
|
-
|
|
8505
|
+
const gripAt = new Spring(0.5, LAYOUT.omega, LAYOUT.zeta, 0.0001);
|
|
8163
8506
|
let loupeHeld = false;
|
|
8164
8507
|
let pointer = null;
|
|
8165
8508
|
let drag = null;
|
|
8166
8509
|
const touches = new Map;
|
|
8167
8510
|
let pinch = null;
|
|
8511
|
+
let press = null;
|
|
8168
8512
|
const zu = new Spring(0, ZOOM.omega, ZOOM.zeta, 0.00001);
|
|
8169
8513
|
const zv = new Spring(0, ZOOM.omega, ZOOM.zeta, 0.00001);
|
|
8170
8514
|
const zw = new Spring(1, ZOOM.omega, ZOOM.zeta, 0.00001);
|
|
@@ -8451,6 +8795,8 @@ function compare(container, options) {
|
|
|
8451
8795
|
for (const s of sides) {
|
|
8452
8796
|
const st = s.el.style;
|
|
8453
8797
|
st.clipPath = "";
|
|
8798
|
+
st.maskImage = "";
|
|
8799
|
+
st.setProperty("-webkit-mask-image", "");
|
|
8454
8800
|
st.mixBlendMode = "";
|
|
8455
8801
|
st.opacity = "";
|
|
8456
8802
|
s.shown = null;
|
|
@@ -8677,43 +9023,65 @@ function compare(container, options) {
|
|
|
8677
9023
|
viewSent = key;
|
|
8678
9024
|
options.onView({ zoom: clampZoom(z), loop: l });
|
|
8679
9025
|
};
|
|
8680
|
-
const
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
return [at.before, at.after];
|
|
8684
|
-
}
|
|
8685
|
-
const ax = r.after.x + INSET + (r.after.w - labelW[1] - INSET * 2) * lay.x;
|
|
8686
|
-
return [
|
|
8687
|
-
{ x: r.before.x + INSET, opacity: 1 },
|
|
8688
|
-
{ x: ax, opacity: 1 }
|
|
8689
|
-
];
|
|
9026
|
+
const differenceWeight = () => {
|
|
9027
|
+
const f = Math.min(1, Math.max(0, fade.x));
|
|
9028
|
+
return (fromMode === "difference" ? 1 - f : 0) + (toMode === "difference" ? f : 0);
|
|
8690
9029
|
};
|
|
8691
9030
|
const placeGrip = (wr) => {
|
|
8692
|
-
const gy = gripTop(
|
|
9031
|
+
const gy = gripTop(wr.y + gripAt.x * wr.h, wr, GRIP.h) - wr.y;
|
|
8693
9032
|
grip.style.transform = `translate3d(0,${gy.toFixed(2)}px,0)`;
|
|
8694
9033
|
};
|
|
9034
|
+
let folded = 0;
|
|
8695
9035
|
const draw = () => {
|
|
8696
9036
|
dirty = false;
|
|
9037
|
+
if (!root.hasAttribute("data-ready") && sides.every((x) => x.shown || x.failed))
|
|
9038
|
+
root.setAttribute("data-ready", "");
|
|
8697
9039
|
const r = layoutNow();
|
|
8698
9040
|
const wr = wipeRect(r.L.overlay);
|
|
8699
|
-
const
|
|
8700
|
-
const
|
|
8701
|
-
const
|
|
8702
|
-
const
|
|
8703
|
-
|
|
9041
|
+
const k = Math.min(1, Math.max(0, lay.x));
|
|
9042
|
+
const fu = fuseFrame(r.L, r.before, r.after, k, wipeLine.x);
|
|
9043
|
+
const dw = differenceWeight();
|
|
9044
|
+
const fusing = 1 - dw;
|
|
9045
|
+
folded = k * fusing;
|
|
9046
|
+
const fold = labelFold(folded, MARK.w);
|
|
9047
|
+
const w = labelW.map((x) => x - (MARK.w - fold.width) - MARK.gap * folded);
|
|
9048
|
+
const ride = seamLabels(fu.cut, r.before.x, r.after.x + r.after.w, w[0], w[1], INSET);
|
|
9049
|
+
const apart = [r.before.x + INSET, r.after.x + INSET];
|
|
9050
|
+
const corner = [r.before.x + INSET, r.after.x + r.after.w - labelW[1] - INSET];
|
|
9051
|
+
const rides = [ride.before, ride.after];
|
|
8704
9052
|
const ys = [r.before.y + INSET, r.after.y + INSET];
|
|
8705
9053
|
for (const i of [0, 1]) {
|
|
8706
|
-
const
|
|
8707
|
-
|
|
8708
|
-
|
|
9054
|
+
const fused = apart[i] + (rides[i].x - apart[i]) * k;
|
|
9055
|
+
const x = fused + (corner[i] - fused) * dw;
|
|
9056
|
+
const opacity = (1 - k + k * rides[i].opacity) * fusing + dw;
|
|
9057
|
+
const l = labels[i];
|
|
9058
|
+
l.style.transform = `translate(${x.toFixed(2)}px,${ys[i].toFixed(2)}px)`;
|
|
9059
|
+
l.style.opacity = opacity.toFixed(3);
|
|
9060
|
+
const mark = l.firstElementChild;
|
|
9061
|
+
if (mark) {
|
|
9062
|
+
mark.style.width = `${fold.width.toFixed(2)}px`;
|
|
9063
|
+
mark.style.marginRight = `${(-MARK.gap * folded).toFixed(2)}px`;
|
|
9064
|
+
mark.style.opacity = fold.opacity.toFixed(3);
|
|
9065
|
+
mark.style.filter = fold.blur > 0.05 ? `blur(${fold.blur.toFixed(2)}px)` : "";
|
|
9066
|
+
}
|
|
8709
9067
|
}
|
|
8710
9068
|
const top = Math.min(r.L.side.before.y, r.L.side.after.y);
|
|
8711
9069
|
split.style.transform = `translate(${r.L.side.split.toFixed(2)}px,${top.toFixed(2)}px)`;
|
|
8712
9070
|
split.style.height = `${Math.max(0, r.L.side.after.h).toFixed(2)}px`;
|
|
8713
9071
|
split.style.opacity = String(Math.max(0, 1 - k * 2));
|
|
9072
|
+
const seamOn = fu.seam * fusing;
|
|
9073
|
+
const blur = fu.blur > 0.01 ? `blur(${fu.blur.toFixed(2)}px)` : "none";
|
|
9074
|
+
seam.style.transform = `translate(${fu.cut.toFixed(2)}px,${wr.y.toFixed(2)}px) translateX(-50%)`;
|
|
9075
|
+
seam.style.width = `${(8 + 2 * fu.feather).toFixed(2)}px`;
|
|
9076
|
+
seam.style.height = `${Math.max(0, wr.h).toFixed(2)}px`;
|
|
9077
|
+
seam.style.opacity = (blur === "none" ? 0 : seamOn).toFixed(3);
|
|
9078
|
+
seam.style.backdropFilter = blur;
|
|
9079
|
+
seam.style.setProperty("-webkit-backdrop-filter", blur);
|
|
8714
9080
|
handle.toggleAttribute("data-on", mode === "wipe");
|
|
8715
|
-
handle.style.transform = `translate(${
|
|
9081
|
+
handle.style.transform = `translate(${fu.cut.toFixed(2)}px,${wr.y.toFixed(2)}px)`;
|
|
8716
9082
|
handle.style.height = `${Math.max(0, wr.h).toFixed(2)}px`;
|
|
9083
|
+
handle.style.opacity = seamOn.toFixed(3);
|
|
9084
|
+
handle.style.filter = blur;
|
|
8717
9085
|
placeGrip(wr);
|
|
8718
9086
|
handle.setAttribute("aria-valuenow", String(Math.round(wipe * 100)));
|
|
8719
9087
|
handle.setAttribute("aria-valuetext", `${Math.round(wipe * 100)}% Before`);
|
|
@@ -8726,24 +9094,27 @@ function compare(container, options) {
|
|
|
8726
9094
|
if (pending)
|
|
8727
9095
|
return;
|
|
8728
9096
|
if (renderer === "dom") {
|
|
8729
|
-
const
|
|
9097
|
+
const cut = mode === "difference" ? null : fu;
|
|
9098
|
+
const place = (n, rect, clip, before) => {
|
|
8730
9099
|
const st = n.style;
|
|
8731
9100
|
st.transform = `translate(${rect.x.toFixed(2)}px,${rect.y.toFixed(2)}px)`;
|
|
8732
9101
|
st.width = `${rect.w.toFixed(2)}px`;
|
|
8733
9102
|
st.height = `${rect.h.toFixed(2)}px`;
|
|
9103
|
+
const edge = before && cut ? cut.cut + cut.feather : Number.POSITIVE_INFINITY;
|
|
8734
9104
|
const t = Math.max(0, clip.y - rect.y);
|
|
8735
9105
|
const l = Math.max(0, clip.x - rect.x);
|
|
8736
9106
|
const b = Math.max(0, rect.y + rect.h - (clip.y + clip.h));
|
|
8737
|
-
const right = Math.max(0, rect.x + rect.w - Math.min(clip.x + clip.w,
|
|
9107
|
+
const right = Math.max(0, rect.x + rect.w - Math.min(clip.x + clip.w, edge));
|
|
8738
9108
|
st.clipPath = t + l + b + right > 0.01 ? `inset(${t.toFixed(2)}px ${right.toFixed(2)}px ${b.toFixed(2)}px ${l.toFixed(2)}px)` : "";
|
|
9109
|
+
const mask = before && cut && cut.feather > 0.5 ? `linear-gradient(to right,#000 ${(cut.cut - cut.feather - rect.x).toFixed(2)}px,transparent ${(edge - rect.x).toFixed(2)}px)` : "";
|
|
9110
|
+
st.maskImage = mask;
|
|
9111
|
+
st.setProperty("-webkit-mask-image", mask);
|
|
8739
9112
|
};
|
|
8740
|
-
const cut = mode === "wipe" ? wx : Number.POSITIVE_INFINITY;
|
|
8741
9113
|
for (const s of sides) {
|
|
8742
9114
|
const rect = s.index === 0 ? r.zBefore : r.zAfter;
|
|
8743
9115
|
const clip = s.index === 0 ? r.before : r.after;
|
|
8744
|
-
|
|
8745
|
-
place(s.
|
|
8746
|
-
place(s.preview, rect, clip, at);
|
|
9116
|
+
place(s.el, rect, clip, s.index === 0);
|
|
9117
|
+
place(s.preview, rect, clip, s.index === 0);
|
|
8747
9118
|
if (s.index !== 0)
|
|
8748
9119
|
continue;
|
|
8749
9120
|
for (const n of [s.el, s.preview])
|
|
@@ -8763,8 +9134,8 @@ function compare(container, options) {
|
|
|
8763
9134
|
width: Math.max(1, Math.round(stageW * dpr)),
|
|
8764
9135
|
height: Math.max(1, Math.round(stageH * dpr)),
|
|
8765
9136
|
dpr,
|
|
8766
|
-
wipeX:
|
|
8767
|
-
soft: 0.75 * dpr,
|
|
9137
|
+
wipeX: fu.cut * dpr,
|
|
9138
|
+
soft: Math.max(0.75, fu.feather) * dpr,
|
|
8768
9139
|
from: MODE_INDEX[fromMode],
|
|
8769
9140
|
to: MODE_INDEX[toMode],
|
|
8770
9141
|
mix: Math.min(1, Math.max(0, fade.x)),
|
|
@@ -8791,6 +9162,7 @@ function compare(container, options) {
|
|
|
8791
9162
|
zu.step(dt);
|
|
8792
9163
|
zv.step(dt);
|
|
8793
9164
|
zw.step(dt);
|
|
9165
|
+
gripAt.step(dt);
|
|
8794
9166
|
const playing = isPlaying();
|
|
8795
9167
|
if (playing) {
|
|
8796
9168
|
const m = master();
|
|
@@ -8821,11 +9193,9 @@ function compare(container, options) {
|
|
|
8821
9193
|
pushFrame(s);
|
|
8822
9194
|
}
|
|
8823
9195
|
}
|
|
8824
|
-
const moving = !lay.resting || !fade.resting || !zoomResting() || wipeCatching;
|
|
9196
|
+
const moving = !lay.resting || !fade.resting || !zoomResting() || wipeCatching || !gripAt.resting;
|
|
8825
9197
|
if (moving)
|
|
8826
9198
|
dirty = true;
|
|
8827
|
-
if (auto && !lay.resting)
|
|
8828
|
-
fitHeight();
|
|
8829
9199
|
if (dirty)
|
|
8830
9200
|
draw();
|
|
8831
9201
|
return playing || moving;
|
|
@@ -8851,13 +9221,7 @@ function compare(container, options) {
|
|
|
8851
9221
|
zw.set(c.w);
|
|
8852
9222
|
requestDraw();
|
|
8853
9223
|
};
|
|
8854
|
-
const autoHeight = (w) =>
|
|
8855
|
-
const shape = sizeOf(sides[1]);
|
|
8856
|
-
const side = splitHeight(w, shape);
|
|
8857
|
-
const over = w * shape.h / shape.w;
|
|
8858
|
-
const h = side + (over - side) * lay.x;
|
|
8859
|
-
return Math.max(60, Math.min(win.innerHeight * 0.8, h));
|
|
8860
|
-
};
|
|
9224
|
+
const autoHeight = (w) => Math.max(60, Math.min(win.innerHeight * 0.8, splitHeight(w, sizeOf(sides[1]))));
|
|
8861
9225
|
const fitHeight = () => {
|
|
8862
9226
|
const h = autoHeight(stageW);
|
|
8863
9227
|
if (Math.abs(h - stageH) < 0.25)
|
|
@@ -8869,7 +9233,8 @@ function compare(container, options) {
|
|
|
8869
9233
|
const readColors = () => {
|
|
8870
9234
|
const c = tokenColor(win.getComputedStyle(root), "--_ku-cmp-diff", [0.49, 0.81, 0.61, 1]);
|
|
8871
9235
|
accent = [c[0], c[1], c[2]];
|
|
8872
|
-
|
|
9236
|
+
const unfold = MARK.w - labelFold(folded, MARK.w).width + MARK.gap * folded;
|
|
9237
|
+
labelW = [labels[0].offsetWidth + unfold, labels[1].offsetWidth + unfold];
|
|
8873
9238
|
requestDraw();
|
|
8874
9239
|
};
|
|
8875
9240
|
const measure = () => {
|
|
@@ -8960,8 +9325,10 @@ function compare(container, options) {
|
|
|
8960
9325
|
root.focus({ preventScroll: true });
|
|
8961
9326
|
touches.set(e.pointerId, p);
|
|
8962
9327
|
stage.setPointerCapture(e.pointerId);
|
|
9328
|
+
press = touches.size === 1 && mode !== "wipe" && master() ? p : null;
|
|
8963
9329
|
if (touches.size >= 2) {
|
|
8964
9330
|
e.preventDefault();
|
|
9331
|
+
press = null;
|
|
8965
9332
|
return startPinch();
|
|
8966
9333
|
}
|
|
8967
9334
|
if (mode === "wipe") {
|
|
@@ -8971,7 +9338,7 @@ function compare(container, options) {
|
|
|
8971
9338
|
if (inside && (grab.onLine || !zoomed())) {
|
|
8972
9339
|
drag = { kind: "wipe", offset: grab.offset };
|
|
8973
9340
|
root.dataset.dragging = "";
|
|
8974
|
-
|
|
9341
|
+
gripAt.snap(wr.h > 0 ? (p.y - wr.y) / wr.h : 0.5);
|
|
8975
9342
|
e.preventDefault();
|
|
8976
9343
|
if (!grab.onLine)
|
|
8977
9344
|
setWipe(wipeAt(p.x, wr), true, true);
|
|
@@ -8989,6 +9356,8 @@ function compare(container, options) {
|
|
|
8989
9356
|
const onMove = (e) => {
|
|
8990
9357
|
const p = local(e);
|
|
8991
9358
|
pointer = p;
|
|
9359
|
+
if (press && Math.hypot(p.x - press.x, p.y - press.y) > 4)
|
|
9360
|
+
press = null;
|
|
8992
9361
|
if (touches.has(e.pointerId))
|
|
8993
9362
|
touches.set(e.pointerId, p);
|
|
8994
9363
|
if (pinch && touches.size >= 2) {
|
|
@@ -9002,14 +9371,12 @@ function compare(container, options) {
|
|
|
9002
9371
|
pinch = { d, x: mid.x, y: mid.y };
|
|
9003
9372
|
return handZoom(zoomAbout(panned, k, q.pu, q.pv));
|
|
9004
9373
|
}
|
|
9005
|
-
if (
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
if (drag?.kind === "
|
|
9011
|
-
setWipe(wipeAt(p.x, wipeRect(layoutNow().L.overlay), drag.offset));
|
|
9012
|
-
else if (drag?.kind === "pan") {
|
|
9374
|
+
if (drag?.kind === "wipe") {
|
|
9375
|
+
const wr = wipeRect(layoutNow().L.overlay);
|
|
9376
|
+
gripAt.snap(wr.h > 0 ? (p.y - wr.y) / wr.h : 0.5);
|
|
9377
|
+
setWipe(wipeAt(p.x, wr, drag.offset));
|
|
9378
|
+
placeGrip(wr);
|
|
9379
|
+
} else if (drag?.kind === "pan") {
|
|
9013
9380
|
const dx = p.x - drag.x;
|
|
9014
9381
|
const dy = p.y - drag.y;
|
|
9015
9382
|
drag.x = p.x;
|
|
@@ -9020,11 +9387,23 @@ function compare(container, options) {
|
|
|
9020
9387
|
drawNow();
|
|
9021
9388
|
};
|
|
9022
9389
|
const endDrag = (e) => {
|
|
9390
|
+
if (press && e.type === "pointerup") {
|
|
9391
|
+
api.toggle();
|
|
9392
|
+
glyph.flash(isPlaying());
|
|
9393
|
+
}
|
|
9394
|
+
press = null;
|
|
9023
9395
|
touches.delete(e.pointerId);
|
|
9024
9396
|
if (touches.size < 2)
|
|
9025
9397
|
pinch = null;
|
|
9026
9398
|
if (!drag)
|
|
9027
9399
|
return;
|
|
9400
|
+
if (drag.kind === "wipe") {
|
|
9401
|
+
if (reducedMotion(win))
|
|
9402
|
+
gripAt.snap(0.5);
|
|
9403
|
+
else
|
|
9404
|
+
gripAt.set(0.5);
|
|
9405
|
+
requestDraw();
|
|
9406
|
+
}
|
|
9028
9407
|
drag = null;
|
|
9029
9408
|
delete root.dataset.dragging;
|
|
9030
9409
|
delete root.dataset.panning;
|
|
@@ -9174,6 +9553,7 @@ function compare(container, options) {
|
|
|
9174
9553
|
old.el.replaceWith(s.el);
|
|
9175
9554
|
old.preview.replaceWith(s.preview);
|
|
9176
9555
|
sides[index] = s;
|
|
9556
|
+
root.toggleAttribute("data-video", sides.some((x) => x.kind === "video"));
|
|
9177
9557
|
note.textContent = "";
|
|
9178
9558
|
setLabel(index, labelOf(index));
|
|
9179
9559
|
if (near)
|
|
@@ -9188,6 +9568,7 @@ function compare(container, options) {
|
|
|
9188
9568
|
stage.insertBefore(sides[0].preview, labels[0]);
|
|
9189
9569
|
setLabel(0, labelOf(0));
|
|
9190
9570
|
setLabel(1, labelOf(1));
|
|
9571
|
+
root.toggleAttribute("data-video", sides.some((s) => s.kind === "video"));
|
|
9191
9572
|
loupeBtn.setAttribute("aria-pressed", String(loupeOn));
|
|
9192
9573
|
loopBtn.setAttribute("aria-pressed", String(loopNow() !== null));
|
|
9193
9574
|
const modePill = modeList.length > 1 ? selectionPill(modes, {
|
|
@@ -9388,6 +9769,7 @@ function compare(container, options) {
|
|
|
9388
9769
|
disposed = true;
|
|
9389
9770
|
loop.stop();
|
|
9390
9771
|
modePill?.dispose();
|
|
9772
|
+
glyph.dispose();
|
|
9391
9773
|
win.clearTimeout(releaseTimer);
|
|
9392
9774
|
ro.disconnect();
|
|
9393
9775
|
nearIo?.disconnect();
|
|
@@ -9417,7 +9799,7 @@ function compare(container, options) {
|
|
|
9417
9799
|
wake();
|
|
9418
9800
|
return api;
|
|
9419
9801
|
}
|
|
9420
|
-
var COMPARE_FFMPEG = "ffmpeg -i in.mov -vf fps=30 -fps_mode cfr -c:v libx264 -preset slow -crf 18 -g 15 -keyint_min 15 -pix_fmt yuv420p -movflags +faststart -an out.mp4", LAYOUT, FADE2, FADE_REDUCED, ZOOM, WIPE_CATCH, SLOP = 12, INSET = 8, GRIP, LOUPE, FAST_SEEK_FRAMES = 15, RELEASE_MS = 3000, MODE_LABEL;
|
|
9802
|
+
var COMPARE_FFMPEG = "ffmpeg -i in.mov -vf fps=30 -fps_mode cfr -c:v libx264 -preset slow -crf 18 -g 15 -keyint_min 15 -pix_fmt yuv420p -movflags +faststart -an out.mp4", LAYOUT, FADE2, FADE_REDUCED, ZOOM, WIPE_CATCH, SLOP = 12, INSET = 8, GRIP, MARK, LOUPE, FAST_SEEK_FRAMES = 15, RELEASE_MS = 3000, MODE_LABEL;
|
|
9421
9803
|
var init_compare = __esm(() => {
|
|
9422
9804
|
init_motion();
|
|
9423
9805
|
init_page_hints();
|
|
@@ -9428,15 +9810,17 @@ var init_compare = __esm(() => {
|
|
|
9428
9810
|
init_icons();
|
|
9429
9811
|
init_look();
|
|
9430
9812
|
init_rgba();
|
|
9813
|
+
init_player();
|
|
9431
9814
|
init_selection_pill();
|
|
9432
9815
|
init_timeline();
|
|
9433
9816
|
init_timeline_model();
|
|
9434
|
-
LAYOUT = springMotion(
|
|
9817
|
+
LAYOUT = springMotion(SPRINGS.default);
|
|
9435
9818
|
FADE2 = springMotion(SPRINGS.snappy);
|
|
9436
9819
|
FADE_REDUCED = springMotion(FADE);
|
|
9437
9820
|
ZOOM = springMotion({ duration: 0.35, bounce: 0 });
|
|
9438
9821
|
WIPE_CATCH = springMotion(CATCH_UP);
|
|
9439
9822
|
GRIP = { w: 16, h: 28 };
|
|
9823
|
+
MARK = { w: 12, gap: 4 };
|
|
9440
9824
|
LOUPE = { r: 88, zoom: 4 };
|
|
9441
9825
|
MODE_LABEL = {
|
|
9442
9826
|
side: { short: "Side", long: "Side by side" },
|
|
@@ -10482,19 +10866,19 @@ var init_diff_css = __esm(() => {
|
|
|
10482
10866
|
@property --kd-fb{syntax:"<length>";inherits:false;initial-value:0px}
|
|
10483
10867
|
:where(.knobs-ui){--kd-add:var(--_k-diff-add);--kd-del:var(--_k-diff-del);--kd-mod:var(--_k-diff-mod);--kd-move:var(--k-diff-move,var(--_k-info))}
|
|
10484
10868
|
:where(.knobs-diff,.knobs-diff *,.knobs-diff-files,.knobs-diff-files *){box-sizing:border-box}
|
|
10485
|
-
.knobs-ui.knobs-diff{--kd-bg:var(--k-diff-bg,var(--_kd-bg,
|
|
10869
|
+
.knobs-ui.knobs-diff{--kd-bg:var(--k-diff-bg,var(--_kd-bg,transparent));--kd-solid:var(--kd-fill,var(--kd-bg));--kd-line:calc(var(--_k-font-size) * 1.5);
|
|
10486
10870
|
--kd-i:var(--k-diff-inset,8px);--kd-x:var(--kd-i);--kd-inner:max(3px,calc(var(--_ku-pane-radius) - var(--kd-i)));--kd-ctl:var(--kd-inner);
|
|
10487
10871
|
--kd-head:calc(var(--_k-control-h) + 2 * var(--kd-i));--kd-fade:var(--k-diff-fade,20px);--kd-stick:0px;position:relative;display:flex;flex-direction:column;width:100%;height:100%;min-width:0;min-height:0;overflow:hidden;
|
|
10488
10872
|
background:var(--kd-bg);border-radius:var(--_ku-pane-radius);font:var(--_k-weight) var(--_k-font-size)/1.35 var(--_k-font);color:var(--_k-fg);text-align:left;
|
|
10489
10873
|
letter-spacing:normal;word-spacing:normal;text-transform:none;-webkit-font-smoothing:antialiased}
|
|
10490
10874
|
.knobs-ui.knobs-diff[data-height="auto"]{height:auto}
|
|
10491
|
-
.knobs-diff::after{content:"";position:absolute;inset:0;z-index:3;
|
|
10875
|
+
.knobs-ui.knobs-diff:not([data-on="pane"])::after{content:"";position:absolute;inset:0;z-index:3;box-shadow:inset 0 -1px 0 var(--_k-border-subtle);pointer-events:none}
|
|
10492
10876
|
.knobs-diff [hidden],.knobs-diff-files [hidden]{display:none!important}
|
|
10493
10877
|
.knobs-diff svg{display:block;flex:none}
|
|
10494
|
-
.knobs-ui.knobs-diff[data-on="
|
|
10495
|
-
/*
|
|
10878
|
+
.knobs-ui.knobs-diff:not([data-on="pane"]){--kd-x:0px;--kd-head:calc(var(--_k-control-h) + var(--kd-i));--kd-ctl:var(--_k-radius)}
|
|
10879
|
+
/* A hairline under the header, firmer once the code has scrolled under it. */
|
|
10496
10880
|
.knobs-diff-head{position:relative;z-index:1;flex:none;display:flex;align-items:center;gap:8px;height:var(--kd-head);padding:0 var(--kd-x);
|
|
10497
|
-
box-shadow:inset 0 -1px
|
|
10881
|
+
box-shadow:inset 0 -1px var(--_k-border-subtle);transition:box-shadow .15s;user-select:none;-webkit-user-select:none}
|
|
10498
10882
|
.knobs-diff[data-scrolled]>.knobs-diff-head{box-shadow:inset 0 -1px var(--_k-border)}
|
|
10499
10883
|
.knobs-diff-path{flex:0 1 auto;min-width:0;overflow:hidden;white-space:nowrap}
|
|
10500
10884
|
.knobs-diff-dir,.knobs-diff-from{color:var(--_k-fg-3)}
|
|
@@ -10532,6 +10916,15 @@ var init_diff_css = __esm(() => {
|
|
|
10532
10916
|
.knobs-ui .knobs-diff-viewed[aria-pressed="true"]{color:var(--_k-fg);background:var(--_ku-wash)}
|
|
10533
10917
|
.knobs-diff-viewed svg{width:16px;height:16px}
|
|
10534
10918
|
.knobs-diff-viewed[aria-pressed="true"] svg{color:var(--kd-add)}
|
|
10919
|
+
/* Narrow, the header gives up words before anything is cut off at its end: Viewed keeps its
|
|
10920
|
+
check, then the counts' bar goes, then the counts; the path fades at its end throughout. */
|
|
10921
|
+
.knobs-diff-head,.knobs-diff-part-head{container-type:inline-size}
|
|
10922
|
+
@container (max-width:520px){
|
|
10923
|
+
.knobs-diff-viewed-label{display:none}
|
|
10924
|
+
.knobs-ui .knobs-diff-viewed{width:var(--_k-control-h);padding:0;justify-content:center}
|
|
10925
|
+
.knobs-diff-counts .knobs-diff-bar{display:none}
|
|
10926
|
+
}
|
|
10927
|
+
@container (max-width:400px){.knobs-diff-counts{display:none}.knobs-diff-nav>span{min-width:2.6em}}
|
|
10535
10928
|
.knobs-diff-path{transition:opacity .2s}
|
|
10536
10929
|
.knobs-diff[data-viewed]>.knobs-diff-head .knobs-diff-path,.knobs-diff-part[data-viewed] .knobs-diff-path{opacity:.6}
|
|
10537
10930
|
/* Split | Unified: the selection pill on the one picked, its bright copy the picked one's text; a
|
|
@@ -10596,24 +10989,26 @@ var init_diff_css = __esm(() => {
|
|
|
10596
10989
|
--diffs-bg-deletion-number-override:color-mix(in srgb,var(--kd-del) 55%,var(--kd-solid));
|
|
10597
10990
|
--diffs-bg-addition-emphasis-override:color-mix(in srgb,var(--kd-add) 22%,transparent);
|
|
10598
10991
|
--diffs-bg-deletion-emphasis-override:color-mix(in srgb,var(--kd-del) 22%,transparent);
|
|
10599
|
-
|
|
10600
|
-
|
|
10601
|
-
--diffs-bg-
|
|
10992
|
+
/* Unchanged lines and the folds between hunks sit on the void; only a split's missing lines
|
|
10993
|
+
are faintly filled. */
|
|
10994
|
+
--diffs-bg-separator-override:var(--kd-solid);
|
|
10995
|
+
--diffs-bg-context-override:var(--kd-solid);
|
|
10996
|
+
--diffs-bg-buffer-override:color-mix(in srgb,var(--_k-fg) 2.5%,var(--kd-solid));
|
|
10602
10997
|
--diffs-bg-selection-override:var(--_k-accent);
|
|
10603
10998
|
--diffs-fg-number-override:var(--_k-fg-3);
|
|
10604
10999
|
}
|
|
10605
|
-
/* diffReview
|
|
10606
|
-
is pinned to its right edge, so it slides out from under the diff), parted
|
|
10607
|
-
|
|
10608
|
-
.knobs-ui.knobs-diff-review{position:relative;display:flex;width:100%;height:100%;min-width:0;min-height:0;overflow:hidden;
|
|
11000
|
+
/* diffReview, on the void as the diff is: the file list a drawer at its left (its width springs;
|
|
11001
|
+
the list inside is pinned to its right edge, so it slides out from under the diff), parted
|
|
11002
|
+
from the diff by a hairline; one hairline runs under both headers and one under it all. */
|
|
11003
|
+
.knobs-ui.knobs-diff-review{position:relative;display:flex;width:100%;height:100%;min-width:0;min-height:0;overflow:hidden;
|
|
10609
11004
|
font:var(--_k-weight) var(--_k-font-size)/1.35 var(--_k-font);color:var(--_k-fg);text-align:left;letter-spacing:normal;-webkit-font-smoothing:antialiased}
|
|
10610
|
-
.knobs-diff-review::after{content:"";position:absolute;inset:0;z-index:5;
|
|
11005
|
+
.knobs-diff-review::after{content:"";position:absolute;inset:0;z-index:5;box-shadow:inset 0 -1px 0 var(--_k-border-subtle);pointer-events:none}
|
|
10611
11006
|
:where(.knobs-diff-review,.knobs-diff-review *){box-sizing:border-box}
|
|
10612
|
-
.knobs-diff-drawer{position:relative;flex:none;width:0;height:100%;overflow:hidden;box-shadow:inset -1px 0 var(--_k-border)}
|
|
11007
|
+
.knobs-diff-drawer{position:relative;flex:none;width:0;height:100%;overflow:hidden;box-shadow:inset -1px 0 var(--_k-border-subtle)}
|
|
10613
11008
|
.knobs-diff-drawer[data-shut]{visibility:hidden}
|
|
10614
11009
|
.knobs-diff-drawer-in{position:absolute;top:0;bottom:0;right:0;display:flex;flex-direction:column}
|
|
10615
11010
|
.knobs-diff-drawer-head{flex:none;display:flex;align-items:center;gap:6px;height:calc(var(--_k-control-h) + 2 * var(--k-diff-inset,8px));padding:0 var(--k-diff-inset,8px) 0 calc(var(--_ku-row-x) + 8px);
|
|
10616
|
-
user-select:none;-webkit-user-select:none}
|
|
11011
|
+
box-shadow:inset 0 -1px var(--_k-border-subtle);user-select:none;-webkit-user-select:none}
|
|
10617
11012
|
.knobs-diff-drawer-title{font-weight:var(--_k-weight-title)}
|
|
10618
11013
|
.knobs-diff-drawer-count{color:var(--_k-fg-3);font-size:var(--_k-text-meta);font-variant-numeric:tabular-nums}
|
|
10619
11014
|
.knobs-diff-drawer-in>.knobs-diff-files{flex:1;min-height:0;height:auto}
|
|
@@ -10625,10 +11020,11 @@ var init_diff_css = __esm(() => {
|
|
|
10625
11020
|
.knobs-ui .knobs-diff-toggle[aria-pressed]:not(:hover,:focus-visible){background:transparent}
|
|
10626
11021
|
/* diffReview's file list: a filter over a compact tree, a status letter, the counts and their
|
|
10627
11022
|
bar on each file; one highlight glides to the file on show. Viewed files dim, with a tick. */
|
|
10628
|
-
.knobs-diff-files{--k-row:var(--k-diff-row,28px);display:flex;flex-direction:column;height:100%;min-height:0;padding-top:
|
|
11023
|
+
.knobs-diff-files{--k-row:var(--k-diff-row,28px);display:flex;flex-direction:column;height:100%;min-height:0;padding-top:4px}
|
|
10629
11024
|
.knobs-diff-filter{position:relative;flex:none;display:flex;align-items:center;gap:6px;height:26px;margin:0 var(--_ku-row-x) 4px;padding:0 4px 0 8px;
|
|
10630
|
-
border-radius:max(6px,var(--_ku-row-radius));
|
|
10631
|
-
|
|
11025
|
+
border-radius:max(6px,var(--_ku-row-radius));color:var(--_k-fg-3);box-shadow:inset 0 0 0 1px transparent;transition:box-shadow .15s}
|
|
11026
|
+
/* The filter is a line of text, no box; focused, a hairline ring. */
|
|
11027
|
+
.knobs-diff-filter:focus-within{box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--_k-accent) 45%,transparent)}
|
|
10632
11028
|
.knobs-diff-filter svg{width:12px;height:12px;flex:none}
|
|
10633
11029
|
.knobs-ui .knobs-diff-filter>input{flex:1;min-width:0;height:100%;font:var(--_k-weight) var(--_k-font-size)/1 var(--_k-font);color:var(--_k-fg);caret-color:var(--_k-accent);background:transparent}
|
|
10634
11030
|
.knobs-ui .knobs-diff-filter>input::placeholder{color:var(--_k-fg-3)}
|
|
@@ -11447,7 +11843,10 @@ function codeDiff(container, options) {
|
|
|
11447
11843
|
const viewedToggle = () => {
|
|
11448
11844
|
const b = make("button", "knobs-diff-viewed");
|
|
11449
11845
|
b.type = "button";
|
|
11450
|
-
|
|
11846
|
+
const label = make("span", "knobs-diff-viewed-label");
|
|
11847
|
+
label.textContent = "Viewed";
|
|
11848
|
+
b.append(make("span", ""), label);
|
|
11849
|
+
b.setAttribute("aria-label", "Viewed");
|
|
11451
11850
|
return b;
|
|
11452
11851
|
};
|
|
11453
11852
|
const paintViewed = (b, on) => {
|
|
@@ -16146,6 +16545,7 @@ init_timeline();
|
|
|
16146
16545
|
init_timeline_model();
|
|
16147
16546
|
init_compare();
|
|
16148
16547
|
init_compare_strip();
|
|
16548
|
+
init_player();
|
|
16149
16549
|
init_compare_model();
|
|
16150
16550
|
init_tree();
|
|
16151
16551
|
init_tree_model();
|
|
@@ -19859,7 +20259,7 @@ function normalizeWalkthrough(doc) {
|
|
|
19859
20259
|
...step.question ? { question: step.question } : {},
|
|
19860
20260
|
media: list.map((m) => fillMedia(m, doc.base)),
|
|
19861
20261
|
columns: Math.max(1, Math.min(3, Math.round(step.columns ?? 1))),
|
|
19862
|
-
...step.more?.trim()
|
|
20262
|
+
...step.more?.trim() ? { more: step.more.trim() } : {},
|
|
19863
20263
|
options,
|
|
19864
20264
|
ask
|
|
19865
20265
|
};
|
|
@@ -19874,6 +20274,7 @@ function normalizeWalkthrough(doc) {
|
|
|
19874
20274
|
...typeof s.kicker === "string" && s.kicker.trim() ? { kicker: s.kicker.trim() } : {},
|
|
19875
20275
|
...s.status ? { status: s.status } : {},
|
|
19876
20276
|
...s.summary ? { summary: s.summary } : {},
|
|
20277
|
+
...s.more?.trim() ? { more: s.more.trim() } : {},
|
|
19877
20278
|
steps: own
|
|
19878
20279
|
};
|
|
19879
20280
|
});
|
|
@@ -19907,6 +20308,7 @@ function normalizeWalkthrough(doc) {
|
|
|
19907
20308
|
id: doc.id,
|
|
19908
20309
|
title: doc.title,
|
|
19909
20310
|
...doc.summary ? { summary: doc.summary } : {},
|
|
20311
|
+
...doc.more?.trim() ? { more: doc.more.trim() } : {},
|
|
19910
20312
|
meta,
|
|
19911
20313
|
feed,
|
|
19912
20314
|
verdicts: ((doc.verdicts?.length) ? doc.verdicts : DEFAULT_VERDICTS).map((v) => ({
|
|
@@ -19914,9 +20316,40 @@ function normalizeWalkthrough(doc) {
|
|
|
19914
20316
|
label: label(v)
|
|
19915
20317
|
})),
|
|
19916
20318
|
sections: withKickers(sections, doc.sections, doc.kickers),
|
|
19917
|
-
steps
|
|
20319
|
+
steps,
|
|
20320
|
+
prs: knownPRs(doc, sections),
|
|
20321
|
+
...repoOf(doc) ? { repo: repoOf(doc) } : {}
|
|
19918
20322
|
};
|
|
19919
20323
|
}
|
|
20324
|
+
var prNumber = (href) => /\/(?:pull|merge_requests)\/(\d+)(?:[/?#]|$)/.exec(href)?.[1] ?? null;
|
|
20325
|
+
var repoOf = (doc) => doc.repo?.trim().replace(/^https?:\/\/github\.com\//, "").replace(/\/+$/, "") || undefined;
|
|
20326
|
+
function knownPRs(doc, sections) {
|
|
20327
|
+
const out = {};
|
|
20328
|
+
const repo = repoOf(doc);
|
|
20329
|
+
for (const s of sections) {
|
|
20330
|
+
const n = s.link && prNumber(s.link.href);
|
|
20331
|
+
if (n)
|
|
20332
|
+
out[n] = { href: s.link.href.replace(/(\/pull\/\d+).*$/, "$1") };
|
|
20333
|
+
}
|
|
20334
|
+
for (const [k, pr] of Object.entries(doc.prs ?? {})) {
|
|
20335
|
+
const n = k.replace(/^#/, "");
|
|
20336
|
+
if (!/^\d+$/.test(n))
|
|
20337
|
+
continue;
|
|
20338
|
+
out[n] = { ...out[n], ...pr };
|
|
20339
|
+
}
|
|
20340
|
+
if (repo) {
|
|
20341
|
+
for (const n of Object.keys(out))
|
|
20342
|
+
if (!out[n].href)
|
|
20343
|
+
out[n] = { ...out[n], href: `https://github.com/${repo}/pull/${n}` };
|
|
20344
|
+
}
|
|
20345
|
+
return out;
|
|
20346
|
+
}
|
|
20347
|
+
function findPR(prs, n, repo) {
|
|
20348
|
+
const known = prs[n];
|
|
20349
|
+
if (known)
|
|
20350
|
+
return known;
|
|
20351
|
+
return repo ? { href: `https://github.com/${repo}/pull/${n}` } : null;
|
|
20352
|
+
}
|
|
19920
20353
|
var isPullRequest = (href) => /\/(?:pull|merge_requests)\/\d+(?:[/?#]|$)/.test(href);
|
|
19921
20354
|
function withKickers(sections, source, kickers = "pr") {
|
|
19922
20355
|
if (kickers === false)
|
|
@@ -20742,6 +21175,74 @@ function disclosure(host, options) {
|
|
|
20742
21175
|
toggle.addEventListener("click", () => api.toggle());
|
|
20743
21176
|
return api;
|
|
20744
21177
|
}
|
|
21178
|
+
var PR_PATHS = {
|
|
21179
|
+
pull: "M104,64A32,32,0,1,0,64,95v66a32,32,0,1,0,16,0V95A32.06,32.06,0,0,0,104,64ZM56,64A16,16,0,1,1,72,80,16,16,0,0,1,56,64ZM88,192a16,16,0,1,1-16-16A16,16,0,0,1,88,192Zm120-31V110.63a23.85,23.85,0,0,0-7-17L163.31,56H192a8,8,0,0,0,0-16H144a8,8,0,0,0-8,8V96a8,8,0,0,0,16,0V67.31L189.66,105a8,8,0,0,1,2.34,5.66V161a32,32,0,1,0,16,0Zm-8,47a16,16,0,1,1,16-16A16,16,0,0,1,200,208Z",
|
|
21180
|
+
merge: "M208,112a32.05,32.05,0,0,0-30.69,23l-42.21-6a8,8,0,0,1-4.95-2.71L94.43,84.55A32,32,0,1,0,72,87v82a32,32,0,1,0,16,0V101.63l30,35a24,24,0,0,0,14.83,8.14l44,6.28A32,32,0,1,0,208,112ZM64,56A16,16,0,1,1,80,72,16,16,0,0,1,64,56ZM96,200a16,16,0,1,1-16-16A16,16,0,0,1,96,200Zm112-40a16,16,0,1,1,16-16A16,16,0,0,1,208,160Z"
|
|
21181
|
+
};
|
|
21182
|
+
var prGlyph = (merged) => `<svg class="knobs-icon" viewBox="0 0 256 256" width="12" height="12" fill="currentColor" aria-hidden="true" focusable="false"><path d="${merged ? PR_PATHS.merge : PR_PATHS.pull}"/></svg>`;
|
|
21183
|
+
var PR_STATE = { merged: "Merged", open: "Open", draft: "Draft", closed: "Closed" };
|
|
21184
|
+
function prChip(doc, n, pr) {
|
|
21185
|
+
const chip = el(doc, pr.href ? "a" : "span", "knobs-wt-pr");
|
|
21186
|
+
if (pr.href) {
|
|
21187
|
+
chip.setAttribute("href", pr.href);
|
|
21188
|
+
chip.setAttribute("target", "_blank");
|
|
21189
|
+
chip.setAttribute("rel", "noreferrer");
|
|
21190
|
+
}
|
|
21191
|
+
if (pr.state)
|
|
21192
|
+
chip.dataset.state = pr.state;
|
|
21193
|
+
chip.innerHTML = prGlyph(pr.state === "merged");
|
|
21194
|
+
chip.append(text(doc, "span", "", `#${n}`));
|
|
21195
|
+
const tip = [`#${n}`, pr.title, pr.state ? PR_STATE[pr.state] : ""].filter(Boolean).join(" · ");
|
|
21196
|
+
chip.title = tip;
|
|
21197
|
+
chip.setAttribute("aria-label", `Pull request ${tip}`);
|
|
21198
|
+
return chip;
|
|
21199
|
+
}
|
|
21200
|
+
var PR_URL = /^https?:\/\/[^/]+\/.+\/(?:pull|merge_requests)\/(\d+)\/?(?:[?#].*)?$/;
|
|
21201
|
+
var PR_REF = /(^|[^\w&/#])#(\d{3,7})\b/g;
|
|
21202
|
+
function linkPRs(root, find) {
|
|
21203
|
+
const doc = root.ownerDocument;
|
|
21204
|
+
for (const a of Array.from(root.querySelectorAll("a[href]"))) {
|
|
21205
|
+
if (a.classList.contains("knobs-wt-pr") || a.closest(".knobs-wt-slot,button"))
|
|
21206
|
+
continue;
|
|
21207
|
+
const n = PR_URL.exec(a.getAttribute("href") ?? "")?.[1];
|
|
21208
|
+
const words = a.textContent?.trim() ?? "";
|
|
21209
|
+
if (!n || !(words === `#${n}` || words === n || words === a.getAttribute("href")))
|
|
21210
|
+
continue;
|
|
21211
|
+
a.replaceWith(prChip(doc, n, { href: a.href, ...find(n) }));
|
|
21212
|
+
}
|
|
21213
|
+
const walker = doc.createTreeWalker(root, 5);
|
|
21214
|
+
const hits = [];
|
|
21215
|
+
for (let t = walker.nextNode();t; t = walker.nextNode()) {
|
|
21216
|
+
if (t.nodeType !== 3)
|
|
21217
|
+
continue;
|
|
21218
|
+
const p = t.parentElement;
|
|
21219
|
+
if (!p || p.closest("a,code,pre,button,.knobs-wt-slot,.knobs-wt-pr"))
|
|
21220
|
+
continue;
|
|
21221
|
+
PR_REF.lastIndex = 0;
|
|
21222
|
+
if (PR_REF.test(t.nodeValue ?? ""))
|
|
21223
|
+
hits.push(t);
|
|
21224
|
+
}
|
|
21225
|
+
for (const t of hits) {
|
|
21226
|
+
const value = t.nodeValue ?? "";
|
|
21227
|
+
const frag = doc.createDocumentFragment();
|
|
21228
|
+
let last = 0;
|
|
21229
|
+
let changed = false;
|
|
21230
|
+
PR_REF.lastIndex = 0;
|
|
21231
|
+
for (let m = PR_REF.exec(value);m; m = PR_REF.exec(value)) {
|
|
21232
|
+
const pr = find(m[2]);
|
|
21233
|
+
if (!pr)
|
|
21234
|
+
continue;
|
|
21235
|
+
const at = m.index + m[1].length;
|
|
21236
|
+
frag.append(value.slice(last, at), prChip(doc, m[2], pr));
|
|
21237
|
+
last = at + m[2].length + 1;
|
|
21238
|
+
changed = true;
|
|
21239
|
+
}
|
|
21240
|
+
if (!changed)
|
|
21241
|
+
continue;
|
|
21242
|
+
frag.append(value.slice(last));
|
|
21243
|
+
t.replaceWith(frag);
|
|
21244
|
+
}
|
|
21245
|
+
}
|
|
20745
21246
|
|
|
20746
21247
|
// src/ui/walkthrough-media.ts
|
|
20747
21248
|
init_compare_model();
|
|
@@ -20759,7 +21260,7 @@ var fade2 = "-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 1.5em)
|
|
|
20759
21260
|
var walkthroughCss = `
|
|
20760
21261
|
:where(.knobs-walkthrough,.knobs-walkthrough *,.knobs-wt-slot,.knobs-wt-slot *){box-sizing:border-box}
|
|
20761
21262
|
.knobs-ui.knobs-walkthrough{--kw-snappy:${snappy3.easing};--kw-snappy-ms:${snappy3.duration}ms;--kw-calm:${calm.easing};--kw-calm-ms:${calm.duration}ms;--kw-state:${state.easing};--kw-state-ms:${state.duration}ms;--kw-quick:${quick.easing};--kw-quick-ms:${quick.duration}ms;
|
|
20762
|
-
--kw-measure:var(--k-walkthrough-measure,68ch);--kw-width:var(--k-walkthrough-width,
|
|
21263
|
+
--kw-measure:var(--k-walkthrough-measure,68ch);--kw-width:var(--k-walkthrough-width,960px);--kw-rail-w:220px;--kw-good:var(--k-walkthrough-good,var(--_k-ok));--kw-info:var(--k-walkthrough-info,var(--_k-info));
|
|
20763
21264
|
--kw-card:var(--_ku-pane-bg);--kw-card-ring:var(--_ku-pane-ring);--kw-under:var(--_k-bg-page);--kw-ink:var(--k-walkthrough-ink,#ff375f);--kw-strong:var(--_k-weight-label);
|
|
20764
21265
|
container:knobs-wt / inline-size;display:block;width:100%;color:var(--_k-fg);font:var(--_k-weight) var(--_k-type-body)/var(--_k-leading-body) var(--_k-font);font-variant-numeric:tabular-nums;text-align:left;
|
|
20765
21266
|
letter-spacing:normal;-webkit-font-smoothing:antialiased}
|
|
@@ -20772,44 +21273,57 @@ var walkthroughCss = `
|
|
|
20772
21273
|
:where(.knobs-ui) :is(.knobs-wt-btn,.knobs-wt-pick,.knobs-wt-toc-row,button.knobs-wt-stitle,.knobs-wt-chapter,.knobs-wt-disc-toggle){all:revert;box-sizing:border-box;font:inherit;color:inherit;letter-spacing:inherit;
|
|
20773
21274
|
background:transparent;border:0;border-radius:0;margin:0;padding:0;min-width:0;box-shadow:none;outline:none}
|
|
20774
21275
|
|
|
20775
|
-
/* ——— layout:
|
|
20776
|
-
|
|
20777
|
-
|
|
21276
|
+
/* ——— layout: the contents rail sticky at the left edge (right if asked), and the column centred
|
|
21277
|
+
in the room beside it, up to --kw-width: media take the column, prose stops at the measure.
|
|
21278
|
+
One column where it's narrow ——— */
|
|
21279
|
+
.knobs-wt-layout{display:grid;grid-template-columns:var(--kw-rail-w) minmax(0,1fr);gap:var(--_k-space-column);align-items:start}
|
|
21280
|
+
.knobs-walkthrough[data-rail="right"] .knobs-wt-layout{grid-template-columns:minmax(0,1fr) var(--kw-rail-w)}
|
|
20778
21281
|
.knobs-walkthrough[data-rail="right"] .knobs-wt-main{grid-column:1;grid-row:1}
|
|
20779
21282
|
.knobs-walkthrough[data-rail="right"] .knobs-wt-rail{grid-column:2;grid-row:1}
|
|
20780
|
-
.knobs-walkthrough[data-rail="
|
|
20781
|
-
.knobs-
|
|
20782
|
-
.knobs-wt-main{display:flex;flex-direction:column;gap:var(--_k-space-group);min-width:0;padding-bottom:12px}
|
|
21283
|
+
.knobs-walkthrough[data-rail="none"] .knobs-wt-layout{grid-template-columns:minmax(0,1fr)}
|
|
21284
|
+
.knobs-wt-main{justify-self:center;width:100%;max-width:var(--kw-width);display:flex;flex-direction:column;gap:var(--_k-space-group);min-width:0;padding-bottom:12px}
|
|
20783
21285
|
/* The intro, the updates and each section: a section's room apart, the hairline in the middle. */
|
|
20784
21286
|
.knobs-wt-flow{display:flex;flex-direction:column;gap:calc(var(--_k-space-section) / 2);min-width:0}
|
|
20785
21287
|
.knobs-wt-flow>*+*{padding-top:calc(var(--_k-space-section) / 2);box-shadow:inset 0 1px 0 var(--_k-border-subtle)}
|
|
20786
|
-
@container knobs-wt (max-width:
|
|
21288
|
+
@container knobs-wt (max-width:960px){.knobs-walkthrough[data-rail] .knobs-wt-layout{grid-template-columns:minmax(0,1fr);gap:var(--_k-space-title)}
|
|
20787
21289
|
.knobs-walkthrough[data-rail] .knobs-wt-layout>*{grid-column:auto;grid-row:auto}}
|
|
20788
21290
|
|
|
20789
21291
|
/* ——— one line of facts: small, faint, joined by ·, its trailing edge fading ——— */
|
|
20790
21292
|
.knobs-wt-metaline{display:flex;align-items:baseline;gap:var(--_k-space-inside);min-width:0;overflow:hidden;white-space:nowrap;font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3);${fade2}}
|
|
20791
21293
|
.knobs-wt-metaline>*{flex:none}
|
|
20792
21294
|
.knobs-wt-metaline>*+*::before{content:"·";margin-right:var(--_k-space-inside);color:var(--_k-fg-3)}
|
|
20793
|
-
.knobs-walkthrough .knobs-wt-metaline a{display:inline-flex;align-items:baseline;color:inherit;text-decoration:none;transition:color calc(var(--_k-hover-out) * 1ms)}
|
|
20794
|
-
.knobs-walkthrough .knobs-wt-metaline a:hover{color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
|
|
21295
|
+
.knobs-walkthrough .knobs-wt-metaline a:not(.knobs-wt-pr){display:inline-flex;align-items:baseline;color:inherit;text-decoration:none;transition:color calc(var(--_k-hover-out) * 1ms)}
|
|
21296
|
+
.knobs-walkthrough .knobs-wt-metaline a:not(.knobs-wt-pr):hover{color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
|
|
20795
21297
|
.knobs-wt-metaline svg{align-self:center;width:12px;height:12px;margin-left:2px}
|
|
21298
|
+
/* A pull request: a small chip, its state's icon and number in a quiet tint of the state (open
|
|
21299
|
+
the success hue, merged the violet series, draft and closed neutral), its title on hover. */
|
|
21300
|
+
.knobs-ui .knobs-wt-pr{--kw-pr:var(--_k-fg-2);display:inline-flex;align-items:center;gap:3px;height:18px;padding:0 5px 0 4px;margin:0 1px;border-radius:calc(var(--_k-radius) / 2);vertical-align:1px;
|
|
21301
|
+
background:color-mix(in oklch,var(--kw-pr) 12%,transparent);color:var(--kw-pr);font:var(--_k-weight-label) var(--_k-type-small)/1 var(--_k-font);font-variant-numeric:tabular-nums;white-space:nowrap;text-decoration:none;
|
|
21302
|
+
transition:background calc(var(--_k-hover-out) * 1ms)}
|
|
21303
|
+
.knobs-ui .knobs-wt-pr[data-state="open"]{--kw-pr:color-mix(in oklch,var(--_k-ok) 60%,var(--_k-fg-2))}
|
|
21304
|
+
.knobs-ui .knobs-wt-pr[data-state="merged"]{--kw-pr:color-mix(in oklch,var(--_k-series-5) 60%,var(--_k-fg-2))}
|
|
21305
|
+
.knobs-ui .knobs-wt-pr:is([data-state="draft"],[data-state="closed"]){--kw-pr:var(--_k-fg-3)}
|
|
21306
|
+
.knobs-ui a.knobs-wt-pr:is(:hover,:focus-visible){background:color-mix(in oklch,var(--kw-pr) 22%,transparent);transition-duration:calc(var(--_k-hover-in) * 1ms)}
|
|
21307
|
+
.knobs-wt-pr svg{flex:none}
|
|
21308
|
+
.knobs-wt-metaline .knobs-wt-pr{align-self:center;margin:0}
|
|
20796
21309
|
.knobs-wt-kicker-lead{color:var(--_k-fg);font-weight:var(--kw-strong)}
|
|
20797
21310
|
.knobs-wt-metaline [data-status="on"]{color:var(--_k-fg)}
|
|
20798
21311
|
.knobs-wt-metaline [data-status="warn"]{color:var(--_k-warn)}
|
|
20799
21312
|
|
|
20800
21313
|
/* ——— the page's head ——— */
|
|
20801
21314
|
.knobs-wt-head{display:flex;flex-direction:column;gap:var(--_k-space-title)}
|
|
20802
|
-
.knobs-wt-title{font-size:var(--_k-type-
|
|
21315
|
+
.knobs-wt-title{font-size:var(--_k-type-heading)!important;line-height:var(--_k-leading-heading);font-weight:var(--kw-strong)!important;text-wrap:balance}
|
|
20803
21316
|
.knobs-wt-summary{max-width:var(--kw-measure);color:var(--_k-fg-2);text-wrap:pretty}
|
|
20804
21317
|
.knobs-wt-meta{display:flex;flex-wrap:wrap;gap:var(--_k-space-inside) var(--_k-space-title);color:var(--_k-fg-3);font-size:var(--_k-type-small);line-height:var(--_k-leading-small)}
|
|
20805
21318
|
.knobs-wt-meta>span{display:inline-flex;gap:var(--_k-space-inside);white-space:nowrap}
|
|
20806
21319
|
.knobs-wt-meta b{font-weight:var(--_k-weight);color:var(--_k-fg-2);font-family:var(--_k-mono)}
|
|
20807
21320
|
|
|
20808
|
-
/* ——— a section: a PR or topic. Its kicker leads the meta line
|
|
21321
|
+
/* ——— a section: a PR or topic. Its kicker leads the meta line, its title under it (16/24, under
|
|
21322
|
+
the page's 20/28), both on the column's edge ——— */
|
|
20809
21323
|
.knobs-wt-section{display:flex;flex-direction:column;gap:var(--_k-space-group);min-width:0}
|
|
20810
21324
|
.knobs-wt-sintro{display:flex;flex-direction:column;gap:var(--_k-space-title);min-width:0}
|
|
20811
21325
|
.knobs-wt-shead{display:flex;flex-direction:column;gap:var(--_k-space-inside);min-width:0;scroll-margin-top:16px}
|
|
20812
|
-
.knobs-wt-heading{font-size:var(--_k-type-
|
|
21326
|
+
.knobs-wt-heading{font-size:var(--_k-type-title)!important;line-height:var(--_k-leading-title);font-weight:var(--kw-strong)!important;text-wrap:balance}
|
|
20813
21327
|
.knobs-wt-ssum{max-width:var(--kw-measure);color:var(--_k-fg-2);text-wrap:pretty}
|
|
20814
21328
|
.knobs-wt-chip{display:inline-flex;align-items:center;gap:6px;height:20px;padding:0 8px;border-radius:var(--_k-round);font-size:var(--_k-type-small);font-weight:var(--kw-strong);white-space:nowrap;
|
|
20815
21329
|
--kw-tone:var(--_k-fg-2);color:var(--kw-tone);background:color-mix(in srgb,var(--kw-tone) 14%,transparent)}
|
|
@@ -20870,6 +21384,8 @@ var walkthroughCss = `
|
|
|
20870
21384
|
.knobs-wt-disc[data-open]>.knobs-wt-disc-body:not([data-moving]){overflow:visible}
|
|
20871
21385
|
.knobs-wt-disc-body[data-moving]{-webkit-mask-image:linear-gradient(#000 calc(100% - 16px),transparent);mask-image:linear-gradient(#000 calc(100% - 16px),transparent)}
|
|
20872
21386
|
.knobs-wt-disc-content{padding-top:var(--_k-space-inside)}
|
|
21387
|
+
/* "More" under a caption or summary: the rest of its words, in the same voice. */
|
|
21388
|
+
.knobs-wt-more-text{display:flex;flex-direction:column;gap:var(--_k-space-sibling);max-width:var(--kw-measure);color:var(--_k-fg-2);text-wrap:pretty}
|
|
20873
21389
|
/* A trail sits on the toggle's label edge: faint lines, the time trailing. */
|
|
20874
21390
|
.knobs-wt-trail{display:flex;flex-direction:column;list-style:none;padding:0 0 0 calc(12px + var(--_k-space-inside));font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3);min-width:0}
|
|
20875
21391
|
.knobs-wt-trail li{display:flex;align-items:baseline;gap:var(--_k-space-sibling);min-width:0;white-space:nowrap}
|
|
@@ -20987,32 +21503,28 @@ var walkthroughCss = `
|
|
|
20987
21503
|
.knobs-wt-ocap{color:var(--_k-fg-2);max-width:var(--kw-measure)}
|
|
20988
21504
|
|
|
20989
21505
|
/* ——— the rail: progress, contents, export ——— */
|
|
20990
|
-
.knobs-wt-rail{position:sticky;top:
|
|
21506
|
+
.knobs-wt-rail{position:sticky;top:var(--_k-pad-page);display:flex;flex-direction:column;gap:var(--_k-space-title);max-height:calc(100vh - var(--_k-pad-page) * 2);min-height:0;font-size:var(--_k-type-small);line-height:var(--_k-leading-small)}
|
|
20991
21507
|
.knobs-wt-progress{display:flex;flex-direction:column;gap:var(--_k-space-sibling)}
|
|
20992
21508
|
.knobs-wt-count{display:flex;align-items:baseline;justify-content:space-between;color:var(--_k-fg-2)}
|
|
20993
21509
|
.knobs-wt-count b{color:var(--_k-fg);font-weight:var(--kw-strong)}
|
|
20994
21510
|
.knobs-wt-bar{position:relative;height:4px;border-radius:2px;overflow:hidden;background:var(--_k-fill)}
|
|
20995
21511
|
.knobs-wt-fill{position:absolute;inset:0;border-radius:inherit;background:var(--_k-accent);transform-origin:0 50%;transform:scaleX(var(--kw-done,0))}
|
|
20996
21512
|
.knobs-walkthrough[data-settled] .knobs-wt-fill{transition:transform var(--kw-calm-ms) var(--kw-calm)}
|
|
20997
|
-
/* The contents:
|
|
20998
|
-
steps under them (20 high, half of it), bled out so their text sits on the
|
|
20999
|
-
selection pill slides to the current step, and base rows never brighten
|
|
21513
|
+
/* The contents: every row on one text edge and in one colour, title rows (32 high, the control
|
|
21514
|
+
radius) and the steps under them (20 high, half of it), bled out so their text sits on the
|
|
21515
|
+
rail's edge; the selection pill slides to the current step, and base rows never brighten. */
|
|
21000
21516
|
.knobs-wt-toc{position:relative;flex:1 1 auto;min-height:0;overflow:auto;scrollbar-width:none;display:flex;flex-direction:column;gap:var(--_k-space-sibling);margin:0 calc(var(--_k-space-sibling) * -1);padding:0 0 8px}
|
|
21001
21517
|
.knobs-wt-toc::-webkit-scrollbar{display:none}
|
|
21002
21518
|
.knobs-wt-toc-group{display:flex;flex-direction:column;min-width:0}
|
|
21003
|
-
.knobs-ui .knobs-wt-toc-row{position:relative;display:grid;grid-template-columns:
|
|
21004
|
-
padding:0 var(--_k-space-sibling);border-radius:var(--_k-radius);color:var(--_k-fg-2);font-size:var(--_k-type-body);line-height:var(--_k-leading-body);font-weight:var(--
|
|
21519
|
+
.knobs-ui .knobs-wt-toc-row{position:relative;display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:var(--_k-space-sibling);align-items:center;flex:none;width:100%;height:32px;
|
|
21520
|
+
padding:0 var(--_k-space-sibling);border-radius:var(--_k-radius);color:var(--_k-fg-2);font-size:var(--_k-type-body);line-height:var(--_k-leading-body);font-weight:var(--_k-weight);text-align:left;white-space:nowrap;cursor:pointer;
|
|
21005
21521
|
transition:color calc(var(--_k-hover-out) * 1ms)}
|
|
21006
|
-
.knobs-ui .knobs-wt-toc-sub{height:20px;border-radius:calc(var(--_k-radius) / 2);font-size:var(--_k-type-small);line-height:var(--_k-leading-small);font-weight:var(--_k-weight)
|
|
21007
|
-
.knobs-wt-toc[data-bare] .knobs-wt-toc-row{grid-template-columns:minmax(0,1fr) auto}
|
|
21008
|
-
.knobs-wt-toc[data-bare] .knobs-wt-toc-n{display:none}
|
|
21522
|
+
.knobs-ui .knobs-wt-toc-sub{height:20px;border-radius:calc(var(--_k-radius) / 2);font-size:var(--_k-type-small);line-height:var(--_k-leading-small);font-weight:var(--_k-weight)}
|
|
21009
21523
|
.knobs-ui .knobs-wt-toc-row:is(:hover,:focus-visible){color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
|
|
21010
|
-
.knobs-ui .knobs-wt-toc-sub:is(:hover,:focus-visible){color:var(--_k-fg-2)}
|
|
21011
21524
|
.knobs-ui .knobs-wt-toc-row:focus-visible{box-shadow:inset 0 0 0 1px var(--_k-focus)}
|
|
21012
|
-
.knobs-wt-toc-
|
|
21013
|
-
.knobs-wt-toc-row[data-active] .knobs-wt-toc-n{color:var(--_k-fg-2)}
|
|
21525
|
+
.knobs-wt-toc-end{display:flex;align-items:center;font-size:var(--_k-type-small);line-height:var(--_k-leading-small);font-weight:var(--_k-weight);color:var(--_k-fg-3)}
|
|
21014
21526
|
.knobs-wt-toc-label{min-width:0;overflow:hidden;${fade2}}
|
|
21015
|
-
.knobs-pick-copy .knobs-wt-toc-
|
|
21527
|
+
.knobs-pick-copy .knobs-wt-toc-end{color:var(--_k-fg-2)}
|
|
21016
21528
|
/* A step's state: an open ring, a filled dot once answered, nothing to answer a faint ring. */
|
|
21017
21529
|
.knobs-wt-toc-glyph{--kw-tone:var(--_k-fg-3);display:block;width:6px;height:6px;border-radius:50%;box-shadow:inset 0 0 0 1.5px var(--kw-tone);
|
|
21018
21530
|
transition:background var(--kw-snappy-ms) var(--kw-snappy),box-shadow var(--kw-snappy-ms) var(--kw-snappy)}
|
|
@@ -21027,10 +21539,12 @@ var walkthroughCss = `
|
|
|
21027
21539
|
.knobs-ui .knobs-wt-btn[data-primary]:is(:hover,:focus-visible){background:color-mix(in srgb,var(--_k-fg) 16%,transparent)}
|
|
21028
21540
|
.knobs-wt-keys{color:var(--_k-fg-3);font-size:var(--_k-type-small);line-height:var(--_k-leading-small)}
|
|
21029
21541
|
.knobs-wt-keys kbd{font:inherit;color:var(--_k-fg-2)}
|
|
21030
|
-
/* Narrow: the rail becomes a bar along the top
|
|
21031
|
-
|
|
21542
|
+
/* Narrow: the contents go; with review on, the rail becomes a bar along the top with the
|
|
21543
|
+
progress and the export. */
|
|
21544
|
+
@container knobs-wt (max-width:960px){
|
|
21032
21545
|
.knobs-wt-rail{z-index:4;flex-direction:row;align-items:center;gap:var(--_k-space-title);max-height:none;margin:0 calc(var(--_k-pad-page) * -1);padding:8px var(--_k-pad-page);top:0;background:var(--_k-bg-page)}
|
|
21033
21546
|
.knobs-wt-progress{flex:1}.knobs-wt-toc,.knobs-wt-keys{display:none}
|
|
21547
|
+
.knobs-walkthrough:not([data-review]) .knobs-wt-rail{display:none}
|
|
21034
21548
|
.knobs-wt-step,.knobs-wt-shead,.knobs-wt-feed{scroll-margin-top:60px}
|
|
21035
21549
|
}
|
|
21036
21550
|
|
|
@@ -21040,9 +21554,14 @@ var walkthroughCss = `
|
|
|
21040
21554
|
well for hairlines, so nothing draws a fill and a ring of its own there ——— */
|
|
21041
21555
|
.knobs-ui.knobs-wt-slot{position:relative;display:flex;flex-direction:column;gap:var(--_k-space-sibling);min-width:0;container:knobs-wt-slot / inline-size;color:var(--_k-fg);
|
|
21042
21556
|
font:var(--_k-weight) var(--_k-type-body)/var(--_k-leading-body) var(--_k-font);font-variant-numeric:tabular-nums;--kw-measure:var(--k-walkthrough-measure,68ch);
|
|
21043
|
-
--kw-good:var(--k-walkthrough-good,var(--_k-ok));--kw-info:var(--k-walkthrough-info,var(--_k-info))}
|
|
21044
|
-
.knobs-wt-slot
|
|
21557
|
+
--kw-good:var(--k-walkthrough-good,var(--_k-ok));--kw-info:var(--k-walkthrough-info,var(--_k-info));--kw-frame-r:var(--_k-radius-panel)}
|
|
21558
|
+
.knobs-surface .knobs-wt-slot{--kw-frame-r:calc(var(--_k-radius-panel) - 4px)}
|
|
21559
|
+
/* A slot's label: sentence case as written, small and faint, never a heading. */
|
|
21560
|
+
.knobs-wt-slot-label{display:block;min-width:0;overflow:hidden;white-space:nowrap;font:var(--_k-weight) var(--_k-type-small)/var(--_k-leading-small) var(--_k-font);letter-spacing:normal;text-transform:none;color:var(--_k-fg-3);${fade2}}
|
|
21045
21561
|
.knobs-wt-body{min-width:0}
|
|
21562
|
+
/* One frame for every slot that has one (a picture, a video, a code block, a compare's stage, a
|
|
21563
|
+
slot still loading): the panel radius and a quiet fill, no ring; concentric on a card. */
|
|
21564
|
+
.knobs-wt-slot .knobs-cmp-stage{border-radius:var(--kw-frame-r)}
|
|
21046
21565
|
/* A compare is exactly as tall as its pictures at this width, so there's no letterbox: the two
|
|
21047
21566
|
sides edge to edge (side) or one (overlaid), plus its mode bar, the timeline and their two gaps
|
|
21048
21567
|
(92 px), and the frame strip with its gap (158 px). */
|
|
@@ -21050,11 +21569,11 @@ var walkthroughCss = `
|
|
|
21050
21569
|
.knobs-wt-fit[data-overlay]{height:calc(100cqw / var(--kw-aspect,1.7778) + var(--kw-chrome,92px))}
|
|
21051
21570
|
.knobs-wt-fit[data-strip]{--kw-chrome:158px}
|
|
21052
21571
|
.knobs-wt-fit[data-settled]{transition:height var(--k-wt-calm-ms,${calm.duration}ms) ${calm.easing}}
|
|
21053
|
-
.knobs-wt-frame{position:relative;width:100%;overflow:hidden;border-radius:
|
|
21572
|
+
.knobs-wt-frame{position:relative;width:100%;overflow:hidden;border-radius:var(--kw-frame-r);background:var(--_k-fill);aspect-ratio:var(--kw-aspect,16/9)}
|
|
21054
21573
|
.knobs-wt-frame>:is(video,img){position:absolute;inset:0;display:block;width:100%;height:100%;object-fit:contain;margin:0;border:0}
|
|
21055
21574
|
.knobs-wt-frame[data-natural]{aspect-ratio:auto;background:transparent}
|
|
21056
21575
|
.knobs-wt-frame[data-natural]>img{position:static;height:auto}
|
|
21057
|
-
.knobs-wt-code{margin:0;padding:var(--_k-pad-card);max-height:420px;overflow:auto;border-radius:
|
|
21576
|
+
.knobs-wt-code{margin:0;padding:var(--_k-pad-card);max-height:420px;overflow:auto;border-radius:var(--kw-frame-r);background:var(--_k-fill);color:var(--_k-fg);
|
|
21058
21577
|
font:var(--_k-type-label)/var(--_k-leading-body) var(--_k-mono);white-space:pre;tab-size:2;scrollbar-width:thin}
|
|
21059
21578
|
.knobs-surface .knobs-wt-code{padding-inline:0;border-radius:0;background:transparent;box-shadow:inset 0 1px 0 var(--_k-border-subtle),inset 0 -1px 0 var(--_k-border-subtle)}
|
|
21060
21579
|
.knobs-wt-code[data-terminal]{white-space:pre-wrap;overflow-wrap:anywhere}
|
|
@@ -21064,8 +21583,8 @@ var walkthroughCss = `
|
|
|
21064
21583
|
.knobs-wt-metric{display:flex;flex-direction:column;gap:var(--_k-space-sibling)}
|
|
21065
21584
|
.knobs-wt-mlabel{color:var(--_k-fg-2)}
|
|
21066
21585
|
.knobs-wt-mvalue{display:flex;align-items:baseline;flex-wrap:wrap;gap:var(--_k-space-sibling);font-variant-numeric:tabular-nums;font-feature-settings:"tnum"}
|
|
21067
|
-
.knobs-wt-mfrom{color:var(--_k-fg-3)
|
|
21068
|
-
.knobs-wt-mto{font-size:var(--_k-type-
|
|
21586
|
+
.knobs-wt-mfrom{color:var(--_k-fg-3)}
|
|
21587
|
+
.knobs-wt-mto{font-size:var(--_k-type-title);line-height:var(--_k-leading-title);font-weight:var(--_k-weight-label)}
|
|
21069
21588
|
.knobs-wt-mof{color:var(--_k-fg-3)}
|
|
21070
21589
|
.knobs-wt-mdelta{--kw-tone:var(--_k-fg-2);font-size:var(--_k-type-small);font-weight:var(--_k-weight-label);color:var(--kw-tone)}
|
|
21071
21590
|
.knobs-wt-mdelta[data-tone="good"]{--kw-tone:var(--kw-good)}
|
|
@@ -21080,7 +21599,7 @@ var walkthroughCss = `
|
|
|
21080
21599
|
.knobs-wt-callout[data-tone="warn"]{--kw-tone:var(--_k-warn)}
|
|
21081
21600
|
.knobs-wt-callout[data-tone="bad"]{--kw-tone:var(--_k-danger)}
|
|
21082
21601
|
.knobs-wt-text{max-width:var(--kw-measure);color:var(--_k-fg-2)}
|
|
21083
|
-
.knobs-wt-hold{border-radius:
|
|
21602
|
+
.knobs-wt-hold{border-radius:var(--kw-frame-r);background:var(--_k-fill)}
|
|
21084
21603
|
.knobs-wt-failed{display:flex;align-items:center;justify-content:center;min-height:80px;color:var(--_k-fg-3);font-size:var(--_k-type-small)}
|
|
21085
21604
|
|
|
21086
21605
|
/* ——— data blocks: history, stack, table, chapters. On the step's card, so no card of their own:
|
|
@@ -21192,7 +21711,7 @@ var walkthroughCss = `
|
|
|
21192
21711
|
.knobs-wt-table{max-height:520px;overflow:auto;overscroll-behavior-x:contain;outline:none;scrollbar-width:thin;scrollbar-color:transparent transparent;
|
|
21193
21712
|
transition:scrollbar-color calc(var(--_k-hover-out) * 1ms)}
|
|
21194
21713
|
.knobs-wt-table:is(:hover,:focus-visible){scrollbar-color:color-mix(in oklch,var(--_k-fg) 28%,transparent) transparent;transition-duration:calc(var(--_k-hover-in) * 1ms)}
|
|
21195
|
-
.knobs-wt-table:focus-visible{box-shadow:inset 0 0 0 2px var(--_k-focus);border-radius:
|
|
21714
|
+
.knobs-wt-table:focus-visible{box-shadow:inset 0 0 0 2px var(--_k-focus);border-radius:var(--kw-frame-r)}
|
|
21196
21715
|
@supports (animation-timeline:scroll()){
|
|
21197
21716
|
.knobs-wt-table{animation:knobs-fade-a linear both,knobs-fade-b linear both,kw-fade-l linear both,kw-fade-r linear both;animation-duration:auto;
|
|
21198
21717
|
animation-timeline:scroll(self block),scroll(self block),scroll(self inline),scroll(self inline);animation-range:0 24px,calc(100% - 24px) 100%,0 8px,calc(100% - 8px) 100%;
|
|
@@ -21806,16 +22325,25 @@ function tasteTest(container, options) {
|
|
|
21806
22325
|
...o.media !== undefined ? { media: fillMedia(o.media) } : {},
|
|
21807
22326
|
suggested: o.suggested === true
|
|
21808
22327
|
});
|
|
21809
|
-
|
|
22328
|
+
const pickable = options.pickable !== false;
|
|
22329
|
+
let picked = pickable ? options.picked ?? null : null;
|
|
21810
22330
|
const root = el2(doc, "div", "knobs-wt-options");
|
|
21811
|
-
|
|
21812
|
-
|
|
22331
|
+
if (pickable) {
|
|
22332
|
+
root.setAttribute("role", "radiogroup");
|
|
22333
|
+
root.setAttribute("aria-label", "Options");
|
|
22334
|
+
} else
|
|
22335
|
+
root.dataset.static = "";
|
|
21813
22336
|
root.style.setProperty("--kw-n", String(Math.min(list.length, 3)));
|
|
21814
22337
|
if (!container.closest(".knobs-ui"))
|
|
21815
22338
|
applyLook2(root, options);
|
|
21816
22339
|
const slots = [];
|
|
21817
22340
|
const tiles = list.map((o, i) => {
|
|
21818
22341
|
const tile = el2(doc, "div", "knobs-wt-option");
|
|
22342
|
+
if (!pickable) {
|
|
22343
|
+
const name = el2(doc, "span", "knobs-wt-slot-label");
|
|
22344
|
+
name.textContent = o.label;
|
|
22345
|
+
tile.append(name);
|
|
22346
|
+
}
|
|
21819
22347
|
const pick = el2(doc, "button", "knobs-wt-pick");
|
|
21820
22348
|
pick.type = "button";
|
|
21821
22349
|
pick.setAttribute("role", "radio");
|
|
@@ -21844,7 +22372,8 @@ function tasteTest(container, options) {
|
|
|
21844
22372
|
api.pick(next);
|
|
21845
22373
|
options.onPick?.(next);
|
|
21846
22374
|
});
|
|
21847
|
-
|
|
22375
|
+
if (pickable)
|
|
22376
|
+
tile.append(pick);
|
|
21848
22377
|
if (o.media)
|
|
21849
22378
|
slots.push(mediaSlot(tile, {
|
|
21850
22379
|
...look,
|
|
@@ -21927,7 +22456,8 @@ function walkthrough(container, options) {
|
|
|
21927
22456
|
let tocRows = [];
|
|
21928
22457
|
let discs = [];
|
|
21929
22458
|
let full = null;
|
|
21930
|
-
const
|
|
22459
|
+
const review = options.review === true;
|
|
22460
|
+
const noteMode = review ? options.noteBar ?? "collapsed" : "hidden";
|
|
21931
22461
|
let reserve = noteBarReserve(52, DOCK_INSET, DOCK_GAP);
|
|
21932
22462
|
const viewOf = (key) => views.find((v) => v.step.key === key);
|
|
21933
22463
|
const feedback = () => collectFeedback(page, state);
|
|
@@ -22663,7 +23193,14 @@ function walkthrough(container, options) {
|
|
|
22663
23193
|
line.append(s);
|
|
22664
23194
|
return s;
|
|
22665
23195
|
};
|
|
23196
|
+
const findPRIn = (n) => findPR(page.prs, n, page.repo);
|
|
22666
23197
|
const metaLink = (line, text, href) => {
|
|
23198
|
+
const n = prNumber(href);
|
|
23199
|
+
if (n) {
|
|
23200
|
+
const chip = prChip(doc, n, { href, ...findPRIn(n) });
|
|
23201
|
+
line.append(chip);
|
|
23202
|
+
return chip;
|
|
23203
|
+
}
|
|
22667
23204
|
const a = el2(doc, "a");
|
|
22668
23205
|
a.href = href;
|
|
22669
23206
|
a.target = "_blank";
|
|
@@ -22750,8 +23287,9 @@ function walkthrough(container, options) {
|
|
|
22750
23287
|
views = [];
|
|
22751
23288
|
root.textContent = "";
|
|
22752
23289
|
root.removeAttribute("data-settled");
|
|
22753
|
-
root.dataset.rail = options.rail === false ? "none" : options.rail === "
|
|
23290
|
+
root.dataset.rail = options.rail === false ? "none" : options.rail === "right" ? "right" : "left";
|
|
22754
23291
|
root.toggleAttribute("data-cards", options.cards === true);
|
|
23292
|
+
root.toggleAttribute("data-review", review);
|
|
22755
23293
|
const layout = el2(doc, "div", "knobs-wt-layout");
|
|
22756
23294
|
const main = el2(doc, "div", "knobs-wt-main");
|
|
22757
23295
|
const flow = el2(doc, "div", "knobs-wt-flow");
|
|
@@ -22761,6 +23299,8 @@ function walkthrough(container, options) {
|
|
|
22761
23299
|
head.append(h1);
|
|
22762
23300
|
if (page.summary)
|
|
22763
23301
|
head.append(prose2(doc, "p", "knobs-wt-summary", page.summary));
|
|
23302
|
+
if (page.more)
|
|
23303
|
+
head.append(more(page.more));
|
|
22764
23304
|
if (page.meta.length) {
|
|
22765
23305
|
const meta = el2(doc, "div", "knobs-wt-meta");
|
|
22766
23306
|
for (const m of page.meta) {
|
|
@@ -22804,28 +23344,28 @@ function walkthrough(container, options) {
|
|
|
22804
23344
|
count = fill = tocEl = null;
|
|
22805
23345
|
if (options.rail !== false) {
|
|
22806
23346
|
rail = el2(doc, "aside", "knobs-wt-rail");
|
|
22807
|
-
rail.setAttribute("aria-label", "
|
|
22808
|
-
const progress = el2(doc, "div", "knobs-wt-progress");
|
|
22809
|
-
count = el2(doc, "div", "knobs-wt-count");
|
|
22810
|
-
const meter = el2(doc, "div", "knobs-wt-bar");
|
|
22811
|
-
fill = el2(doc, "div", "knobs-wt-fill");
|
|
22812
|
-
meter.append(fill);
|
|
22813
|
-
progress.append(count, meter);
|
|
23347
|
+
rail.setAttribute("aria-label", "Contents");
|
|
22814
23348
|
tocEl = el2(doc, "nav", "knobs-wt-toc");
|
|
22815
23349
|
tocEl.setAttribute("aria-label", "Contents");
|
|
22816
|
-
const ns = page.sections.map((s) => s.kicker && s.link ? s.link.label : "");
|
|
22817
|
-
const nw = Math.max(0, ...ns.map((n) => n.length));
|
|
22818
|
-
tocEl.style.setProperty("--kw-toc-n", nw ? `${nw + 0.5}ch` : "0px");
|
|
22819
|
-
if (!nw)
|
|
22820
|
-
tocEl.dataset.bare = "";
|
|
22821
23350
|
if (feedEl) {
|
|
22822
23351
|
const group = el2(doc, "div", "knobs-wt-toc-group");
|
|
22823
23352
|
const target = feedEl;
|
|
22824
|
-
group.append(tocRow("Latest",
|
|
23353
|
+
group.append(tocRow("Latest", false, () => target.scrollIntoView({ block: "start", behavior: reduced() ? "auto" : "smooth" })));
|
|
22825
23354
|
tocEl.append(group);
|
|
22826
23355
|
}
|
|
22827
|
-
|
|
22828
|
-
|
|
23356
|
+
if (review) {
|
|
23357
|
+
const progress = el2(doc, "div", "knobs-wt-progress");
|
|
23358
|
+
count = el2(doc, "div", "knobs-wt-count");
|
|
23359
|
+
const meter = el2(doc, "div", "knobs-wt-bar");
|
|
23360
|
+
fill = el2(doc, "div", "knobs-wt-fill");
|
|
23361
|
+
meter.append(fill);
|
|
23362
|
+
progress.append(count, meter);
|
|
23363
|
+
rail.append(progress);
|
|
23364
|
+
}
|
|
23365
|
+
rail.append(tocEl);
|
|
23366
|
+
if (review)
|
|
23367
|
+
rail.append(actions(true));
|
|
23368
|
+
if (review && options.keys !== false) {
|
|
22829
23369
|
const keys = el2(doc, "div", "knobs-wt-keys");
|
|
22830
23370
|
keys.innerHTML = "<kbd>j</kbd> <kbd>k</kbd> steps · <kbd>←</kbd> <kbd>→</kbd> seek · <kbd>f</kbd> full · <kbd>n</kbd> note" + (page.steps.some((s) => s.ask === "pick") ? " · <kbd>1</kbd>–<kbd>9</kbd> pick" : "");
|
|
22831
23371
|
rail.append(keys);
|
|
@@ -22835,14 +23375,15 @@ function walkthrough(container, options) {
|
|
|
22835
23375
|
const sec = el2(doc, "section", "knobs-wt-section");
|
|
22836
23376
|
sec.id = `wt-${section.id}`;
|
|
22837
23377
|
const sh = el2(doc, "header", "knobs-wt-shead");
|
|
22838
|
-
|
|
23378
|
+
const status = review ? section.status : undefined;
|
|
23379
|
+
if (section.kicker || section.link || status) {
|
|
22839
23380
|
const meta = metaLine("knobs-wt-kicker");
|
|
22840
23381
|
if (section.kicker)
|
|
22841
23382
|
metaText(meta, section.kicker, "knobs-wt-kicker-lead");
|
|
22842
23383
|
if (section.link)
|
|
22843
23384
|
metaLink(meta, section.link.label, section.link.href);
|
|
22844
|
-
if (
|
|
22845
|
-
const s = statusLook(
|
|
23385
|
+
if (status) {
|
|
23386
|
+
const s = statusLook(status);
|
|
22846
23387
|
metaText(meta, s.label, "knobs-wt-status").dataset.tone = s.tone;
|
|
22847
23388
|
}
|
|
22848
23389
|
sh.append(meta);
|
|
@@ -22854,13 +23395,14 @@ function walkthrough(container, options) {
|
|
|
22854
23395
|
intro.append(sh);
|
|
22855
23396
|
if (section.summary)
|
|
22856
23397
|
intro.append(prose2(doc, "p", "knobs-wt-ssum", section.summary));
|
|
23398
|
+
if (section.more)
|
|
23399
|
+
intro.append(more(section.more));
|
|
22857
23400
|
sec.append(intro);
|
|
22858
23401
|
let group = null;
|
|
22859
23402
|
if (tocEl) {
|
|
22860
23403
|
group = el2(doc, "div", "knobs-wt-toc-group");
|
|
22861
23404
|
const first = section.steps[0]?.key;
|
|
22862
|
-
|
|
22863
|
-
group.append(tocRow(plainMarkdown(section.title), n, "", false, () => first ? go(first) : undefined, section.id));
|
|
23405
|
+
group.append(tocRow(plainMarkdown(section.title), false, () => first ? go(first) : undefined, section.id));
|
|
22864
23406
|
tocEl.append(group);
|
|
22865
23407
|
}
|
|
22866
23408
|
for (const step of section.steps) {
|
|
@@ -22870,6 +23412,7 @@ function walkthrough(container, options) {
|
|
|
22870
23412
|
}
|
|
22871
23413
|
flow.append(sec);
|
|
22872
23414
|
}
|
|
23415
|
+
linkPRs(flow, findPRIn);
|
|
22873
23416
|
main.append(flow);
|
|
22874
23417
|
if (noteMode !== "hidden")
|
|
22875
23418
|
main.append(bar.dock);
|
|
@@ -22898,19 +23441,25 @@ function walkthrough(container, options) {
|
|
|
22898
23441
|
root.dataset.settled = "";
|
|
22899
23442
|
}));
|
|
22900
23443
|
};
|
|
22901
|
-
const
|
|
23444
|
+
const more = (text) => {
|
|
23445
|
+
const box = el2(doc, "div", "knobs-wt-more");
|
|
23446
|
+
const body = el2(doc, "div", "knobs-wt-more-text");
|
|
23447
|
+
for (const para of text.split(/\n\s*\n/))
|
|
23448
|
+
if (para.trim())
|
|
23449
|
+
body.append(prose2(doc, "p", "", para.trim()));
|
|
23450
|
+
discs.push(disclosure(box, { label: "More", content: body }));
|
|
23451
|
+
return box;
|
|
23452
|
+
};
|
|
23453
|
+
const tocRow = (label, sub, run, section) => {
|
|
22902
23454
|
const b = el2(doc, "button", sub ? "knobs-wt-toc-row knobs-wt-toc-sub" : "knobs-wt-toc-row");
|
|
22903
23455
|
b.type = "button";
|
|
22904
23456
|
if (section)
|
|
22905
23457
|
b.dataset.section = section;
|
|
22906
|
-
const ncol = el2(doc, "span", "knobs-wt-toc-n");
|
|
22907
|
-
ncol.textContent = n;
|
|
22908
23458
|
const t = el2(doc, "span", "knobs-wt-toc-label");
|
|
22909
23459
|
t.textContent = label;
|
|
22910
23460
|
const e = el2(doc, "span", "knobs-wt-toc-end");
|
|
22911
|
-
e.textContent = end;
|
|
22912
23461
|
b.title = label;
|
|
22913
|
-
b.append(
|
|
23462
|
+
b.append(t, e);
|
|
22914
23463
|
b.addEventListener("mousedown", (ev) => ev.preventDefault());
|
|
22915
23464
|
b.addEventListener("click", run);
|
|
22916
23465
|
tocRows.push(b);
|
|
@@ -22968,33 +23517,26 @@ function walkthrough(container, options) {
|
|
|
22968
23517
|
head.append(h3);
|
|
22969
23518
|
if (step.caption)
|
|
22970
23519
|
head.append(prose2(doc, "p", "knobs-wt-caption", step.caption));
|
|
23520
|
+
if (step.more)
|
|
23521
|
+
head.append(more(step.more));
|
|
22971
23522
|
card.append(head);
|
|
22972
23523
|
const slots = [];
|
|
22973
23524
|
const onEvent = (e) => record({ ...e, step: step.key });
|
|
22974
23525
|
let media = null;
|
|
22975
23526
|
if (step.media.length) {
|
|
22976
|
-
|
|
22977
|
-
|
|
22978
|
-
|
|
22979
|
-
|
|
22980
|
-
slots.push(mediaSlot(g, { ...look, media: m, onEvent, steps: stepLinks }));
|
|
22981
|
-
return g;
|
|
22982
|
-
};
|
|
22983
|
-
const [first, ...rest] = step.media;
|
|
22984
|
-
media = grid(step.more ? [first] : step.media);
|
|
23527
|
+
media = el2(doc, "div", "knobs-wt-media");
|
|
23528
|
+
media.style.setProperty("--kw-n", String(Math.min(step.media.length, step.columns)));
|
|
23529
|
+
for (const m of step.media)
|
|
23530
|
+
slots.push(mediaSlot(media, { ...look, media: m, onEvent, steps: stepLinks }));
|
|
22985
23531
|
card.append(media);
|
|
22986
|
-
if (step.more && rest.length) {
|
|
22987
|
-
const holder = el2(doc, "div", "knobs-wt-more");
|
|
22988
|
-
discs.push(disclosure(holder, { label: step.more, count: rest.length, content: grid(rest) }));
|
|
22989
|
-
card.append(holder);
|
|
22990
|
-
}
|
|
22991
23532
|
}
|
|
22992
23533
|
let taste = null;
|
|
22993
|
-
if (step.question)
|
|
23534
|
+
if (step.question && review)
|
|
22994
23535
|
card.append(prose2(doc, "p", "knobs-wt-question", step.question));
|
|
22995
23536
|
if (step.ask === "pick") {
|
|
22996
23537
|
taste = tasteTest(card, {
|
|
22997
23538
|
...look,
|
|
23539
|
+
pickable: review,
|
|
22998
23540
|
options: step.options,
|
|
22999
23541
|
picked: state.steps[step.key]?.pick ?? null,
|
|
23000
23542
|
onPick: (id) => pick(step.key, id),
|
|
@@ -23003,7 +23545,8 @@ function walkthrough(container, options) {
|
|
|
23003
23545
|
media ??= taste.element;
|
|
23004
23546
|
}
|
|
23005
23547
|
const notes = el2(doc, "div", "knobs-wt-notes");
|
|
23006
|
-
|
|
23548
|
+
if (review)
|
|
23549
|
+
card.append(notes);
|
|
23007
23550
|
card.addEventListener("focusin", () => {
|
|
23008
23551
|
if (scroll.to)
|
|
23009
23552
|
return;
|
|
@@ -23013,9 +23556,11 @@ function walkthrough(container, options) {
|
|
|
23013
23556
|
let toc = null;
|
|
23014
23557
|
let glyph = null;
|
|
23015
23558
|
if (group) {
|
|
23016
|
-
toc = tocRow(plainMarkdown(step.title),
|
|
23017
|
-
|
|
23018
|
-
|
|
23559
|
+
toc = tocRow(plainMarkdown(step.title), true, () => go(step.key));
|
|
23560
|
+
if (review) {
|
|
23561
|
+
glyph = el2(doc, "span", "knobs-wt-toc-glyph");
|
|
23562
|
+
toc.querySelector(".knobs-wt-toc-end").append(glyph);
|
|
23563
|
+
}
|
|
23019
23564
|
group.append(toc);
|
|
23020
23565
|
}
|
|
23021
23566
|
return { step, card, media, notes, toc, glyph, taste, slots };
|
|
@@ -23093,7 +23638,7 @@ function walkthrough(container, options) {
|
|
|
23093
23638
|
return;
|
|
23094
23639
|
}
|
|
23095
23640
|
const v = viewOf(current);
|
|
23096
|
-
if (!v || e.shiftKey)
|
|
23641
|
+
if (!v || e.shiftKey || !review)
|
|
23097
23642
|
return;
|
|
23098
23643
|
const choice = digitChoice(v.step, state.steps[v.step.key], key);
|
|
23099
23644
|
if (choice === undefined)
|
|
@@ -23258,6 +23803,7 @@ export {
|
|
|
23258
23803
|
mediaKind,
|
|
23259
23804
|
mediaSlot,
|
|
23260
23805
|
menu,
|
|
23806
|
+
minutes,
|
|
23261
23807
|
modeForKey,
|
|
23262
23808
|
motionBlur,
|
|
23263
23809
|
moveHead,
|
|
@@ -23277,6 +23823,7 @@ export {
|
|
|
23277
23823
|
plainAccess,
|
|
23278
23824
|
planBento,
|
|
23279
23825
|
planPill,
|
|
23826
|
+
player,
|
|
23280
23827
|
popover,
|
|
23281
23828
|
railSegments,
|
|
23282
23829
|
rank,
|