@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-solid.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",
|
|
@@ -3003,8 +3035,6 @@ var css2 = `
|
|
|
3003
3035
|
.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}
|
|
3004
3036
|
.knobs-tl-track:hover .knobs-tl-hover{opacity:1}
|
|
3005
3037
|
.knobs-timeline[data-scrubbing] .knobs-tl-hover{opacity:0}
|
|
3006
|
-
@media(prefers-reduced-motion:reduce){.knobs-timeline :where(*,::before,::after){transition:none!important}}
|
|
3007
|
-
@media(prefers-reduced-motion:reduce){:is(.knobs-tree,.knobs-split>.knobs-divider,.knobs-pane-head,.knobs-pane-rail) :where(*,::before,::after){transition:none!important}}
|
|
3008
3038
|
.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}
|
|
3009
3039
|
.knobs-tl-tag[data-on]{opacity:1}
|
|
3010
3040
|
/* The lane (variant: "lane"): one slim track of ticks under a compare or a video; markers are
|
|
@@ -3020,7 +3050,26 @@ var css2 = `
|
|
|
3020
3050
|
.knobs-timeline[data-variant="lane"] .knobs-tl-head::before{top:0}
|
|
3021
3051
|
.knobs-timeline[data-variant="lane"] .knobs-tl-head::after{content:none}
|
|
3022
3052
|
.knobs-timeline[data-variant="lane"] .knobs-tl-tag{top:2px;bottom:auto}
|
|
3023
|
-
|
|
3053
|
+
/* The line (variant: "line"): a player's progress under its media. One 2px line, fg-3 with fg
|
|
3054
|
+
filling it, a segment per chapter; it thickens under the pointer and while scrubbing. The
|
|
3055
|
+
chapter and time under the pointer float over it; nothing else shows. */
|
|
3056
|
+
.knobs-ui.knobs-timeline[data-variant="line"]{height:auto;min-height:0;padding:0;gap:0}
|
|
3057
|
+
.knobs-timeline[data-variant="line"] .knobs-tl-transport{display:none}
|
|
3058
|
+
.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}
|
|
3059
|
+
.knobs-timeline[data-variant="line"] :is(.knobs-tl-ruler,.knobs-tl-lane,.knobs-tl-hover,.knobs-tl-head,.knobs-tl-rlabel){display:none}
|
|
3060
|
+
.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)}
|
|
3061
|
+
.knobs-tl-seg{position:absolute;left:0;top:0;height:100%;overflow:hidden;border-radius:1px;background:var(--_k-fg-3);will-change:transform}
|
|
3062
|
+
.knobs-tl-seg-fill{position:absolute;inset:0;background:var(--_k-fg);transform-origin:0 0;will-change:transform}
|
|
3063
|
+
.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}
|
|
3064
|
+
.knobs-timeline[data-variant="line"] .knobs-tl-range{top:3px;bottom:3px;border-radius:3px}
|
|
3065
|
+
.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)}
|
|
3066
|
+
.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);
|
|
3067
|
+
-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:inset 0 0 0 .5px var(--_k-border)}
|
|
3068
|
+
.knobs-tl-tag-label{color:var(--_k-fg-2)}
|
|
3069
|
+
.knobs-tl-tag-label:empty{display:none}
|
|
3070
|
+
@media(prefers-reduced-motion:reduce){.knobs-timeline :where(*,::before,::after){transition:none!important}}
|
|
3071
|
+
@media(prefers-reduced-motion:reduce){:is(.knobs-tree,.knobs-split>.knobs-divider,.knobs-pane-head,.knobs-pane-rail) :where(*,::before,::after){transition:none!important}}
|
|
3072
|
+
`, uiCss = () => aliased(css2 + readoutCss + overlayCss + bentoCss + activityCss + pickerCss + compareCss + playerCss + selectionPillCss) + rolling_css_default, lookDefaults, tune = (s, m) => {
|
|
3024
3073
|
s.omega = m.omega;
|
|
3025
3074
|
s.zeta = m.zeta;
|
|
3026
3075
|
return s;
|
|
@@ -3143,19 +3192,19 @@ var init_diff_css = __esm(() => {
|
|
|
3143
3192
|
@property --kd-fb{syntax:"<length>";inherits:false;initial-value:0px}
|
|
3144
3193
|
: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))}
|
|
3145
3194
|
:where(.knobs-diff,.knobs-diff *,.knobs-diff-files,.knobs-diff-files *){box-sizing:border-box}
|
|
3146
|
-
.knobs-ui.knobs-diff{--kd-bg:var(--k-diff-bg,var(--_kd-bg,
|
|
3195
|
+
.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);
|
|
3147
3196
|
--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);
|
|
3148
3197
|
--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;
|
|
3149
3198
|
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;
|
|
3150
3199
|
letter-spacing:normal;word-spacing:normal;text-transform:none;-webkit-font-smoothing:antialiased}
|
|
3151
3200
|
.knobs-ui.knobs-diff[data-height="auto"]{height:auto}
|
|
3152
|
-
.knobs-diff::after{content:"";position:absolute;inset:0;z-index:3;
|
|
3201
|
+
.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}
|
|
3153
3202
|
.knobs-diff [hidden],.knobs-diff-files [hidden]{display:none!important}
|
|
3154
3203
|
.knobs-diff svg{display:block;flex:none}
|
|
3155
|
-
.knobs-ui.knobs-diff[data-on="
|
|
3156
|
-
/*
|
|
3204
|
+
.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)}
|
|
3205
|
+
/* A hairline under the header, firmer once the code has scrolled under it. */
|
|
3157
3206
|
.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);
|
|
3158
|
-
box-shadow:inset 0 -1px
|
|
3207
|
+
box-shadow:inset 0 -1px var(--_k-border-subtle);transition:box-shadow .15s;user-select:none;-webkit-user-select:none}
|
|
3159
3208
|
.knobs-diff[data-scrolled]>.knobs-diff-head{box-shadow:inset 0 -1px var(--_k-border)}
|
|
3160
3209
|
.knobs-diff-path{flex:0 1 auto;min-width:0;overflow:hidden;white-space:nowrap}
|
|
3161
3210
|
.knobs-diff-dir,.knobs-diff-from{color:var(--_k-fg-3)}
|
|
@@ -3193,6 +3242,15 @@ var init_diff_css = __esm(() => {
|
|
|
3193
3242
|
.knobs-ui .knobs-diff-viewed[aria-pressed="true"]{color:var(--_k-fg);background:var(--_ku-wash)}
|
|
3194
3243
|
.knobs-diff-viewed svg{width:16px;height:16px}
|
|
3195
3244
|
.knobs-diff-viewed[aria-pressed="true"] svg{color:var(--kd-add)}
|
|
3245
|
+
/* Narrow, the header gives up words before anything is cut off at its end: Viewed keeps its
|
|
3246
|
+
check, then the counts' bar goes, then the counts; the path fades at its end throughout. */
|
|
3247
|
+
.knobs-diff-head,.knobs-diff-part-head{container-type:inline-size}
|
|
3248
|
+
@container (max-width:520px){
|
|
3249
|
+
.knobs-diff-viewed-label{display:none}
|
|
3250
|
+
.knobs-ui .knobs-diff-viewed{width:var(--_k-control-h);padding:0;justify-content:center}
|
|
3251
|
+
.knobs-diff-counts .knobs-diff-bar{display:none}
|
|
3252
|
+
}
|
|
3253
|
+
@container (max-width:400px){.knobs-diff-counts{display:none}.knobs-diff-nav>span{min-width:2.6em}}
|
|
3196
3254
|
.knobs-diff-path{transition:opacity .2s}
|
|
3197
3255
|
.knobs-diff[data-viewed]>.knobs-diff-head .knobs-diff-path,.knobs-diff-part[data-viewed] .knobs-diff-path{opacity:.6}
|
|
3198
3256
|
/* Split | Unified: the selection pill on the one picked, its bright copy the picked one's text; a
|
|
@@ -3257,24 +3315,26 @@ var init_diff_css = __esm(() => {
|
|
|
3257
3315
|
--diffs-bg-deletion-number-override:color-mix(in srgb,var(--kd-del) 55%,var(--kd-solid));
|
|
3258
3316
|
--diffs-bg-addition-emphasis-override:color-mix(in srgb,var(--kd-add) 22%,transparent);
|
|
3259
3317
|
--diffs-bg-deletion-emphasis-override:color-mix(in srgb,var(--kd-del) 22%,transparent);
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
--diffs-bg-
|
|
3318
|
+
/* Unchanged lines and the folds between hunks sit on the void; only a split's missing lines
|
|
3319
|
+
are faintly filled. */
|
|
3320
|
+
--diffs-bg-separator-override:var(--kd-solid);
|
|
3321
|
+
--diffs-bg-context-override:var(--kd-solid);
|
|
3322
|
+
--diffs-bg-buffer-override:color-mix(in srgb,var(--_k-fg) 2.5%,var(--kd-solid));
|
|
3263
3323
|
--diffs-bg-selection-override:var(--_k-accent);
|
|
3264
3324
|
--diffs-fg-number-override:var(--_k-fg-3);
|
|
3265
3325
|
}
|
|
3266
|
-
/* diffReview
|
|
3267
|
-
is pinned to its right edge, so it slides out from under the diff), parted
|
|
3268
|
-
|
|
3269
|
-
.knobs-ui.knobs-diff-review{position:relative;display:flex;width:100%;height:100%;min-width:0;min-height:0;overflow:hidden;
|
|
3326
|
+
/* diffReview, on the void as the diff is: the file list a drawer at its left (its width springs;
|
|
3327
|
+
the list inside is pinned to its right edge, so it slides out from under the diff), parted
|
|
3328
|
+
from the diff by a hairline; one hairline runs under both headers and one under it all. */
|
|
3329
|
+
.knobs-ui.knobs-diff-review{position:relative;display:flex;width:100%;height:100%;min-width:0;min-height:0;overflow:hidden;
|
|
3270
3330
|
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}
|
|
3271
|
-
.knobs-diff-review::after{content:"";position:absolute;inset:0;z-index:5;
|
|
3331
|
+
.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}
|
|
3272
3332
|
:where(.knobs-diff-review,.knobs-diff-review *){box-sizing:border-box}
|
|
3273
|
-
.knobs-diff-drawer{position:relative;flex:none;width:0;height:100%;overflow:hidden;box-shadow:inset -1px 0 var(--_k-border)}
|
|
3333
|
+
.knobs-diff-drawer{position:relative;flex:none;width:0;height:100%;overflow:hidden;box-shadow:inset -1px 0 var(--_k-border-subtle)}
|
|
3274
3334
|
.knobs-diff-drawer[data-shut]{visibility:hidden}
|
|
3275
3335
|
.knobs-diff-drawer-in{position:absolute;top:0;bottom:0;right:0;display:flex;flex-direction:column}
|
|
3276
3336
|
.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);
|
|
3277
|
-
user-select:none;-webkit-user-select:none}
|
|
3337
|
+
box-shadow:inset 0 -1px var(--_k-border-subtle);user-select:none;-webkit-user-select:none}
|
|
3278
3338
|
.knobs-diff-drawer-title{font-weight:var(--_k-weight-title)}
|
|
3279
3339
|
.knobs-diff-drawer-count{color:var(--_k-fg-3);font-size:var(--_k-text-meta);font-variant-numeric:tabular-nums}
|
|
3280
3340
|
.knobs-diff-drawer-in>.knobs-diff-files{flex:1;min-height:0;height:auto}
|
|
@@ -3286,10 +3346,11 @@ var init_diff_css = __esm(() => {
|
|
|
3286
3346
|
.knobs-ui .knobs-diff-toggle[aria-pressed]:not(:hover,:focus-visible){background:transparent}
|
|
3287
3347
|
/* diffReview's file list: a filter over a compact tree, a status letter, the counts and their
|
|
3288
3348
|
bar on each file; one highlight glides to the file on show. Viewed files dim, with a tick. */
|
|
3289
|
-
.knobs-diff-files{--k-row:var(--k-diff-row,28px);display:flex;flex-direction:column;height:100%;min-height:0;padding-top:
|
|
3349
|
+
.knobs-diff-files{--k-row:var(--k-diff-row,28px);display:flex;flex-direction:column;height:100%;min-height:0;padding-top:4px}
|
|
3290
3350
|
.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;
|
|
3291
|
-
border-radius:max(6px,var(--_ku-row-radius));
|
|
3292
|
-
|
|
3351
|
+
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}
|
|
3352
|
+
/* The filter is a line of text, no box; focused, a hairline ring. */
|
|
3353
|
+
.knobs-diff-filter:focus-within{box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--_k-accent) 45%,transparent)}
|
|
3293
3354
|
.knobs-diff-filter svg{width:12px;height:12px;flex:none}
|
|
3294
3355
|
.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}
|
|
3295
3356
|
.knobs-ui .knobs-diff-filter>input::placeholder{color:var(--_k-fg-3)}
|
|
@@ -6214,7 +6275,10 @@ function codeDiff(container, options) {
|
|
|
6214
6275
|
const viewedToggle = () => {
|
|
6215
6276
|
const b = make("button", "knobs-diff-viewed");
|
|
6216
6277
|
b.type = "button";
|
|
6217
|
-
|
|
6278
|
+
const label = make("span", "knobs-diff-viewed-label");
|
|
6279
|
+
label.textContent = "Viewed";
|
|
6280
|
+
b.append(make("span", ""), label);
|
|
6281
|
+
b.setAttribute("aria-label", "Viewed");
|
|
6218
6282
|
return b;
|
|
6219
6283
|
};
|
|
6220
6284
|
const paintViewed = (b, on) => {
|
|
@@ -8501,6 +8565,17 @@ function currentMarker(markers, t, fps) {
|
|
|
8501
8565
|
best = m;
|
|
8502
8566
|
return best;
|
|
8503
8567
|
}
|
|
8568
|
+
function minutes(t) {
|
|
8569
|
+
const s = Math.max(0, Math.floor(t + 0.000001));
|
|
8570
|
+
const h = Math.floor(s / 3600);
|
|
8571
|
+
const m = Math.floor(s % 3600 / 60);
|
|
8572
|
+
const ss = String(s % 60).padStart(2, "0");
|
|
8573
|
+
return h > 0 ? `${h}:${String(m).padStart(2, "0")}:${ss}` : `${m}:${ss}`;
|
|
8574
|
+
}
|
|
8575
|
+
function chapterBounds(times, duration) {
|
|
8576
|
+
const inside = times.filter((t) => t > 0.000001 && t < duration - 0.000001);
|
|
8577
|
+
return [0, ...[...new Set(inside)].sort((a, b) => a - b), duration];
|
|
8578
|
+
}
|
|
8504
8579
|
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;
|
|
8505
8580
|
var init_timeline_model = __esm(() => {
|
|
8506
8581
|
SECOND_STEPS = [0.1, 0.2, 0.25, 0.5, 1, 2, 5, 10, 15, 30, 60, 120, 300, 600, 900, 1800, 3600];
|
|
@@ -8518,7 +8593,8 @@ function timeline(container, options) {
|
|
|
8518
8593
|
let speed = options.speed ?? 1;
|
|
8519
8594
|
let repeat = options.repeat ?? true;
|
|
8520
8595
|
const arrows = options.arrows ?? "frames";
|
|
8521
|
-
const
|
|
8596
|
+
const line = options.variant === "line";
|
|
8597
|
+
const lane = line || options.variant === "lane";
|
|
8522
8598
|
let time = Math.min(duration, Math.max(0, options.time ?? 0));
|
|
8523
8599
|
let playing = false;
|
|
8524
8600
|
let markers = [...options.markers ?? []];
|
|
@@ -8553,7 +8629,7 @@ function timeline(container, options) {
|
|
|
8553
8629
|
root.setAttribute("aria-keyshortcuts", "Space ArrowLeft ArrowRight , . Home End [ ] I O M");
|
|
8554
8630
|
applyLook2(root, options);
|
|
8555
8631
|
if (lane)
|
|
8556
|
-
root.dataset.variant = "lane";
|
|
8632
|
+
root.dataset.variant = line ? "line" : "lane";
|
|
8557
8633
|
const transport = el("div", "knobs-tl-transport");
|
|
8558
8634
|
const playBtn = el("button", "knobs-tl-play");
|
|
8559
8635
|
playBtn.type = "button";
|
|
@@ -8566,6 +8642,10 @@ function timeline(container, options) {
|
|
|
8566
8642
|
const hover = el("div", "knobs-tl-hover");
|
|
8567
8643
|
const tag = el("span", "knobs-tl-tag");
|
|
8568
8644
|
tag.setAttribute("aria-hidden", "true");
|
|
8645
|
+
const tagLabel = el("span", "knobs-tl-tag-label");
|
|
8646
|
+
const tagTime = el("span", "knobs-tl-tag-time");
|
|
8647
|
+
tag.append(tagLabel, tagTime);
|
|
8648
|
+
const segs = el("div", "knobs-tl-segs");
|
|
8569
8649
|
const head = el("div", "knobs-tl-head");
|
|
8570
8650
|
head.id = `${id}-head`;
|
|
8571
8651
|
head.setAttribute("role", "slider");
|
|
@@ -8573,11 +8653,13 @@ function timeline(container, options) {
|
|
|
8573
8653
|
head.setAttribute("aria-valuemin", "0");
|
|
8574
8654
|
head.setAttribute("aria-valuemax", String(duration));
|
|
8575
8655
|
track.append(canvas, markerLane, hover, head, tag);
|
|
8656
|
+
if (line)
|
|
8657
|
+
track.insertBefore(segs, markerLane);
|
|
8576
8658
|
root.append(transport, track);
|
|
8577
8659
|
container.append(root);
|
|
8578
8660
|
let width = 1;
|
|
8579
8661
|
let height = 1;
|
|
8580
|
-
const fitView = () => clampView({ start: 0, span: duration }, duration, { pad: 0.02 });
|
|
8662
|
+
const fitView = () => clampView({ start: 0, span: duration }, duration, { pad: line ? 0 : 0.02 });
|
|
8581
8663
|
let target = fitView();
|
|
8582
8664
|
const vStart = new Spring(target.start, INSTANT2.omega, 1, 0.00001);
|
|
8583
8665
|
const vSpan = new Spring(target.span, INSTANT2.omega, 1, 0.00001);
|
|
@@ -8586,13 +8668,13 @@ function timeline(container, options) {
|
|
|
8586
8668
|
const xOf = (t) => (t - vStart.x) * pxPerSec();
|
|
8587
8669
|
const tOf = (x) => vStart.x + x / pxPerSec();
|
|
8588
8670
|
const minSpan = () => Math.max(4 / fps, 0.05);
|
|
8589
|
-
const retarget = (next, motion = INSTANT2) => {
|
|
8590
|
-
target = clampView(next, duration, { pad: 0.04, minSpan: minSpan() });
|
|
8671
|
+
const retarget = (next, motion = INSTANT2, hand = false) => {
|
|
8672
|
+
target = clampView(next, duration, { pad: line ? 0 : 0.04, minSpan: minSpan() });
|
|
8591
8673
|
for (const s of [vStart, vSpan]) {
|
|
8592
8674
|
s.omega = motion.omega;
|
|
8593
8675
|
s.zeta = motion.zeta;
|
|
8594
8676
|
}
|
|
8595
|
-
if (reducedMotion(win) && motion === CALM) {
|
|
8677
|
+
if (hand || reducedMotion(win) && motion === CALM) {
|
|
8596
8678
|
vStart.snap(target.start);
|
|
8597
8679
|
vSpan.snap(target.span);
|
|
8598
8680
|
} else {
|
|
@@ -8618,6 +8700,8 @@ function timeline(container, options) {
|
|
|
8618
8700
|
};
|
|
8619
8701
|
let drawn = "";
|
|
8620
8702
|
const drawRuler = () => {
|
|
8703
|
+
if (line)
|
|
8704
|
+
return;
|
|
8621
8705
|
const v = view();
|
|
8622
8706
|
const key = `${v.start.toFixed(6)}:${v.span.toFixed(6)}:${width}:${height}:${units}:${fps}`;
|
|
8623
8707
|
if (key === drawn)
|
|
@@ -8705,6 +8789,8 @@ function timeline(container, options) {
|
|
|
8705
8789
|
n.title = `${m.label ? `${m.label} · ` : ""}${clock(m.time)}`;
|
|
8706
8790
|
}
|
|
8707
8791
|
placeMarkers();
|
|
8792
|
+
if (line)
|
|
8793
|
+
syncSegments();
|
|
8708
8794
|
};
|
|
8709
8795
|
const placeMarkers = () => {
|
|
8710
8796
|
for (const m of markers) {
|
|
@@ -8713,6 +8799,36 @@ function timeline(container, options) {
|
|
|
8713
8799
|
n.style.transform = `translateX(${xOf(m.time).toFixed(2)}px)`;
|
|
8714
8800
|
}
|
|
8715
8801
|
};
|
|
8802
|
+
let bounds = [];
|
|
8803
|
+
const segEls = [];
|
|
8804
|
+
const syncSegments = () => {
|
|
8805
|
+
bounds = chapterBounds(markers.map((m) => m.time), duration);
|
|
8806
|
+
while (segEls.length > bounds.length - 1)
|
|
8807
|
+
segEls.pop().el.remove();
|
|
8808
|
+
while (segEls.length < bounds.length - 1) {
|
|
8809
|
+
const n = el("i", "knobs-tl-seg");
|
|
8810
|
+
const fill = el("b", "knobs-tl-seg-fill");
|
|
8811
|
+
n.append(fill);
|
|
8812
|
+
segs.append(n);
|
|
8813
|
+
segEls.push({ el: n, fill });
|
|
8814
|
+
}
|
|
8815
|
+
placeSegments();
|
|
8816
|
+
};
|
|
8817
|
+
const placeSegments = () => {
|
|
8818
|
+
if (!line)
|
|
8819
|
+
return;
|
|
8820
|
+
const last = segEls.length - 1;
|
|
8821
|
+
segEls.forEach((s, i) => {
|
|
8822
|
+
const a = bounds[i];
|
|
8823
|
+
const b = bounds[i + 1];
|
|
8824
|
+
const x0 = xOf(a) + (i > 0 ? SEG_GAP / 2 : 0);
|
|
8825
|
+
const x1 = xOf(b) - (i < last ? SEG_GAP / 2 : 0);
|
|
8826
|
+
s.el.style.transform = `translateX(${x0.toFixed(2)}px)`;
|
|
8827
|
+
s.el.style.width = `${Math.max(0, x1 - x0).toFixed(2)}px`;
|
|
8828
|
+
const k = Math.min(1, Math.max(0, (shownHead - a) / Math.max(0.000000001, b - a)));
|
|
8829
|
+
s.fill.style.transform = `scaleX(${k.toFixed(4)})`;
|
|
8830
|
+
});
|
|
8831
|
+
};
|
|
8716
8832
|
const syncRanges = () => {
|
|
8717
8833
|
const keep = new Set(ranges.map((r) => r.id));
|
|
8718
8834
|
for (const [k, n] of rangeEls)
|
|
@@ -8729,7 +8845,7 @@ function timeline(container, options) {
|
|
|
8729
8845
|
b.dataset.edge = "end";
|
|
8730
8846
|
const label = el("span", "knobs-tl-rlabel");
|
|
8731
8847
|
n.append(label, a, b);
|
|
8732
|
-
track.insertBefore(n, markerLane);
|
|
8848
|
+
track.insertBefore(n, line ? segs : markerLane);
|
|
8733
8849
|
rangeEls.set(r.id, n);
|
|
8734
8850
|
if (built)
|
|
8735
8851
|
enter(n);
|
|
@@ -8781,6 +8897,7 @@ function timeline(container, options) {
|
|
|
8781
8897
|
let currentId = null;
|
|
8782
8898
|
const renderHead = () => {
|
|
8783
8899
|
head.style.transform = `translateX(${xOf(shownHead).toFixed(2)}px)`;
|
|
8900
|
+
placeSegments();
|
|
8784
8901
|
head.setAttribute("aria-valuenow", time.toFixed(3));
|
|
8785
8902
|
head.setAttribute("aria-valuetext", `${time.toFixed(2)} seconds, frame ${toFrame(time, fps)}`);
|
|
8786
8903
|
const cur = currentMarker(markers, time, fps)?.id ?? null;
|
|
@@ -9044,14 +9161,25 @@ function timeline(container, options) {
|
|
|
9044
9161
|
setTime(s.t, "scrub", s.shown, press);
|
|
9045
9162
|
root.toggleAttribute("data-stuck", s.stop !== null);
|
|
9046
9163
|
};
|
|
9164
|
+
let pointerX = null;
|
|
9165
|
+
const zoomAtHand = (factor) => zoom(factor, pointerX === null ? time : target.start + pointerX / width * target.span);
|
|
9047
9166
|
const showTag = (x) => {
|
|
9048
9167
|
const at = Math.min(duration, Math.max(0, tOf(x)));
|
|
9049
|
-
const text = `${at.toFixed(2)} s`;
|
|
9050
|
-
if (
|
|
9051
|
-
|
|
9168
|
+
const text = line ? minutes(at) : `${at.toFixed(2)} s`;
|
|
9169
|
+
if (tagTime.textContent !== text)
|
|
9170
|
+
tagTime.textContent = text;
|
|
9171
|
+
const label = line ? currentMarker(markers, at, fps)?.label ?? "" : "";
|
|
9172
|
+
if (tagLabel.textContent !== label)
|
|
9173
|
+
tagLabel.textContent = label;
|
|
9052
9174
|
const cx = Math.min(width, Math.max(0, x));
|
|
9053
|
-
|
|
9054
|
-
|
|
9175
|
+
if (line) {
|
|
9176
|
+
const w = tag.offsetWidth;
|
|
9177
|
+
const left = Math.min(Math.max(0, width - w), Math.max(0, cx - w / 2));
|
|
9178
|
+
tag.style.transform = `translateX(${left.toFixed(1)}px)`;
|
|
9179
|
+
} else {
|
|
9180
|
+
const flip = cx > width - tag.offsetWidth - 12;
|
|
9181
|
+
tag.style.transform = `translateX(${cx.toFixed(1)}px) translateX(${flip ? "calc(-100% - 4px)" : "4px"})`;
|
|
9182
|
+
}
|
|
9055
9183
|
tag.dataset.on = "";
|
|
9056
9184
|
};
|
|
9057
9185
|
const hideTag = () => delete tag.dataset.on;
|
|
@@ -9112,6 +9240,7 @@ function timeline(container, options) {
|
|
|
9112
9240
|
});
|
|
9113
9241
|
track.addEventListener("pointermove", (e) => {
|
|
9114
9242
|
const x = localX(e);
|
|
9243
|
+
pointerX = x;
|
|
9115
9244
|
hover.style.transform = `translateX(${x.toFixed(1)}px)`;
|
|
9116
9245
|
if (!drag || drag.kind === "scrub")
|
|
9117
9246
|
showTag(x);
|
|
@@ -9185,6 +9314,7 @@ function timeline(container, options) {
|
|
|
9185
9314
|
track.addEventListener("pointercancel", endDrag);
|
|
9186
9315
|
track.addEventListener("lostpointercapture", endDrag);
|
|
9187
9316
|
track.addEventListener("pointerleave", () => {
|
|
9317
|
+
pointerX = null;
|
|
9188
9318
|
if (drag)
|
|
9189
9319
|
return;
|
|
9190
9320
|
hover.style.transform = "translateX(-100px)";
|
|
@@ -9192,7 +9322,7 @@ function timeline(container, options) {
|
|
|
9192
9322
|
});
|
|
9193
9323
|
track.addEventListener("dblclick", (e) => {
|
|
9194
9324
|
if (lane)
|
|
9195
|
-
return;
|
|
9325
|
+
return fit();
|
|
9196
9326
|
const markerEl = e.target.closest(".knobs-tl-marker");
|
|
9197
9327
|
if (markerEl)
|
|
9198
9328
|
return rename(markerEl.dataset.id);
|
|
@@ -9203,18 +9333,40 @@ function timeline(container, options) {
|
|
|
9203
9333
|
rename(m.id);
|
|
9204
9334
|
});
|
|
9205
9335
|
track.addEventListener("wheel", (e) => {
|
|
9206
|
-
|
|
9336
|
+
const zooming = e.ctrlKey || e.metaKey;
|
|
9337
|
+
if (zooming && pinching)
|
|
9338
|
+
return e.preventDefault();
|
|
9339
|
+
const sideways = Math.abs(e.deltaX) > Math.abs(e.deltaY);
|
|
9340
|
+
if (lane && !zooming && !sideways)
|
|
9207
9341
|
return;
|
|
9208
9342
|
e.preventDefault();
|
|
9209
9343
|
const scale = e.deltaMode === 1 ? 16 : e.deltaMode === 2 ? height : 1;
|
|
9210
|
-
if (
|
|
9344
|
+
if (zooming) {
|
|
9211
9345
|
const at = target.start + localX(e) / width * target.span;
|
|
9212
|
-
|
|
9346
|
+
const k = Math.exp(-e.deltaY * scale * (e.ctrlKey ? 0.01 : 0.002));
|
|
9347
|
+
retarget(zoomView(target, k, at), INSTANT2, lane);
|
|
9213
9348
|
} else {
|
|
9214
|
-
const d = (
|
|
9215
|
-
retarget({ ...target, start: target.start + d / width * target.span });
|
|
9349
|
+
const d = (sideways ? e.deltaX : e.deltaY) * scale;
|
|
9350
|
+
retarget({ ...target, start: target.start + d / width * target.span }, INSTANT2, lane);
|
|
9216
9351
|
}
|
|
9217
9352
|
}, { passive: false });
|
|
9353
|
+
let pinchScale = 1;
|
|
9354
|
+
let pinching = false;
|
|
9355
|
+
const onGesture = (e) => {
|
|
9356
|
+
const g = e;
|
|
9357
|
+
e.preventDefault();
|
|
9358
|
+
if (e.type === "gesturestart")
|
|
9359
|
+
pinchScale = 1;
|
|
9360
|
+
pinching = e.type !== "gestureend";
|
|
9361
|
+
if (e.type !== "gesturechange")
|
|
9362
|
+
return;
|
|
9363
|
+
const s = g.scale ?? 1;
|
|
9364
|
+
const x = g.clientX !== undefined ? localX({ clientX: g.clientX }) : width / 2;
|
|
9365
|
+
retarget(zoomView(target, s / pinchScale, target.start + x / width * target.span), INSTANT2, true);
|
|
9366
|
+
pinchScale = s;
|
|
9367
|
+
};
|
|
9368
|
+
for (const type of ["gesturestart", "gesturechange", "gestureend"])
|
|
9369
|
+
track.addEventListener(type, onGesture);
|
|
9218
9370
|
root.addEventListener("keydown", (e) => {
|
|
9219
9371
|
if (e.target !== root || e.metaKey || e.ctrlKey) {
|
|
9220
9372
|
if ((e.metaKey || e.ctrlKey) && (e.key === "=" || e.key === "-" || e.key === "0") && e.target === root) {
|
|
@@ -9222,7 +9374,7 @@ function timeline(container, options) {
|
|
|
9222
9374
|
if (e.key === "0")
|
|
9223
9375
|
fit();
|
|
9224
9376
|
else
|
|
9225
|
-
|
|
9377
|
+
zoomAtHand(e.key === "=" ? 1.5 : 1 / 1.5);
|
|
9226
9378
|
}
|
|
9227
9379
|
return;
|
|
9228
9380
|
}
|
|
@@ -9304,18 +9456,12 @@ function timeline(container, options) {
|
|
|
9304
9456
|
return;
|
|
9305
9457
|
case "=":
|
|
9306
9458
|
case "+":
|
|
9307
|
-
if (lane)
|
|
9308
|
-
return;
|
|
9309
9459
|
claim();
|
|
9310
|
-
return
|
|
9460
|
+
return zoomAtHand(1.5);
|
|
9311
9461
|
case "-":
|
|
9312
|
-
if (lane)
|
|
9313
|
-
return;
|
|
9314
9462
|
claim();
|
|
9315
|
-
return
|
|
9463
|
+
return zoomAtHand(1 / 1.5);
|
|
9316
9464
|
case "0":
|
|
9317
|
-
if (lane)
|
|
9318
|
-
return;
|
|
9319
9465
|
claim();
|
|
9320
9466
|
return fit();
|
|
9321
9467
|
}
|
|
@@ -9531,7 +9677,7 @@ function timeline(container, options) {
|
|
|
9531
9677
|
};
|
|
9532
9678
|
return api;
|
|
9533
9679
|
}
|
|
9534
|
-
var RULER = 22, INSTANT2, CALM, GLIDE, uid2 = 0, PLAY2 = '<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>';
|
|
9680
|
+
var RULER = 22, SEG_GAP = 3, INSTANT2, CALM, GLIDE, uid2 = 0, PLAY2 = '<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>';
|
|
9535
9681
|
var init_timeline = __esm(() => {
|
|
9536
9682
|
init_rgba();
|
|
9537
9683
|
init_motion();
|
|
@@ -9603,15 +9749,28 @@ function compareLayout(box, before, after) {
|
|
|
9603
9749
|
const shape = after.w > 0 && after.h > 0 ? after : before;
|
|
9604
9750
|
const pair = letterbox({ w: shape.w * 2, h: shape.h }, box);
|
|
9605
9751
|
const half = pair.w / 2;
|
|
9752
|
+
const fused = { x: pair.x + half / 2, y: pair.y, w: half, h: pair.h };
|
|
9606
9753
|
return {
|
|
9607
9754
|
side: {
|
|
9608
9755
|
before: letterbox(before, { x: pair.x, y: pair.y, w: half, h: pair.h }),
|
|
9609
9756
|
after: letterbox(after, { x: pair.x + half, y: pair.y, w: half, h: pair.h }),
|
|
9610
9757
|
split: pair.x + half
|
|
9611
9758
|
},
|
|
9612
|
-
overlay: { before: letterbox(before,
|
|
9759
|
+
overlay: { before: letterbox(before, fused), after: letterbox(after, fused) }
|
|
9613
9760
|
};
|
|
9614
9761
|
}
|
|
9762
|
+
function fuseFrame(L, before, after, k, wipe) {
|
|
9763
|
+
const t = Math.min(1, Math.max(0, k));
|
|
9764
|
+
const o = L.overlay;
|
|
9765
|
+
const left = Math.min(o.before.x, o.after.x);
|
|
9766
|
+
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);
|
|
9767
|
+
const cut = L.side.split + (line - L.side.split) * t;
|
|
9768
|
+
const contact = Math.max(0, Math.min(cut - after.x, before.x + before.w - cut));
|
|
9769
|
+
const feather = Math.min(contact, 1 + FUSE.feather * Math.sin(Math.PI * t));
|
|
9770
|
+
const overlap = before.x + before.w - after.x;
|
|
9771
|
+
const seam = t > 0.001 && overlap >= 0 ? Math.min(1, Math.max(0, overlap / FUSE.seamFade)) * Math.min(1, t * 4) : 0;
|
|
9772
|
+
return { cut, feather, seam, blur: t < 0.995 ? FUSE.blur * (1 - t) : 0 };
|
|
9773
|
+
}
|
|
9615
9774
|
function clampZoom(r) {
|
|
9616
9775
|
const w = Math.min(1, Math.max(1 / ZOOM_MAX, r.w));
|
|
9617
9776
|
const u = Math.min(1 - w, Math.max(0, r.u));
|
|
@@ -9697,7 +9856,15 @@ function stripPeaks(diffs, o = {}) {
|
|
|
9697
9856
|
});
|
|
9698
9857
|
return out;
|
|
9699
9858
|
}
|
|
9700
|
-
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), sameFrame = (a, b, fps) => frameOf(a, fps) === frameOf(b, fps), SEEK_IDLE,
|
|
9859
|
+
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), sameFrame = (a, b, fps) => frameOf(a, fps) === frameOf(b, fps), SEEK_IDLE, FUSE, labelFold = (fold, mark, blur = 3) => {
|
|
9860
|
+
const f = Math.min(1, Math.max(0, fold));
|
|
9861
|
+
const open = 1 - f;
|
|
9862
|
+
return {
|
|
9863
|
+
width: mark * open,
|
|
9864
|
+
opacity: Math.min(1, Math.max(0, (open - 0.2) / 0.8)),
|
|
9865
|
+
blur: blur * Math.min(1, Math.max(0, (f - 0.25) / 0.75))
|
|
9866
|
+
};
|
|
9867
|
+
}, 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({
|
|
9701
9868
|
u: r.u - dx / Math.max(1, base.w) * r.w,
|
|
9702
9869
|
v: r.v - dy / Math.max(1, base.h) * r.h,
|
|
9703
9870
|
w: r.w
|
|
@@ -9720,6 +9887,7 @@ var init_compare_model = __esm(() => {
|
|
|
9720
9887
|
MODE_KEYS = { side: "s", wipe: "w", difference: "d" };
|
|
9721
9888
|
VIDEO = /\.(mp4|m4v|mov|webm|ogv|mkv)$/i;
|
|
9722
9889
|
SEEK_IDLE = { seeking: false, pending: null };
|
|
9890
|
+
FUSE = { feather: 24, blur: 4, seamFade: 48 };
|
|
9723
9891
|
ZOOM_FIT = { u: 0, v: 0, w: 1, h: 1 };
|
|
9724
9892
|
});
|
|
9725
9893
|
|
|
@@ -10614,11 +10782,6 @@ fn pm(c: vec4f) -> vec4f { return vec4f(c.rgb * c.a, c.a); }
|
|
|
10614
10782
|
|
|
10615
10783
|
fn composite(mode: u32, p: vec2f, a: vec4f, b: vec4f) -> vec4f {
|
|
10616
10784
|
switch mode {
|
|
10617
|
-
case 1u: {
|
|
10618
|
-
let s = max(u.wipe.y, 0.5);
|
|
10619
|
-
let m = smoothstep(u.wipe.x - s, u.wipe.x + s, p.x);
|
|
10620
|
-
return pm(vec4f(mix(a.rgb, b.rgb, m), mix(a.a, b.a, m)));
|
|
10621
|
-
}
|
|
10622
10785
|
case 2u: {
|
|
10623
10786
|
let d = abs(a.rgb - b.rgb);
|
|
10624
10787
|
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);
|
|
@@ -10627,8 +10790,10 @@ fn composite(mode: u32, p: vec2f, a: vec4f, b: vec4f) -> vec4f {
|
|
|
10627
10790
|
}
|
|
10628
10791
|
default: {}
|
|
10629
10792
|
}
|
|
10630
|
-
// Side by side
|
|
10631
|
-
|
|
10793
|
+
// Side by side, fusing and wiping: Before over After, cut at the seam with its feather.
|
|
10794
|
+
let s = max(u.wipe.y, 0.5);
|
|
10795
|
+
let ka = a.a * (1.0 - smoothstep(u.wipe.x - s, u.wipe.x + s, p.x));
|
|
10796
|
+
return vec4f(a.rgb * ka + b.rgb * b.a * (1.0 - ka), ka + b.a * (1.0 - ka));
|
|
10632
10797
|
}
|
|
10633
10798
|
|
|
10634
10799
|
@fragment fn fs(@builtin(position) q: vec4f) -> @location(0) vec4f {
|
|
@@ -11030,6 +11195,29 @@ var init_compare_strip = __esm(() => {
|
|
|
11030
11195
|
DIFF = { w: 32, h: 18 };
|
|
11031
11196
|
});
|
|
11032
11197
|
|
|
11198
|
+
// src/ui/player.ts
|
|
11199
|
+
function playGlyph(doc) {
|
|
11200
|
+
const element = doc.createElement("span");
|
|
11201
|
+
element.className = "knobs-player-glyph";
|
|
11202
|
+
element.setAttribute("aria-hidden", "true");
|
|
11203
|
+
let timer = 0;
|
|
11204
|
+
const win = doc.defaultView;
|
|
11205
|
+
return {
|
|
11206
|
+
element,
|
|
11207
|
+
flash(playing) {
|
|
11208
|
+
element.innerHTML = playing ? PLAY2 : PAUSE;
|
|
11209
|
+
element.setAttribute("data-flash", "");
|
|
11210
|
+
win.clearTimeout(timer);
|
|
11211
|
+
timer = win.setTimeout(() => element.removeAttribute("data-flash"), FLASH_MS);
|
|
11212
|
+
},
|
|
11213
|
+
dispose: () => win.clearTimeout(timer)
|
|
11214
|
+
};
|
|
11215
|
+
}
|
|
11216
|
+
var FLASH_MS = 450;
|
|
11217
|
+
var init_player = __esm(() => {
|
|
11218
|
+
init_timeline();
|
|
11219
|
+
});
|
|
11220
|
+
|
|
11033
11221
|
// src/ui/compare.ts
|
|
11034
11222
|
function compare(container, options) {
|
|
11035
11223
|
const doc = container.ownerDocument;
|
|
@@ -11137,6 +11325,8 @@ function compare(container, options) {
|
|
|
11137
11325
|
if (name.textContent !== text)
|
|
11138
11326
|
name.textContent = text;
|
|
11139
11327
|
};
|
|
11328
|
+
const seam = el("div", "knobs-cmp-seam");
|
|
11329
|
+
seam.setAttribute("aria-hidden", "true");
|
|
11140
11330
|
const handle = el("div", "knobs-cmp-handle");
|
|
11141
11331
|
handle.setAttribute("role", "slider");
|
|
11142
11332
|
handle.setAttribute("aria-label", "Wipe");
|
|
@@ -11146,7 +11336,8 @@ function compare(container, options) {
|
|
|
11146
11336
|
grip.setAttribute("aria-hidden", "true");
|
|
11147
11337
|
handle.append(grip);
|
|
11148
11338
|
const note = el("div", "knobs-cmp-note");
|
|
11149
|
-
|
|
11339
|
+
const glyph = playGlyph(doc);
|
|
11340
|
+
stage.append(canvas, split, seam, labels[0], labels[1], handle, zoomPill, note, glyph.element);
|
|
11150
11341
|
root.append(bar, stage);
|
|
11151
11342
|
const wantsStrip = options.strip === true;
|
|
11152
11343
|
let tl = bound;
|
|
@@ -11161,7 +11352,7 @@ function compare(container, options) {
|
|
|
11161
11352
|
root.append(host);
|
|
11162
11353
|
tl = timeline(host, {
|
|
11163
11354
|
...look,
|
|
11164
|
-
variant: "
|
|
11355
|
+
variant: "line",
|
|
11165
11356
|
duration: 1,
|
|
11166
11357
|
fps: fps(),
|
|
11167
11358
|
arrows: { step: 0.5, shift: 2 },
|
|
@@ -11182,12 +11373,13 @@ function compare(container, options) {
|
|
|
11182
11373
|
let toMode = mode;
|
|
11183
11374
|
const wipeLine = new Spring(wipe, WIPE_CATCH.omega, WIPE_CATCH.zeta, 0.0001);
|
|
11184
11375
|
let wipeCatching = false;
|
|
11185
|
-
|
|
11376
|
+
const gripAt = new Spring(0.5, LAYOUT2.omega, LAYOUT2.zeta, 0.0001);
|
|
11186
11377
|
let loupeHeld = false;
|
|
11187
11378
|
let pointer = null;
|
|
11188
11379
|
let drag = null;
|
|
11189
11380
|
const touches = new Map;
|
|
11190
11381
|
let pinch = null;
|
|
11382
|
+
let press = null;
|
|
11191
11383
|
const zu = new Spring(0, ZOOM.omega, ZOOM.zeta, 0.00001);
|
|
11192
11384
|
const zv = new Spring(0, ZOOM.omega, ZOOM.zeta, 0.00001);
|
|
11193
11385
|
const zw = new Spring(1, ZOOM.omega, ZOOM.zeta, 0.00001);
|
|
@@ -11474,6 +11666,8 @@ function compare(container, options) {
|
|
|
11474
11666
|
for (const s of sides) {
|
|
11475
11667
|
const st = s.el.style;
|
|
11476
11668
|
st.clipPath = "";
|
|
11669
|
+
st.maskImage = "";
|
|
11670
|
+
st.setProperty("-webkit-mask-image", "");
|
|
11477
11671
|
st.mixBlendMode = "";
|
|
11478
11672
|
st.opacity = "";
|
|
11479
11673
|
s.shown = null;
|
|
@@ -11700,43 +11894,65 @@ function compare(container, options) {
|
|
|
11700
11894
|
viewSent = key;
|
|
11701
11895
|
options.onView({ zoom: clampZoom(z), loop: l });
|
|
11702
11896
|
};
|
|
11703
|
-
const
|
|
11704
|
-
|
|
11705
|
-
|
|
11706
|
-
return [at.before, at.after];
|
|
11707
|
-
}
|
|
11708
|
-
const ax = r.after.x + INSET + (r.after.w - labelW[1] - INSET * 2) * lay.x;
|
|
11709
|
-
return [
|
|
11710
|
-
{ x: r.before.x + INSET, opacity: 1 },
|
|
11711
|
-
{ x: ax, opacity: 1 }
|
|
11712
|
-
];
|
|
11897
|
+
const differenceWeight = () => {
|
|
11898
|
+
const f = Math.min(1, Math.max(0, fade.x));
|
|
11899
|
+
return (fromMode === "difference" ? 1 - f : 0) + (toMode === "difference" ? f : 0);
|
|
11713
11900
|
};
|
|
11714
11901
|
const placeGrip = (wr) => {
|
|
11715
|
-
const gy = gripTop(
|
|
11902
|
+
const gy = gripTop(wr.y + gripAt.x * wr.h, wr, GRIP.h) - wr.y;
|
|
11716
11903
|
grip.style.transform = `translate3d(0,${gy.toFixed(2)}px,0)`;
|
|
11717
11904
|
};
|
|
11905
|
+
let folded = 0;
|
|
11718
11906
|
const draw = () => {
|
|
11719
11907
|
dirty = false;
|
|
11908
|
+
if (!root.hasAttribute("data-ready") && sides.every((x) => x.shown || x.failed))
|
|
11909
|
+
root.setAttribute("data-ready", "");
|
|
11720
11910
|
const r = layoutNow();
|
|
11721
11911
|
const wr = wipeRect(r.L.overlay);
|
|
11722
|
-
const
|
|
11723
|
-
const
|
|
11724
|
-
const
|
|
11725
|
-
const
|
|
11726
|
-
|
|
11912
|
+
const k = Math.min(1, Math.max(0, lay.x));
|
|
11913
|
+
const fu = fuseFrame(r.L, r.before, r.after, k, wipeLine.x);
|
|
11914
|
+
const dw = differenceWeight();
|
|
11915
|
+
const fusing = 1 - dw;
|
|
11916
|
+
folded = k * fusing;
|
|
11917
|
+
const fold = labelFold(folded, MARK.w);
|
|
11918
|
+
const w = labelW.map((x) => x - (MARK.w - fold.width) - MARK.gap * folded);
|
|
11919
|
+
const ride = seamLabels(fu.cut, r.before.x, r.after.x + r.after.w, w[0], w[1], INSET);
|
|
11920
|
+
const apart = [r.before.x + INSET, r.after.x + INSET];
|
|
11921
|
+
const corner = [r.before.x + INSET, r.after.x + r.after.w - labelW[1] - INSET];
|
|
11922
|
+
const rides = [ride.before, ride.after];
|
|
11727
11923
|
const ys = [r.before.y + INSET, r.after.y + INSET];
|
|
11728
11924
|
for (const i of [0, 1]) {
|
|
11729
|
-
const
|
|
11730
|
-
|
|
11731
|
-
|
|
11925
|
+
const fused = apart[i] + (rides[i].x - apart[i]) * k;
|
|
11926
|
+
const x = fused + (corner[i] - fused) * dw;
|
|
11927
|
+
const opacity = (1 - k + k * rides[i].opacity) * fusing + dw;
|
|
11928
|
+
const l = labels[i];
|
|
11929
|
+
l.style.transform = `translate(${x.toFixed(2)}px,${ys[i].toFixed(2)}px)`;
|
|
11930
|
+
l.style.opacity = opacity.toFixed(3);
|
|
11931
|
+
const mark = l.firstElementChild;
|
|
11932
|
+
if (mark) {
|
|
11933
|
+
mark.style.width = `${fold.width.toFixed(2)}px`;
|
|
11934
|
+
mark.style.marginRight = `${(-MARK.gap * folded).toFixed(2)}px`;
|
|
11935
|
+
mark.style.opacity = fold.opacity.toFixed(3);
|
|
11936
|
+
mark.style.filter = fold.blur > 0.05 ? `blur(${fold.blur.toFixed(2)}px)` : "";
|
|
11937
|
+
}
|
|
11732
11938
|
}
|
|
11733
11939
|
const top = Math.min(r.L.side.before.y, r.L.side.after.y);
|
|
11734
11940
|
split.style.transform = `translate(${r.L.side.split.toFixed(2)}px,${top.toFixed(2)}px)`;
|
|
11735
11941
|
split.style.height = `${Math.max(0, r.L.side.after.h).toFixed(2)}px`;
|
|
11736
11942
|
split.style.opacity = String(Math.max(0, 1 - k * 2));
|
|
11943
|
+
const seamOn = fu.seam * fusing;
|
|
11944
|
+
const blur = fu.blur > 0.01 ? `blur(${fu.blur.toFixed(2)}px)` : "none";
|
|
11945
|
+
seam.style.transform = `translate(${fu.cut.toFixed(2)}px,${wr.y.toFixed(2)}px) translateX(-50%)`;
|
|
11946
|
+
seam.style.width = `${(8 + 2 * fu.feather).toFixed(2)}px`;
|
|
11947
|
+
seam.style.height = `${Math.max(0, wr.h).toFixed(2)}px`;
|
|
11948
|
+
seam.style.opacity = (blur === "none" ? 0 : seamOn).toFixed(3);
|
|
11949
|
+
seam.style.backdropFilter = blur;
|
|
11950
|
+
seam.style.setProperty("-webkit-backdrop-filter", blur);
|
|
11737
11951
|
handle.toggleAttribute("data-on", mode === "wipe");
|
|
11738
|
-
handle.style.transform = `translate(${
|
|
11952
|
+
handle.style.transform = `translate(${fu.cut.toFixed(2)}px,${wr.y.toFixed(2)}px)`;
|
|
11739
11953
|
handle.style.height = `${Math.max(0, wr.h).toFixed(2)}px`;
|
|
11954
|
+
handle.style.opacity = seamOn.toFixed(3);
|
|
11955
|
+
handle.style.filter = blur;
|
|
11740
11956
|
placeGrip(wr);
|
|
11741
11957
|
handle.setAttribute("aria-valuenow", String(Math.round(wipe * 100)));
|
|
11742
11958
|
handle.setAttribute("aria-valuetext", `${Math.round(wipe * 100)}% Before`);
|
|
@@ -11749,24 +11965,27 @@ function compare(container, options) {
|
|
|
11749
11965
|
if (pending)
|
|
11750
11966
|
return;
|
|
11751
11967
|
if (renderer === "dom") {
|
|
11752
|
-
const
|
|
11968
|
+
const cut = mode === "difference" ? null : fu;
|
|
11969
|
+
const place = (n, rect, clip, before) => {
|
|
11753
11970
|
const st = n.style;
|
|
11754
11971
|
st.transform = `translate(${rect.x.toFixed(2)}px,${rect.y.toFixed(2)}px)`;
|
|
11755
11972
|
st.width = `${rect.w.toFixed(2)}px`;
|
|
11756
11973
|
st.height = `${rect.h.toFixed(2)}px`;
|
|
11974
|
+
const edge = before && cut ? cut.cut + cut.feather : Number.POSITIVE_INFINITY;
|
|
11757
11975
|
const t = Math.max(0, clip.y - rect.y);
|
|
11758
11976
|
const l = Math.max(0, clip.x - rect.x);
|
|
11759
11977
|
const b = Math.max(0, rect.y + rect.h - (clip.y + clip.h));
|
|
11760
|
-
const right = Math.max(0, rect.x + rect.w - Math.min(clip.x + clip.w,
|
|
11978
|
+
const right = Math.max(0, rect.x + rect.w - Math.min(clip.x + clip.w, edge));
|
|
11761
11979
|
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)` : "";
|
|
11980
|
+
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)` : "";
|
|
11981
|
+
st.maskImage = mask;
|
|
11982
|
+
st.setProperty("-webkit-mask-image", mask);
|
|
11762
11983
|
};
|
|
11763
|
-
const cut = mode === "wipe" ? wx : Number.POSITIVE_INFINITY;
|
|
11764
11984
|
for (const s of sides) {
|
|
11765
11985
|
const rect = s.index === 0 ? r.zBefore : r.zAfter;
|
|
11766
11986
|
const clip = s.index === 0 ? r.before : r.after;
|
|
11767
|
-
|
|
11768
|
-
place(s.
|
|
11769
|
-
place(s.preview, rect, clip, at);
|
|
11987
|
+
place(s.el, rect, clip, s.index === 0);
|
|
11988
|
+
place(s.preview, rect, clip, s.index === 0);
|
|
11770
11989
|
if (s.index !== 0)
|
|
11771
11990
|
continue;
|
|
11772
11991
|
for (const n of [s.el, s.preview])
|
|
@@ -11786,8 +12005,8 @@ function compare(container, options) {
|
|
|
11786
12005
|
width: Math.max(1, Math.round(stageW * dpr)),
|
|
11787
12006
|
height: Math.max(1, Math.round(stageH * dpr)),
|
|
11788
12007
|
dpr,
|
|
11789
|
-
wipeX:
|
|
11790
|
-
soft: 0.75 * dpr,
|
|
12008
|
+
wipeX: fu.cut * dpr,
|
|
12009
|
+
soft: Math.max(0.75, fu.feather) * dpr,
|
|
11791
12010
|
from: MODE_INDEX[fromMode],
|
|
11792
12011
|
to: MODE_INDEX[toMode],
|
|
11793
12012
|
mix: Math.min(1, Math.max(0, fade.x)),
|
|
@@ -11814,6 +12033,7 @@ function compare(container, options) {
|
|
|
11814
12033
|
zu.step(dt);
|
|
11815
12034
|
zv.step(dt);
|
|
11816
12035
|
zw.step(dt);
|
|
12036
|
+
gripAt.step(dt);
|
|
11817
12037
|
const playing = isPlaying();
|
|
11818
12038
|
if (playing) {
|
|
11819
12039
|
const m = master();
|
|
@@ -11844,11 +12064,9 @@ function compare(container, options) {
|
|
|
11844
12064
|
pushFrame(s);
|
|
11845
12065
|
}
|
|
11846
12066
|
}
|
|
11847
|
-
const moving = !lay.resting || !fade.resting || !zoomResting() || wipeCatching;
|
|
12067
|
+
const moving = !lay.resting || !fade.resting || !zoomResting() || wipeCatching || !gripAt.resting;
|
|
11848
12068
|
if (moving)
|
|
11849
12069
|
dirty = true;
|
|
11850
|
-
if (auto && !lay.resting)
|
|
11851
|
-
fitHeight();
|
|
11852
12070
|
if (dirty)
|
|
11853
12071
|
draw();
|
|
11854
12072
|
return playing || moving;
|
|
@@ -11874,13 +12092,7 @@ function compare(container, options) {
|
|
|
11874
12092
|
zw.set(c.w);
|
|
11875
12093
|
requestDraw();
|
|
11876
12094
|
};
|
|
11877
|
-
const autoHeight = (w) =>
|
|
11878
|
-
const shape = sizeOf(sides[1]);
|
|
11879
|
-
const side = splitHeight(w, shape);
|
|
11880
|
-
const over = w * shape.h / shape.w;
|
|
11881
|
-
const h = side + (over - side) * lay.x;
|
|
11882
|
-
return Math.max(60, Math.min(win.innerHeight * 0.8, h));
|
|
11883
|
-
};
|
|
12095
|
+
const autoHeight = (w) => Math.max(60, Math.min(win.innerHeight * 0.8, splitHeight(w, sizeOf(sides[1]))));
|
|
11884
12096
|
const fitHeight = () => {
|
|
11885
12097
|
const h = autoHeight(stageW);
|
|
11886
12098
|
if (Math.abs(h - stageH) < 0.25)
|
|
@@ -11892,7 +12104,8 @@ function compare(container, options) {
|
|
|
11892
12104
|
const readColors = () => {
|
|
11893
12105
|
const c = tokenColor(win.getComputedStyle(root), "--_ku-cmp-diff", [0.49, 0.81, 0.61, 1]);
|
|
11894
12106
|
accent = [c[0], c[1], c[2]];
|
|
11895
|
-
|
|
12107
|
+
const unfold = MARK.w - labelFold(folded, MARK.w).width + MARK.gap * folded;
|
|
12108
|
+
labelW = [labels[0].offsetWidth + unfold, labels[1].offsetWidth + unfold];
|
|
11896
12109
|
requestDraw();
|
|
11897
12110
|
};
|
|
11898
12111
|
const measure = () => {
|
|
@@ -11983,8 +12196,10 @@ function compare(container, options) {
|
|
|
11983
12196
|
root.focus({ preventScroll: true });
|
|
11984
12197
|
touches.set(e.pointerId, p);
|
|
11985
12198
|
stage.setPointerCapture(e.pointerId);
|
|
12199
|
+
press = touches.size === 1 && mode !== "wipe" && master() ? p : null;
|
|
11986
12200
|
if (touches.size >= 2) {
|
|
11987
12201
|
e.preventDefault();
|
|
12202
|
+
press = null;
|
|
11988
12203
|
return startPinch();
|
|
11989
12204
|
}
|
|
11990
12205
|
if (mode === "wipe") {
|
|
@@ -11994,7 +12209,7 @@ function compare(container, options) {
|
|
|
11994
12209
|
if (inside && (grab.onLine || !zoomed())) {
|
|
11995
12210
|
drag = { kind: "wipe", offset: grab.offset };
|
|
11996
12211
|
root.dataset.dragging = "";
|
|
11997
|
-
|
|
12212
|
+
gripAt.snap(wr.h > 0 ? (p.y - wr.y) / wr.h : 0.5);
|
|
11998
12213
|
e.preventDefault();
|
|
11999
12214
|
if (!grab.onLine)
|
|
12000
12215
|
setWipe(wipeAt(p.x, wr), true, true);
|
|
@@ -12012,6 +12227,8 @@ function compare(container, options) {
|
|
|
12012
12227
|
const onMove = (e) => {
|
|
12013
12228
|
const p = local(e);
|
|
12014
12229
|
pointer = p;
|
|
12230
|
+
if (press && Math.hypot(p.x - press.x, p.y - press.y) > 4)
|
|
12231
|
+
press = null;
|
|
12015
12232
|
if (touches.has(e.pointerId))
|
|
12016
12233
|
touches.set(e.pointerId, p);
|
|
12017
12234
|
if (pinch && touches.size >= 2) {
|
|
@@ -12025,14 +12242,12 @@ function compare(container, options) {
|
|
|
12025
12242
|
pinch = { d, x: mid.x, y: mid.y };
|
|
12026
12243
|
return handZoom(zoomAbout(panned, k, q.pu, q.pv));
|
|
12027
12244
|
}
|
|
12028
|
-
if (
|
|
12029
|
-
|
|
12030
|
-
|
|
12031
|
-
|
|
12032
|
-
|
|
12033
|
-
if (drag?.kind === "
|
|
12034
|
-
setWipe(wipeAt(p.x, wipeRect(layoutNow().L.overlay), drag.offset));
|
|
12035
|
-
else if (drag?.kind === "pan") {
|
|
12245
|
+
if (drag?.kind === "wipe") {
|
|
12246
|
+
const wr = wipeRect(layoutNow().L.overlay);
|
|
12247
|
+
gripAt.snap(wr.h > 0 ? (p.y - wr.y) / wr.h : 0.5);
|
|
12248
|
+
setWipe(wipeAt(p.x, wr, drag.offset));
|
|
12249
|
+
placeGrip(wr);
|
|
12250
|
+
} else if (drag?.kind === "pan") {
|
|
12036
12251
|
const dx = p.x - drag.x;
|
|
12037
12252
|
const dy = p.y - drag.y;
|
|
12038
12253
|
drag.x = p.x;
|
|
@@ -12043,11 +12258,23 @@ function compare(container, options) {
|
|
|
12043
12258
|
drawNow();
|
|
12044
12259
|
};
|
|
12045
12260
|
const endDrag = (e) => {
|
|
12261
|
+
if (press && e.type === "pointerup") {
|
|
12262
|
+
api.toggle();
|
|
12263
|
+
glyph.flash(isPlaying());
|
|
12264
|
+
}
|
|
12265
|
+
press = null;
|
|
12046
12266
|
touches.delete(e.pointerId);
|
|
12047
12267
|
if (touches.size < 2)
|
|
12048
12268
|
pinch = null;
|
|
12049
12269
|
if (!drag)
|
|
12050
12270
|
return;
|
|
12271
|
+
if (drag.kind === "wipe") {
|
|
12272
|
+
if (reducedMotion(win))
|
|
12273
|
+
gripAt.snap(0.5);
|
|
12274
|
+
else
|
|
12275
|
+
gripAt.set(0.5);
|
|
12276
|
+
requestDraw();
|
|
12277
|
+
}
|
|
12051
12278
|
drag = null;
|
|
12052
12279
|
delete root.dataset.dragging;
|
|
12053
12280
|
delete root.dataset.panning;
|
|
@@ -12197,6 +12424,7 @@ function compare(container, options) {
|
|
|
12197
12424
|
old.el.replaceWith(s.el);
|
|
12198
12425
|
old.preview.replaceWith(s.preview);
|
|
12199
12426
|
sides[index] = s;
|
|
12427
|
+
root.toggleAttribute("data-video", sides.some((x) => x.kind === "video"));
|
|
12200
12428
|
note.textContent = "";
|
|
12201
12429
|
setLabel(index, labelOf(index));
|
|
12202
12430
|
if (near)
|
|
@@ -12211,6 +12439,7 @@ function compare(container, options) {
|
|
|
12211
12439
|
stage.insertBefore(sides[0].preview, labels[0]);
|
|
12212
12440
|
setLabel(0, labelOf(0));
|
|
12213
12441
|
setLabel(1, labelOf(1));
|
|
12442
|
+
root.toggleAttribute("data-video", sides.some((s) => s.kind === "video"));
|
|
12214
12443
|
loupeBtn.setAttribute("aria-pressed", String(loupeOn));
|
|
12215
12444
|
loopBtn.setAttribute("aria-pressed", String(loopNow() !== null));
|
|
12216
12445
|
const modePill = modeList.length > 1 ? selectionPill(modes, {
|
|
@@ -12411,6 +12640,7 @@ function compare(container, options) {
|
|
|
12411
12640
|
disposed = true;
|
|
12412
12641
|
loop.stop();
|
|
12413
12642
|
modePill?.dispose();
|
|
12643
|
+
glyph.dispose();
|
|
12414
12644
|
win.clearTimeout(releaseTimer);
|
|
12415
12645
|
ro.disconnect();
|
|
12416
12646
|
nearIo?.disconnect();
|
|
@@ -12440,7 +12670,7 @@ function compare(container, options) {
|
|
|
12440
12670
|
wake();
|
|
12441
12671
|
return api;
|
|
12442
12672
|
}
|
|
12443
|
-
var LAYOUT2, FADE6, FADE_REDUCED, ZOOM, WIPE_CATCH, SLOP = 12, INSET = 8, GRIP, LOUPE, FAST_SEEK_FRAMES = 15, RELEASE_MS = 3000, MODE_LABEL;
|
|
12673
|
+
var LAYOUT2, FADE6, FADE_REDUCED, ZOOM, WIPE_CATCH, SLOP = 12, INSET = 8, GRIP, MARK, LOUPE, FAST_SEEK_FRAMES = 15, RELEASE_MS = 3000, MODE_LABEL;
|
|
12444
12674
|
var init_compare = __esm(() => {
|
|
12445
12675
|
init_motion();
|
|
12446
12676
|
init_page_hints();
|
|
@@ -12451,15 +12681,17 @@ var init_compare = __esm(() => {
|
|
|
12451
12681
|
init_icons();
|
|
12452
12682
|
init_look();
|
|
12453
12683
|
init_rgba();
|
|
12684
|
+
init_player();
|
|
12454
12685
|
init_selection_pill();
|
|
12455
12686
|
init_timeline();
|
|
12456
12687
|
init_timeline_model();
|
|
12457
|
-
LAYOUT2 = springMotion(
|
|
12688
|
+
LAYOUT2 = springMotion(SPRINGS.default);
|
|
12458
12689
|
FADE6 = springMotion(SPRINGS.snappy);
|
|
12459
12690
|
FADE_REDUCED = springMotion(FADE);
|
|
12460
12691
|
ZOOM = springMotion({ duration: 0.35, bounce: 0 });
|
|
12461
12692
|
WIPE_CATCH = springMotion(CATCH_UP);
|
|
12462
12693
|
GRIP = { w: 16, h: 28 };
|
|
12694
|
+
MARK = { w: 12, gap: 4 };
|
|
12463
12695
|
LOUPE = { r: 88, zoom: 4 };
|
|
12464
12696
|
MODE_LABEL = {
|
|
12465
12697
|
side: { short: "Side", long: "Side by side" },
|
|
@@ -17259,7 +17491,7 @@ function normalizeWalkthrough(doc) {
|
|
|
17259
17491
|
...step.question ? { question: step.question } : {},
|
|
17260
17492
|
media: list.map((m) => fillMedia(m, doc.base)),
|
|
17261
17493
|
columns: Math.max(1, Math.min(3, Math.round(step.columns ?? 1))),
|
|
17262
|
-
...step.more?.trim()
|
|
17494
|
+
...step.more?.trim() ? { more: step.more.trim() } : {},
|
|
17263
17495
|
options,
|
|
17264
17496
|
ask
|
|
17265
17497
|
};
|
|
@@ -17274,6 +17506,7 @@ function normalizeWalkthrough(doc) {
|
|
|
17274
17506
|
...typeof s.kicker === "string" && s.kicker.trim() ? { kicker: s.kicker.trim() } : {},
|
|
17275
17507
|
...s.status ? { status: s.status } : {},
|
|
17276
17508
|
...s.summary ? { summary: s.summary } : {},
|
|
17509
|
+
...s.more?.trim() ? { more: s.more.trim() } : {},
|
|
17277
17510
|
steps: own
|
|
17278
17511
|
};
|
|
17279
17512
|
});
|
|
@@ -17307,6 +17540,7 @@ function normalizeWalkthrough(doc) {
|
|
|
17307
17540
|
id: doc.id,
|
|
17308
17541
|
title: doc.title,
|
|
17309
17542
|
...doc.summary ? { summary: doc.summary } : {},
|
|
17543
|
+
...doc.more?.trim() ? { more: doc.more.trim() } : {},
|
|
17310
17544
|
meta,
|
|
17311
17545
|
feed,
|
|
17312
17546
|
verdicts: ((doc.verdicts?.length) ? doc.verdicts : DEFAULT_VERDICTS).map((v) => ({
|
|
@@ -17314,9 +17548,40 @@ function normalizeWalkthrough(doc) {
|
|
|
17314
17548
|
label: label(v)
|
|
17315
17549
|
})),
|
|
17316
17550
|
sections: withKickers(sections, doc.sections, doc.kickers),
|
|
17317
|
-
steps
|
|
17551
|
+
steps,
|
|
17552
|
+
prs: knownPRs(doc, sections),
|
|
17553
|
+
...repoOf(doc) ? { repo: repoOf(doc) } : {}
|
|
17318
17554
|
};
|
|
17319
17555
|
}
|
|
17556
|
+
var prNumber = (href) => /\/(?:pull|merge_requests)\/(\d+)(?:[/?#]|$)/.exec(href)?.[1] ?? null;
|
|
17557
|
+
var repoOf = (doc) => doc.repo?.trim().replace(/^https?:\/\/github\.com\//, "").replace(/\/+$/, "") || undefined;
|
|
17558
|
+
function knownPRs(doc, sections) {
|
|
17559
|
+
const out = {};
|
|
17560
|
+
const repo = repoOf(doc);
|
|
17561
|
+
for (const s of sections) {
|
|
17562
|
+
const n = s.link && prNumber(s.link.href);
|
|
17563
|
+
if (n)
|
|
17564
|
+
out[n] = { href: s.link.href.replace(/(\/pull\/\d+).*$/, "$1") };
|
|
17565
|
+
}
|
|
17566
|
+
for (const [k, pr] of Object.entries(doc.prs ?? {})) {
|
|
17567
|
+
const n = k.replace(/^#/, "");
|
|
17568
|
+
if (!/^\d+$/.test(n))
|
|
17569
|
+
continue;
|
|
17570
|
+
out[n] = { ...out[n], ...pr };
|
|
17571
|
+
}
|
|
17572
|
+
if (repo) {
|
|
17573
|
+
for (const n of Object.keys(out))
|
|
17574
|
+
if (!out[n].href)
|
|
17575
|
+
out[n] = { ...out[n], href: `https://github.com/${repo}/pull/${n}` };
|
|
17576
|
+
}
|
|
17577
|
+
return out;
|
|
17578
|
+
}
|
|
17579
|
+
function findPR(prs, n, repo) {
|
|
17580
|
+
const known = prs[n];
|
|
17581
|
+
if (known)
|
|
17582
|
+
return known;
|
|
17583
|
+
return repo ? { href: `https://github.com/${repo}/pull/${n}` } : null;
|
|
17584
|
+
}
|
|
17320
17585
|
var isPullRequest = (href) => /\/(?:pull|merge_requests)\/\d+(?:[/?#]|$)/.test(href);
|
|
17321
17586
|
function withKickers(sections, source, kickers = "pr") {
|
|
17322
17587
|
if (kickers === false)
|
|
@@ -18142,6 +18407,74 @@ function disclosure(host, options) {
|
|
|
18142
18407
|
toggle.addEventListener("click", () => api.toggle());
|
|
18143
18408
|
return api;
|
|
18144
18409
|
}
|
|
18410
|
+
var PR_PATHS = {
|
|
18411
|
+
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",
|
|
18412
|
+
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"
|
|
18413
|
+
};
|
|
18414
|
+
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>`;
|
|
18415
|
+
var PR_STATE = { merged: "Merged", open: "Open", draft: "Draft", closed: "Closed" };
|
|
18416
|
+
function prChip(doc, n, pr) {
|
|
18417
|
+
const chip = el(doc, pr.href ? "a" : "span", "knobs-wt-pr");
|
|
18418
|
+
if (pr.href) {
|
|
18419
|
+
chip.setAttribute("href", pr.href);
|
|
18420
|
+
chip.setAttribute("target", "_blank");
|
|
18421
|
+
chip.setAttribute("rel", "noreferrer");
|
|
18422
|
+
}
|
|
18423
|
+
if (pr.state)
|
|
18424
|
+
chip.dataset.state = pr.state;
|
|
18425
|
+
chip.innerHTML = prGlyph(pr.state === "merged");
|
|
18426
|
+
chip.append(text(doc, "span", "", `#${n}`));
|
|
18427
|
+
const tip = [`#${n}`, pr.title, pr.state ? PR_STATE[pr.state] : ""].filter(Boolean).join(" · ");
|
|
18428
|
+
chip.title = tip;
|
|
18429
|
+
chip.setAttribute("aria-label", `Pull request ${tip}`);
|
|
18430
|
+
return chip;
|
|
18431
|
+
}
|
|
18432
|
+
var PR_URL = /^https?:\/\/[^/]+\/.+\/(?:pull|merge_requests)\/(\d+)\/?(?:[?#].*)?$/;
|
|
18433
|
+
var PR_REF = /(^|[^\w&/#])#(\d{3,7})\b/g;
|
|
18434
|
+
function linkPRs(root, find) {
|
|
18435
|
+
const doc = root.ownerDocument;
|
|
18436
|
+
for (const a of Array.from(root.querySelectorAll("a[href]"))) {
|
|
18437
|
+
if (a.classList.contains("knobs-wt-pr") || a.closest(".knobs-wt-slot,button"))
|
|
18438
|
+
continue;
|
|
18439
|
+
const n = PR_URL.exec(a.getAttribute("href") ?? "")?.[1];
|
|
18440
|
+
const words = a.textContent?.trim() ?? "";
|
|
18441
|
+
if (!n || !(words === `#${n}` || words === n || words === a.getAttribute("href")))
|
|
18442
|
+
continue;
|
|
18443
|
+
a.replaceWith(prChip(doc, n, { href: a.href, ...find(n) }));
|
|
18444
|
+
}
|
|
18445
|
+
const walker = doc.createTreeWalker(root, 5);
|
|
18446
|
+
const hits = [];
|
|
18447
|
+
for (let t = walker.nextNode();t; t = walker.nextNode()) {
|
|
18448
|
+
if (t.nodeType !== 3)
|
|
18449
|
+
continue;
|
|
18450
|
+
const p = t.parentElement;
|
|
18451
|
+
if (!p || p.closest("a,code,pre,button,.knobs-wt-slot,.knobs-wt-pr"))
|
|
18452
|
+
continue;
|
|
18453
|
+
PR_REF.lastIndex = 0;
|
|
18454
|
+
if (PR_REF.test(t.nodeValue ?? ""))
|
|
18455
|
+
hits.push(t);
|
|
18456
|
+
}
|
|
18457
|
+
for (const t of hits) {
|
|
18458
|
+
const value = t.nodeValue ?? "";
|
|
18459
|
+
const frag = doc.createDocumentFragment();
|
|
18460
|
+
let last = 0;
|
|
18461
|
+
let changed = false;
|
|
18462
|
+
PR_REF.lastIndex = 0;
|
|
18463
|
+
for (let m = PR_REF.exec(value);m; m = PR_REF.exec(value)) {
|
|
18464
|
+
const pr = find(m[2]);
|
|
18465
|
+
if (!pr)
|
|
18466
|
+
continue;
|
|
18467
|
+
const at = m.index + m[1].length;
|
|
18468
|
+
frag.append(value.slice(last, at), prChip(doc, m[2], pr));
|
|
18469
|
+
last = at + m[2].length + 1;
|
|
18470
|
+
changed = true;
|
|
18471
|
+
}
|
|
18472
|
+
if (!changed)
|
|
18473
|
+
continue;
|
|
18474
|
+
frag.append(value.slice(last));
|
|
18475
|
+
t.replaceWith(frag);
|
|
18476
|
+
}
|
|
18477
|
+
}
|
|
18145
18478
|
|
|
18146
18479
|
// src/ui/walkthrough-media.ts
|
|
18147
18480
|
init_compare_model();
|
|
@@ -18159,7 +18492,7 @@ var fade2 = "-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 1.5em)
|
|
|
18159
18492
|
var walkthroughCss = `
|
|
18160
18493
|
:where(.knobs-walkthrough,.knobs-walkthrough *,.knobs-wt-slot,.knobs-wt-slot *){box-sizing:border-box}
|
|
18161
18494
|
.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;
|
|
18162
|
-
--kw-measure:var(--k-walkthrough-measure,68ch);--kw-width:var(--k-walkthrough-width,
|
|
18495
|
+
--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));
|
|
18163
18496
|
--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);
|
|
18164
18497
|
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;
|
|
18165
18498
|
letter-spacing:normal;-webkit-font-smoothing:antialiased}
|
|
@@ -18172,44 +18505,57 @@ var walkthroughCss = `
|
|
|
18172
18505
|
: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;
|
|
18173
18506
|
background:transparent;border:0;border-radius:0;margin:0;padding:0;min-width:0;box-shadow:none;outline:none}
|
|
18174
18507
|
|
|
18175
|
-
/* ——— layout:
|
|
18176
|
-
|
|
18177
|
-
|
|
18508
|
+
/* ——— layout: the contents rail sticky at the left edge (right if asked), and the column centred
|
|
18509
|
+
in the room beside it, up to --kw-width: media take the column, prose stops at the measure.
|
|
18510
|
+
One column where it's narrow ——— */
|
|
18511
|
+
.knobs-wt-layout{display:grid;grid-template-columns:var(--kw-rail-w) minmax(0,1fr);gap:var(--_k-space-column);align-items:start}
|
|
18512
|
+
.knobs-walkthrough[data-rail="right"] .knobs-wt-layout{grid-template-columns:minmax(0,1fr) var(--kw-rail-w)}
|
|
18178
18513
|
.knobs-walkthrough[data-rail="right"] .knobs-wt-main{grid-column:1;grid-row:1}
|
|
18179
18514
|
.knobs-walkthrough[data-rail="right"] .knobs-wt-rail{grid-column:2;grid-row:1}
|
|
18180
|
-
.knobs-walkthrough[data-rail="
|
|
18181
|
-
.knobs-
|
|
18182
|
-
.knobs-wt-main{display:flex;flex-direction:column;gap:var(--_k-space-group);min-width:0;padding-bottom:12px}
|
|
18515
|
+
.knobs-walkthrough[data-rail="none"] .knobs-wt-layout{grid-template-columns:minmax(0,1fr)}
|
|
18516
|
+
.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}
|
|
18183
18517
|
/* The intro, the updates and each section: a section's room apart, the hairline in the middle. */
|
|
18184
18518
|
.knobs-wt-flow{display:flex;flex-direction:column;gap:calc(var(--_k-space-section) / 2);min-width:0}
|
|
18185
18519
|
.knobs-wt-flow>*+*{padding-top:calc(var(--_k-space-section) / 2);box-shadow:inset 0 1px 0 var(--_k-border-subtle)}
|
|
18186
|
-
@container knobs-wt (max-width:
|
|
18520
|
+
@container knobs-wt (max-width:960px){.knobs-walkthrough[data-rail] .knobs-wt-layout{grid-template-columns:minmax(0,1fr);gap:var(--_k-space-title)}
|
|
18187
18521
|
.knobs-walkthrough[data-rail] .knobs-wt-layout>*{grid-column:auto;grid-row:auto}}
|
|
18188
18522
|
|
|
18189
18523
|
/* ——— one line of facts: small, faint, joined by ·, its trailing edge fading ——— */
|
|
18190
18524
|
.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}}
|
|
18191
18525
|
.knobs-wt-metaline>*{flex:none}
|
|
18192
18526
|
.knobs-wt-metaline>*+*::before{content:"·";margin-right:var(--_k-space-inside);color:var(--_k-fg-3)}
|
|
18193
|
-
.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)}
|
|
18194
|
-
.knobs-walkthrough .knobs-wt-metaline a:hover{color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
|
|
18527
|
+
.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)}
|
|
18528
|
+
.knobs-walkthrough .knobs-wt-metaline a:not(.knobs-wt-pr):hover{color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
|
|
18195
18529
|
.knobs-wt-metaline svg{align-self:center;width:12px;height:12px;margin-left:2px}
|
|
18530
|
+
/* A pull request: a small chip, its state's icon and number in a quiet tint of the state (open
|
|
18531
|
+
the success hue, merged the violet series, draft and closed neutral), its title on hover. */
|
|
18532
|
+
.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;
|
|
18533
|
+
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;
|
|
18534
|
+
transition:background calc(var(--_k-hover-out) * 1ms)}
|
|
18535
|
+
.knobs-ui .knobs-wt-pr[data-state="open"]{--kw-pr:color-mix(in oklch,var(--_k-ok) 60%,var(--_k-fg-2))}
|
|
18536
|
+
.knobs-ui .knobs-wt-pr[data-state="merged"]{--kw-pr:color-mix(in oklch,var(--_k-series-5) 60%,var(--_k-fg-2))}
|
|
18537
|
+
.knobs-ui .knobs-wt-pr:is([data-state="draft"],[data-state="closed"]){--kw-pr:var(--_k-fg-3)}
|
|
18538
|
+
.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)}
|
|
18539
|
+
.knobs-wt-pr svg{flex:none}
|
|
18540
|
+
.knobs-wt-metaline .knobs-wt-pr{align-self:center;margin:0}
|
|
18196
18541
|
.knobs-wt-kicker-lead{color:var(--_k-fg);font-weight:var(--kw-strong)}
|
|
18197
18542
|
.knobs-wt-metaline [data-status="on"]{color:var(--_k-fg)}
|
|
18198
18543
|
.knobs-wt-metaline [data-status="warn"]{color:var(--_k-warn)}
|
|
18199
18544
|
|
|
18200
18545
|
/* ——— the page's head ——— */
|
|
18201
18546
|
.knobs-wt-head{display:flex;flex-direction:column;gap:var(--_k-space-title)}
|
|
18202
|
-
.knobs-wt-title{font-size:var(--_k-type-
|
|
18547
|
+
.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}
|
|
18203
18548
|
.knobs-wt-summary{max-width:var(--kw-measure);color:var(--_k-fg-2);text-wrap:pretty}
|
|
18204
18549
|
.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)}
|
|
18205
18550
|
.knobs-wt-meta>span{display:inline-flex;gap:var(--_k-space-inside);white-space:nowrap}
|
|
18206
18551
|
.knobs-wt-meta b{font-weight:var(--_k-weight);color:var(--_k-fg-2);font-family:var(--_k-mono)}
|
|
18207
18552
|
|
|
18208
|
-
/* ——— a section: a PR or topic. Its kicker leads the meta line
|
|
18553
|
+
/* ——— a section: a PR or topic. Its kicker leads the meta line, its title under it (16/24, under
|
|
18554
|
+
the page's 20/28), both on the column's edge ——— */
|
|
18209
18555
|
.knobs-wt-section{display:flex;flex-direction:column;gap:var(--_k-space-group);min-width:0}
|
|
18210
18556
|
.knobs-wt-sintro{display:flex;flex-direction:column;gap:var(--_k-space-title);min-width:0}
|
|
18211
18557
|
.knobs-wt-shead{display:flex;flex-direction:column;gap:var(--_k-space-inside);min-width:0;scroll-margin-top:16px}
|
|
18212
|
-
.knobs-wt-heading{font-size:var(--_k-type-
|
|
18558
|
+
.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}
|
|
18213
18559
|
.knobs-wt-ssum{max-width:var(--kw-measure);color:var(--_k-fg-2);text-wrap:pretty}
|
|
18214
18560
|
.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;
|
|
18215
18561
|
--kw-tone:var(--_k-fg-2);color:var(--kw-tone);background:color-mix(in srgb,var(--kw-tone) 14%,transparent)}
|
|
@@ -18270,6 +18616,8 @@ var walkthroughCss = `
|
|
|
18270
18616
|
.knobs-wt-disc[data-open]>.knobs-wt-disc-body:not([data-moving]){overflow:visible}
|
|
18271
18617
|
.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)}
|
|
18272
18618
|
.knobs-wt-disc-content{padding-top:var(--_k-space-inside)}
|
|
18619
|
+
/* "More" under a caption or summary: the rest of its words, in the same voice. */
|
|
18620
|
+
.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}
|
|
18273
18621
|
/* A trail sits on the toggle's label edge: faint lines, the time trailing. */
|
|
18274
18622
|
.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}
|
|
18275
18623
|
.knobs-wt-trail li{display:flex;align-items:baseline;gap:var(--_k-space-sibling);min-width:0;white-space:nowrap}
|
|
@@ -18387,32 +18735,28 @@ var walkthroughCss = `
|
|
|
18387
18735
|
.knobs-wt-ocap{color:var(--_k-fg-2);max-width:var(--kw-measure)}
|
|
18388
18736
|
|
|
18389
18737
|
/* ——— the rail: progress, contents, export ——— */
|
|
18390
|
-
.knobs-wt-rail{position:sticky;top:
|
|
18738
|
+
.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)}
|
|
18391
18739
|
.knobs-wt-progress{display:flex;flex-direction:column;gap:var(--_k-space-sibling)}
|
|
18392
18740
|
.knobs-wt-count{display:flex;align-items:baseline;justify-content:space-between;color:var(--_k-fg-2)}
|
|
18393
18741
|
.knobs-wt-count b{color:var(--_k-fg);font-weight:var(--kw-strong)}
|
|
18394
18742
|
.knobs-wt-bar{position:relative;height:4px;border-radius:2px;overflow:hidden;background:var(--_k-fill)}
|
|
18395
18743
|
.knobs-wt-fill{position:absolute;inset:0;border-radius:inherit;background:var(--_k-accent);transform-origin:0 50%;transform:scaleX(var(--kw-done,0))}
|
|
18396
18744
|
.knobs-walkthrough[data-settled] .knobs-wt-fill{transition:transform var(--kw-calm-ms) var(--kw-calm)}
|
|
18397
|
-
/* The contents:
|
|
18398
|
-
steps under them (20 high, half of it), bled out so their text sits on the
|
|
18399
|
-
selection pill slides to the current step, and base rows never brighten
|
|
18745
|
+
/* The contents: every row on one text edge and in one colour, title rows (32 high, the control
|
|
18746
|
+
radius) and the steps under them (20 high, half of it), bled out so their text sits on the
|
|
18747
|
+
rail's edge; the selection pill slides to the current step, and base rows never brighten. */
|
|
18400
18748
|
.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}
|
|
18401
18749
|
.knobs-wt-toc::-webkit-scrollbar{display:none}
|
|
18402
18750
|
.knobs-wt-toc-group{display:flex;flex-direction:column;min-width:0}
|
|
18403
|
-
.knobs-ui .knobs-wt-toc-row{position:relative;display:grid;grid-template-columns:
|
|
18404
|
-
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(--
|
|
18751
|
+
.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;
|
|
18752
|
+
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;
|
|
18405
18753
|
transition:color calc(var(--_k-hover-out) * 1ms)}
|
|
18406
|
-
.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)
|
|
18407
|
-
.knobs-wt-toc[data-bare] .knobs-wt-toc-row{grid-template-columns:minmax(0,1fr) auto}
|
|
18408
|
-
.knobs-wt-toc[data-bare] .knobs-wt-toc-n{display:none}
|
|
18754
|
+
.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)}
|
|
18409
18755
|
.knobs-ui .knobs-wt-toc-row:is(:hover,:focus-visible){color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
|
|
18410
|
-
.knobs-ui .knobs-wt-toc-sub:is(:hover,:focus-visible){color:var(--_k-fg-2)}
|
|
18411
18756
|
.knobs-ui .knobs-wt-toc-row:focus-visible{box-shadow:inset 0 0 0 1px var(--_k-focus)}
|
|
18412
|
-
.knobs-wt-toc-
|
|
18413
|
-
.knobs-wt-toc-row[data-active] .knobs-wt-toc-n{color:var(--_k-fg-2)}
|
|
18757
|
+
.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)}
|
|
18414
18758
|
.knobs-wt-toc-label{min-width:0;overflow:hidden;${fade2}}
|
|
18415
|
-
.knobs-pick-copy .knobs-wt-toc-
|
|
18759
|
+
.knobs-pick-copy .knobs-wt-toc-end{color:var(--_k-fg-2)}
|
|
18416
18760
|
/* A step's state: an open ring, a filled dot once answered, nothing to answer a faint ring. */
|
|
18417
18761
|
.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);
|
|
18418
18762
|
transition:background var(--kw-snappy-ms) var(--kw-snappy),box-shadow var(--kw-snappy-ms) var(--kw-snappy)}
|
|
@@ -18427,10 +18771,12 @@ var walkthroughCss = `
|
|
|
18427
18771
|
.knobs-ui .knobs-wt-btn[data-primary]:is(:hover,:focus-visible){background:color-mix(in srgb,var(--_k-fg) 16%,transparent)}
|
|
18428
18772
|
.knobs-wt-keys{color:var(--_k-fg-3);font-size:var(--_k-type-small);line-height:var(--_k-leading-small)}
|
|
18429
18773
|
.knobs-wt-keys kbd{font:inherit;color:var(--_k-fg-2)}
|
|
18430
|
-
/* Narrow: the rail becomes a bar along the top
|
|
18431
|
-
|
|
18774
|
+
/* Narrow: the contents go; with review on, the rail becomes a bar along the top with the
|
|
18775
|
+
progress and the export. */
|
|
18776
|
+
@container knobs-wt (max-width:960px){
|
|
18432
18777
|
.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)}
|
|
18433
18778
|
.knobs-wt-progress{flex:1}.knobs-wt-toc,.knobs-wt-keys{display:none}
|
|
18779
|
+
.knobs-walkthrough:not([data-review]) .knobs-wt-rail{display:none}
|
|
18434
18780
|
.knobs-wt-step,.knobs-wt-shead,.knobs-wt-feed{scroll-margin-top:60px}
|
|
18435
18781
|
}
|
|
18436
18782
|
|
|
@@ -18440,9 +18786,14 @@ var walkthroughCss = `
|
|
|
18440
18786
|
well for hairlines, so nothing draws a fill and a ring of its own there ——— */
|
|
18441
18787
|
.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);
|
|
18442
18788
|
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);
|
|
18443
|
-
--kw-good:var(--k-walkthrough-good,var(--_k-ok));--kw-info:var(--k-walkthrough-info,var(--_k-info))}
|
|
18444
|
-
.knobs-wt-slot
|
|
18789
|
+
--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)}
|
|
18790
|
+
.knobs-surface .knobs-wt-slot{--kw-frame-r:calc(var(--_k-radius-panel) - 4px)}
|
|
18791
|
+
/* A slot's label: sentence case as written, small and faint, never a heading. */
|
|
18792
|
+
.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}}
|
|
18445
18793
|
.knobs-wt-body{min-width:0}
|
|
18794
|
+
/* One frame for every slot that has one (a picture, a video, a code block, a compare's stage, a
|
|
18795
|
+
slot still loading): the panel radius and a quiet fill, no ring; concentric on a card. */
|
|
18796
|
+
.knobs-wt-slot .knobs-cmp-stage{border-radius:var(--kw-frame-r)}
|
|
18446
18797
|
/* A compare is exactly as tall as its pictures at this width, so there's no letterbox: the two
|
|
18447
18798
|
sides edge to edge (side) or one (overlaid), plus its mode bar, the timeline and their two gaps
|
|
18448
18799
|
(92 px), and the frame strip with its gap (158 px). */
|
|
@@ -18450,11 +18801,11 @@ var walkthroughCss = `
|
|
|
18450
18801
|
.knobs-wt-fit[data-overlay]{height:calc(100cqw / var(--kw-aspect,1.7778) + var(--kw-chrome,92px))}
|
|
18451
18802
|
.knobs-wt-fit[data-strip]{--kw-chrome:158px}
|
|
18452
18803
|
.knobs-wt-fit[data-settled]{transition:height var(--k-wt-calm-ms,${calm.duration}ms) ${calm.easing}}
|
|
18453
|
-
.knobs-wt-frame{position:relative;width:100%;overflow:hidden;border-radius:
|
|
18804
|
+
.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)}
|
|
18454
18805
|
.knobs-wt-frame>:is(video,img){position:absolute;inset:0;display:block;width:100%;height:100%;object-fit:contain;margin:0;border:0}
|
|
18455
18806
|
.knobs-wt-frame[data-natural]{aspect-ratio:auto;background:transparent}
|
|
18456
18807
|
.knobs-wt-frame[data-natural]>img{position:static;height:auto}
|
|
18457
|
-
.knobs-wt-code{margin:0;padding:var(--_k-pad-card);max-height:420px;overflow:auto;border-radius:
|
|
18808
|
+
.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);
|
|
18458
18809
|
font:var(--_k-type-label)/var(--_k-leading-body) var(--_k-mono);white-space:pre;tab-size:2;scrollbar-width:thin}
|
|
18459
18810
|
.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)}
|
|
18460
18811
|
.knobs-wt-code[data-terminal]{white-space:pre-wrap;overflow-wrap:anywhere}
|
|
@@ -18464,8 +18815,8 @@ var walkthroughCss = `
|
|
|
18464
18815
|
.knobs-wt-metric{display:flex;flex-direction:column;gap:var(--_k-space-sibling)}
|
|
18465
18816
|
.knobs-wt-mlabel{color:var(--_k-fg-2)}
|
|
18466
18817
|
.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"}
|
|
18467
|
-
.knobs-wt-mfrom{color:var(--_k-fg-3)
|
|
18468
|
-
.knobs-wt-mto{font-size:var(--_k-type-
|
|
18818
|
+
.knobs-wt-mfrom{color:var(--_k-fg-3)}
|
|
18819
|
+
.knobs-wt-mto{font-size:var(--_k-type-title);line-height:var(--_k-leading-title);font-weight:var(--_k-weight-label)}
|
|
18469
18820
|
.knobs-wt-mof{color:var(--_k-fg-3)}
|
|
18470
18821
|
.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)}
|
|
18471
18822
|
.knobs-wt-mdelta[data-tone="good"]{--kw-tone:var(--kw-good)}
|
|
@@ -18480,7 +18831,7 @@ var walkthroughCss = `
|
|
|
18480
18831
|
.knobs-wt-callout[data-tone="warn"]{--kw-tone:var(--_k-warn)}
|
|
18481
18832
|
.knobs-wt-callout[data-tone="bad"]{--kw-tone:var(--_k-danger)}
|
|
18482
18833
|
.knobs-wt-text{max-width:var(--kw-measure);color:var(--_k-fg-2)}
|
|
18483
|
-
.knobs-wt-hold{border-radius:
|
|
18834
|
+
.knobs-wt-hold{border-radius:var(--kw-frame-r);background:var(--_k-fill)}
|
|
18484
18835
|
.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)}
|
|
18485
18836
|
|
|
18486
18837
|
/* ——— data blocks: history, stack, table, chapters. On the step's card, so no card of their own:
|
|
@@ -18592,7 +18943,7 @@ var walkthroughCss = `
|
|
|
18592
18943
|
.knobs-wt-table{max-height:520px;overflow:auto;overscroll-behavior-x:contain;outline:none;scrollbar-width:thin;scrollbar-color:transparent transparent;
|
|
18593
18944
|
transition:scrollbar-color calc(var(--_k-hover-out) * 1ms)}
|
|
18594
18945
|
.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)}
|
|
18595
|
-
.knobs-wt-table:focus-visible{box-shadow:inset 0 0 0 2px var(--_k-focus);border-radius:
|
|
18946
|
+
.knobs-wt-table:focus-visible{box-shadow:inset 0 0 0 2px var(--_k-focus);border-radius:var(--kw-frame-r)}
|
|
18596
18947
|
@supports (animation-timeline:scroll()){
|
|
18597
18948
|
.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;
|
|
18598
18949
|
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%;
|
|
@@ -19206,16 +19557,25 @@ function tasteTest(container, options) {
|
|
|
19206
19557
|
...o.media !== undefined ? { media: fillMedia(o.media) } : {},
|
|
19207
19558
|
suggested: o.suggested === true
|
|
19208
19559
|
});
|
|
19209
|
-
|
|
19560
|
+
const pickable = options.pickable !== false;
|
|
19561
|
+
let picked = pickable ? options.picked ?? null : null;
|
|
19210
19562
|
const root = el2(doc, "div", "knobs-wt-options");
|
|
19211
|
-
|
|
19212
|
-
|
|
19563
|
+
if (pickable) {
|
|
19564
|
+
root.setAttribute("role", "radiogroup");
|
|
19565
|
+
root.setAttribute("aria-label", "Options");
|
|
19566
|
+
} else
|
|
19567
|
+
root.dataset.static = "";
|
|
19213
19568
|
root.style.setProperty("--kw-n", String(Math.min(list.length, 3)));
|
|
19214
19569
|
if (!container.closest(".knobs-ui"))
|
|
19215
19570
|
applyLook2(root, options);
|
|
19216
19571
|
const slots = [];
|
|
19217
19572
|
const tiles = list.map((o, i) => {
|
|
19218
19573
|
const tile = el2(doc, "div", "knobs-wt-option");
|
|
19574
|
+
if (!pickable) {
|
|
19575
|
+
const name = el2(doc, "span", "knobs-wt-slot-label");
|
|
19576
|
+
name.textContent = o.label;
|
|
19577
|
+
tile.append(name);
|
|
19578
|
+
}
|
|
19219
19579
|
const pick = el2(doc, "button", "knobs-wt-pick");
|
|
19220
19580
|
pick.type = "button";
|
|
19221
19581
|
pick.setAttribute("role", "radio");
|
|
@@ -19244,7 +19604,8 @@ function tasteTest(container, options) {
|
|
|
19244
19604
|
api.pick(next);
|
|
19245
19605
|
options.onPick?.(next);
|
|
19246
19606
|
});
|
|
19247
|
-
|
|
19607
|
+
if (pickable)
|
|
19608
|
+
tile.append(pick);
|
|
19248
19609
|
if (o.media)
|
|
19249
19610
|
slots.push(mediaSlot(tile, {
|
|
19250
19611
|
...look,
|
|
@@ -19327,7 +19688,8 @@ function walkthrough(container, options) {
|
|
|
19327
19688
|
let tocRows = [];
|
|
19328
19689
|
let discs = [];
|
|
19329
19690
|
let full = null;
|
|
19330
|
-
const
|
|
19691
|
+
const review = options.review === true;
|
|
19692
|
+
const noteMode = review ? options.noteBar ?? "collapsed" : "hidden";
|
|
19331
19693
|
let reserve = noteBarReserve(52, DOCK_INSET, DOCK_GAP);
|
|
19332
19694
|
const viewOf = (key) => views.find((v) => v.step.key === key);
|
|
19333
19695
|
const feedback = () => collectFeedback(page, state);
|
|
@@ -20063,7 +20425,14 @@ function walkthrough(container, options) {
|
|
|
20063
20425
|
line.append(s);
|
|
20064
20426
|
return s;
|
|
20065
20427
|
};
|
|
20428
|
+
const findPRIn = (n) => findPR(page.prs, n, page.repo);
|
|
20066
20429
|
const metaLink = (line, text, href) => {
|
|
20430
|
+
const n = prNumber(href);
|
|
20431
|
+
if (n) {
|
|
20432
|
+
const chip = prChip(doc, n, { href, ...findPRIn(n) });
|
|
20433
|
+
line.append(chip);
|
|
20434
|
+
return chip;
|
|
20435
|
+
}
|
|
20067
20436
|
const a = el2(doc, "a");
|
|
20068
20437
|
a.href = href;
|
|
20069
20438
|
a.target = "_blank";
|
|
@@ -20150,8 +20519,9 @@ function walkthrough(container, options) {
|
|
|
20150
20519
|
views = [];
|
|
20151
20520
|
root.textContent = "";
|
|
20152
20521
|
root.removeAttribute("data-settled");
|
|
20153
|
-
root.dataset.rail = options.rail === false ? "none" : options.rail === "
|
|
20522
|
+
root.dataset.rail = options.rail === false ? "none" : options.rail === "right" ? "right" : "left";
|
|
20154
20523
|
root.toggleAttribute("data-cards", options.cards === true);
|
|
20524
|
+
root.toggleAttribute("data-review", review);
|
|
20155
20525
|
const layout = el2(doc, "div", "knobs-wt-layout");
|
|
20156
20526
|
const main = el2(doc, "div", "knobs-wt-main");
|
|
20157
20527
|
const flow = el2(doc, "div", "knobs-wt-flow");
|
|
@@ -20161,6 +20531,8 @@ function walkthrough(container, options) {
|
|
|
20161
20531
|
head.append(h1);
|
|
20162
20532
|
if (page.summary)
|
|
20163
20533
|
head.append(prose2(doc, "p", "knobs-wt-summary", page.summary));
|
|
20534
|
+
if (page.more)
|
|
20535
|
+
head.append(more(page.more));
|
|
20164
20536
|
if (page.meta.length) {
|
|
20165
20537
|
const meta = el2(doc, "div", "knobs-wt-meta");
|
|
20166
20538
|
for (const m of page.meta) {
|
|
@@ -20204,28 +20576,28 @@ function walkthrough(container, options) {
|
|
|
20204
20576
|
count = fill = tocEl = null;
|
|
20205
20577
|
if (options.rail !== false) {
|
|
20206
20578
|
rail = el2(doc, "aside", "knobs-wt-rail");
|
|
20207
|
-
rail.setAttribute("aria-label", "
|
|
20208
|
-
const progress = el2(doc, "div", "knobs-wt-progress");
|
|
20209
|
-
count = el2(doc, "div", "knobs-wt-count");
|
|
20210
|
-
const meter = el2(doc, "div", "knobs-wt-bar");
|
|
20211
|
-
fill = el2(doc, "div", "knobs-wt-fill");
|
|
20212
|
-
meter.append(fill);
|
|
20213
|
-
progress.append(count, meter);
|
|
20579
|
+
rail.setAttribute("aria-label", "Contents");
|
|
20214
20580
|
tocEl = el2(doc, "nav", "knobs-wt-toc");
|
|
20215
20581
|
tocEl.setAttribute("aria-label", "Contents");
|
|
20216
|
-
const ns = page.sections.map((s) => s.kicker && s.link ? s.link.label : "");
|
|
20217
|
-
const nw = Math.max(0, ...ns.map((n) => n.length));
|
|
20218
|
-
tocEl.style.setProperty("--kw-toc-n", nw ? `${nw + 0.5}ch` : "0px");
|
|
20219
|
-
if (!nw)
|
|
20220
|
-
tocEl.dataset.bare = "";
|
|
20221
20582
|
if (feedEl) {
|
|
20222
20583
|
const group = el2(doc, "div", "knobs-wt-toc-group");
|
|
20223
20584
|
const target = feedEl;
|
|
20224
|
-
group.append(tocRow("Latest",
|
|
20585
|
+
group.append(tocRow("Latest", false, () => target.scrollIntoView({ block: "start", behavior: reduced() ? "auto" : "smooth" })));
|
|
20225
20586
|
tocEl.append(group);
|
|
20226
20587
|
}
|
|
20227
|
-
|
|
20228
|
-
|
|
20588
|
+
if (review) {
|
|
20589
|
+
const progress = el2(doc, "div", "knobs-wt-progress");
|
|
20590
|
+
count = el2(doc, "div", "knobs-wt-count");
|
|
20591
|
+
const meter = el2(doc, "div", "knobs-wt-bar");
|
|
20592
|
+
fill = el2(doc, "div", "knobs-wt-fill");
|
|
20593
|
+
meter.append(fill);
|
|
20594
|
+
progress.append(count, meter);
|
|
20595
|
+
rail.append(progress);
|
|
20596
|
+
}
|
|
20597
|
+
rail.append(tocEl);
|
|
20598
|
+
if (review)
|
|
20599
|
+
rail.append(actions(true));
|
|
20600
|
+
if (review && options.keys !== false) {
|
|
20229
20601
|
const keys = el2(doc, "div", "knobs-wt-keys");
|
|
20230
20602
|
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" : "");
|
|
20231
20603
|
rail.append(keys);
|
|
@@ -20235,14 +20607,15 @@ function walkthrough(container, options) {
|
|
|
20235
20607
|
const sec = el2(doc, "section", "knobs-wt-section");
|
|
20236
20608
|
sec.id = `wt-${section.id}`;
|
|
20237
20609
|
const sh = el2(doc, "header", "knobs-wt-shead");
|
|
20238
|
-
|
|
20610
|
+
const status = review ? section.status : undefined;
|
|
20611
|
+
if (section.kicker || section.link || status) {
|
|
20239
20612
|
const meta = metaLine("knobs-wt-kicker");
|
|
20240
20613
|
if (section.kicker)
|
|
20241
20614
|
metaText(meta, section.kicker, "knobs-wt-kicker-lead");
|
|
20242
20615
|
if (section.link)
|
|
20243
20616
|
metaLink(meta, section.link.label, section.link.href);
|
|
20244
|
-
if (
|
|
20245
|
-
const s = statusLook(
|
|
20617
|
+
if (status) {
|
|
20618
|
+
const s = statusLook(status);
|
|
20246
20619
|
metaText(meta, s.label, "knobs-wt-status").dataset.tone = s.tone;
|
|
20247
20620
|
}
|
|
20248
20621
|
sh.append(meta);
|
|
@@ -20254,13 +20627,14 @@ function walkthrough(container, options) {
|
|
|
20254
20627
|
intro.append(sh);
|
|
20255
20628
|
if (section.summary)
|
|
20256
20629
|
intro.append(prose2(doc, "p", "knobs-wt-ssum", section.summary));
|
|
20630
|
+
if (section.more)
|
|
20631
|
+
intro.append(more(section.more));
|
|
20257
20632
|
sec.append(intro);
|
|
20258
20633
|
let group = null;
|
|
20259
20634
|
if (tocEl) {
|
|
20260
20635
|
group = el2(doc, "div", "knobs-wt-toc-group");
|
|
20261
20636
|
const first = section.steps[0]?.key;
|
|
20262
|
-
|
|
20263
|
-
group.append(tocRow(plainMarkdown(section.title), n, "", false, () => first ? go(first) : undefined, section.id));
|
|
20637
|
+
group.append(tocRow(plainMarkdown(section.title), false, () => first ? go(first) : undefined, section.id));
|
|
20264
20638
|
tocEl.append(group);
|
|
20265
20639
|
}
|
|
20266
20640
|
for (const step of section.steps) {
|
|
@@ -20270,6 +20644,7 @@ function walkthrough(container, options) {
|
|
|
20270
20644
|
}
|
|
20271
20645
|
flow.append(sec);
|
|
20272
20646
|
}
|
|
20647
|
+
linkPRs(flow, findPRIn);
|
|
20273
20648
|
main.append(flow);
|
|
20274
20649
|
if (noteMode !== "hidden")
|
|
20275
20650
|
main.append(bar.dock);
|
|
@@ -20298,19 +20673,25 @@ function walkthrough(container, options) {
|
|
|
20298
20673
|
root.dataset.settled = "";
|
|
20299
20674
|
}));
|
|
20300
20675
|
};
|
|
20301
|
-
const
|
|
20676
|
+
const more = (text) => {
|
|
20677
|
+
const box = el2(doc, "div", "knobs-wt-more");
|
|
20678
|
+
const body = el2(doc, "div", "knobs-wt-more-text");
|
|
20679
|
+
for (const para of text.split(/\n\s*\n/))
|
|
20680
|
+
if (para.trim())
|
|
20681
|
+
body.append(prose2(doc, "p", "", para.trim()));
|
|
20682
|
+
discs.push(disclosure(box, { label: "More", content: body }));
|
|
20683
|
+
return box;
|
|
20684
|
+
};
|
|
20685
|
+
const tocRow = (label, sub, run, section) => {
|
|
20302
20686
|
const b = el2(doc, "button", sub ? "knobs-wt-toc-row knobs-wt-toc-sub" : "knobs-wt-toc-row");
|
|
20303
20687
|
b.type = "button";
|
|
20304
20688
|
if (section)
|
|
20305
20689
|
b.dataset.section = section;
|
|
20306
|
-
const ncol = el2(doc, "span", "knobs-wt-toc-n");
|
|
20307
|
-
ncol.textContent = n;
|
|
20308
20690
|
const t = el2(doc, "span", "knobs-wt-toc-label");
|
|
20309
20691
|
t.textContent = label;
|
|
20310
20692
|
const e = el2(doc, "span", "knobs-wt-toc-end");
|
|
20311
|
-
e.textContent = end;
|
|
20312
20693
|
b.title = label;
|
|
20313
|
-
b.append(
|
|
20694
|
+
b.append(t, e);
|
|
20314
20695
|
b.addEventListener("mousedown", (ev) => ev.preventDefault());
|
|
20315
20696
|
b.addEventListener("click", run);
|
|
20316
20697
|
tocRows.push(b);
|
|
@@ -20368,33 +20749,26 @@ function walkthrough(container, options) {
|
|
|
20368
20749
|
head.append(h3);
|
|
20369
20750
|
if (step.caption)
|
|
20370
20751
|
head.append(prose2(doc, "p", "knobs-wt-caption", step.caption));
|
|
20752
|
+
if (step.more)
|
|
20753
|
+
head.append(more(step.more));
|
|
20371
20754
|
card.append(head);
|
|
20372
20755
|
const slots = [];
|
|
20373
20756
|
const onEvent = (e) => record({ ...e, step: step.key });
|
|
20374
20757
|
let media = null;
|
|
20375
20758
|
if (step.media.length) {
|
|
20376
|
-
|
|
20377
|
-
|
|
20378
|
-
|
|
20379
|
-
|
|
20380
|
-
slots.push(mediaSlot(g, { ...look, media: m, onEvent, steps: stepLinks }));
|
|
20381
|
-
return g;
|
|
20382
|
-
};
|
|
20383
|
-
const [first, ...rest] = step.media;
|
|
20384
|
-
media = grid(step.more ? [first] : step.media);
|
|
20759
|
+
media = el2(doc, "div", "knobs-wt-media");
|
|
20760
|
+
media.style.setProperty("--kw-n", String(Math.min(step.media.length, step.columns)));
|
|
20761
|
+
for (const m of step.media)
|
|
20762
|
+
slots.push(mediaSlot(media, { ...look, media: m, onEvent, steps: stepLinks }));
|
|
20385
20763
|
card.append(media);
|
|
20386
|
-
if (step.more && rest.length) {
|
|
20387
|
-
const holder = el2(doc, "div", "knobs-wt-more");
|
|
20388
|
-
discs.push(disclosure(holder, { label: step.more, count: rest.length, content: grid(rest) }));
|
|
20389
|
-
card.append(holder);
|
|
20390
|
-
}
|
|
20391
20764
|
}
|
|
20392
20765
|
let taste = null;
|
|
20393
|
-
if (step.question)
|
|
20766
|
+
if (step.question && review)
|
|
20394
20767
|
card.append(prose2(doc, "p", "knobs-wt-question", step.question));
|
|
20395
20768
|
if (step.ask === "pick") {
|
|
20396
20769
|
taste = tasteTest(card, {
|
|
20397
20770
|
...look,
|
|
20771
|
+
pickable: review,
|
|
20398
20772
|
options: step.options,
|
|
20399
20773
|
picked: state.steps[step.key]?.pick ?? null,
|
|
20400
20774
|
onPick: (id) => pick(step.key, id),
|
|
@@ -20403,7 +20777,8 @@ function walkthrough(container, options) {
|
|
|
20403
20777
|
media ??= taste.element;
|
|
20404
20778
|
}
|
|
20405
20779
|
const notes = el2(doc, "div", "knobs-wt-notes");
|
|
20406
|
-
|
|
20780
|
+
if (review)
|
|
20781
|
+
card.append(notes);
|
|
20407
20782
|
card.addEventListener("focusin", () => {
|
|
20408
20783
|
if (scroll.to)
|
|
20409
20784
|
return;
|
|
@@ -20413,9 +20788,11 @@ function walkthrough(container, options) {
|
|
|
20413
20788
|
let toc = null;
|
|
20414
20789
|
let glyph = null;
|
|
20415
20790
|
if (group) {
|
|
20416
|
-
toc = tocRow(plainMarkdown(step.title),
|
|
20417
|
-
|
|
20418
|
-
|
|
20791
|
+
toc = tocRow(plainMarkdown(step.title), true, () => go(step.key));
|
|
20792
|
+
if (review) {
|
|
20793
|
+
glyph = el2(doc, "span", "knobs-wt-toc-glyph");
|
|
20794
|
+
toc.querySelector(".knobs-wt-toc-end").append(glyph);
|
|
20795
|
+
}
|
|
20419
20796
|
group.append(toc);
|
|
20420
20797
|
}
|
|
20421
20798
|
return { step, card, media, notes, toc, glyph, taste, slots };
|
|
@@ -20493,7 +20870,7 @@ function walkthrough(container, options) {
|
|
|
20493
20870
|
return;
|
|
20494
20871
|
}
|
|
20495
20872
|
const v = viewOf(current);
|
|
20496
|
-
if (!v || e.shiftKey)
|
|
20873
|
+
if (!v || e.shiftKey || !review)
|
|
20497
20874
|
return;
|
|
20498
20875
|
const choice = digitChoice(v.step, state.steps[v.step.key], key);
|
|
20499
20876
|
if (choice === undefined)
|