@kitlangton/knobs 0.4.0-next.7 → 0.4.0-next.8

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/dist/ui.js CHANGED
@@ -9662,6 +9662,171 @@ var init_compare = __esm(() => {
9662
9662
  };
9663
9663
  });
9664
9664
 
9665
+ // src/ui/player.ts
9666
+ function player(container, options) {
9667
+ const doc = container.ownerDocument;
9668
+ const el = (tag, cls) => {
9669
+ const n = doc.createElement(tag);
9670
+ n.className = cls;
9671
+ return n;
9672
+ };
9673
+ const look = {
9674
+ ...options.preset ? { preset: options.preset } : {},
9675
+ ...options.theme ? { theme: options.theme } : {},
9676
+ ...options.appearance ? { appearance: options.appearance } : {}
9677
+ };
9678
+ const root = el("div", "knobs-player");
9679
+ root.setAttribute("role", "group");
9680
+ root.setAttribute("aria-label", options.label ?? "Video");
9681
+ applyLook2(root, options);
9682
+ const frame = el("div", "knobs-player-frame");
9683
+ frame.style.setProperty("--kp-aspect", String(options.aspect ?? 16 / 9));
9684
+ const v = el("video", "knobs-player-media");
9685
+ v.muted = options.muted ?? true;
9686
+ v.defaultMuted = v.muted;
9687
+ v.playsInline = true;
9688
+ v.preload = "metadata";
9689
+ v.setAttribute("playsinline", "");
9690
+ if (options.poster)
9691
+ v.poster = options.poster;
9692
+ if (options.captions) {
9693
+ const track = doc.createElement("track");
9694
+ track.kind = "captions";
9695
+ track.label = "Captions";
9696
+ track.srclang = doc.documentElement.lang || "en";
9697
+ track.default = true;
9698
+ track.src = options.captions;
9699
+ v.append(track);
9700
+ }
9701
+ frame.append(v);
9702
+ const host = el("div", "knobs-player-line");
9703
+ root.append(frame, host);
9704
+ container.append(root);
9705
+ const markers = (options.chapters ?? []).map((c, i) => ({
9706
+ id: c.id ?? `c${i}`,
9707
+ time: c.start,
9708
+ label: c.label
9709
+ }));
9710
+ const tl = timeline(host, {
9711
+ ...look,
9712
+ variant: "line",
9713
+ duration: 1,
9714
+ fps: options.fps ?? 30,
9715
+ markers,
9716
+ repeat: options.loop ?? true,
9717
+ arrows: { step: 5, shift: 10 },
9718
+ label: options.label ? `${options.label} time` : "Video time",
9719
+ ...options.hints === false ? { hints: false } : {}
9720
+ });
9721
+ const onFrame = () => root.setAttribute("data-ready", "");
9722
+ let pending = null;
9723
+ const seekVideo = (t) => {
9724
+ if (v.readyState < 1)
9725
+ return;
9726
+ if (v.seeking)
9727
+ pending = t;
9728
+ else
9729
+ v.currentTime = t;
9730
+ };
9731
+ let reported = 0;
9732
+ const onSeeked = () => {
9733
+ if (pending !== null) {
9734
+ const t = pending;
9735
+ pending = null;
9736
+ v.currentTime = t;
9737
+ return;
9738
+ }
9739
+ if (!v.paused || tl.playing || Math.abs(v.currentTime - reported) < 0.1)
9740
+ return;
9741
+ reported = v.currentTime;
9742
+ options.onSeek?.(reported);
9743
+ };
9744
+ const onMeta = () => {
9745
+ onFrame();
9746
+ if (v.videoWidth)
9747
+ frame.style.setProperty("--kp-aspect", String(v.videoWidth / v.videoHeight));
9748
+ if (Number.isFinite(v.duration))
9749
+ tl.update({ duration: v.duration });
9750
+ };
9751
+ v.addEventListener("loadedmetadata", onMeta);
9752
+ v.addEventListener("loadeddata", onFrame);
9753
+ v.addEventListener("seeked", onSeeked);
9754
+ const unlisten = tl.listen({
9755
+ onSeek(t, source) {
9756
+ if (source === "play") {
9757
+ if (Math.abs(v.currentTime - t) > 0.25)
9758
+ v.currentTime = t;
9759
+ return;
9760
+ }
9761
+ if (Math.abs(v.currentTime - t) < 1 / (options.fps ?? 30))
9762
+ return;
9763
+ seekVideo(t);
9764
+ },
9765
+ onPlay(on) {
9766
+ if (on)
9767
+ v.play().catch(() => tl.pause());
9768
+ else {
9769
+ v.pause();
9770
+ reported = tl.time;
9771
+ }
9772
+ root.toggleAttribute("data-playing", on);
9773
+ options.onPlay?.(on, tl.time);
9774
+ },
9775
+ onScrub(on) {
9776
+ if (on)
9777
+ v.pause();
9778
+ }
9779
+ });
9780
+ tl.drive(() => {
9781
+ if (v.readyState < 1)
9782
+ return Number.NaN;
9783
+ if (v.seeking)
9784
+ return tl.time;
9785
+ return v.ended ? Number.POSITIVE_INFINITY : v.currentTime;
9786
+ });
9787
+ const onClick = () => tl.focus();
9788
+ frame.addEventListener("click", onClick);
9789
+ v.src = options.src;
9790
+ const api = {
9791
+ element: root,
9792
+ video: v,
9793
+ timeline: tl,
9794
+ get time() {
9795
+ return tl.time;
9796
+ },
9797
+ get playing() {
9798
+ return tl.playing;
9799
+ },
9800
+ get duration() {
9801
+ return Number.isFinite(v.duration) ? v.duration : 0;
9802
+ },
9803
+ play: () => tl.play(),
9804
+ pause: () => tl.pause(),
9805
+ toggle: () => tl.toggle(),
9806
+ seek: (t, o) => tl.seek(t, o?.glide ? { glide: true } : undefined),
9807
+ seekBy: (d) => tl.seek(Math.min(tl.duration, Math.max(0, tl.time + d)), { glide: true }),
9808
+ focus: () => tl.focus(),
9809
+ dispose() {
9810
+ unlisten();
9811
+ tl.drive(null);
9812
+ tl.dispose();
9813
+ v.pause();
9814
+ frame.removeEventListener("click", onClick);
9815
+ v.removeEventListener("loadedmetadata", onMeta);
9816
+ v.removeEventListener("loadeddata", onFrame);
9817
+ v.removeEventListener("seeked", onSeeked);
9818
+ v.removeAttribute("src");
9819
+ v.load();
9820
+ root.remove();
9821
+ }
9822
+ };
9823
+ return api;
9824
+ }
9825
+ var init_player = __esm(() => {
9826
+ init_look();
9827
+ init_timeline();
9828
+ });
9829
+
9665
9830
  // src/ui/scrollbar.ts
9666
9831
  function overlayScrollbar(scroller, content) {
9667
9832
  const doc = scroller.ownerDocument;
@@ -16379,169 +16544,8 @@ init_timeline();
16379
16544
  init_timeline_model();
16380
16545
  init_compare();
16381
16546
  init_compare_strip();
16547
+ init_player();
16382
16548
 
16383
- // src/ui/player.ts
16384
- init_look();
16385
- init_timeline();
16386
- function player(container, options) {
16387
- const doc = container.ownerDocument;
16388
- const el = (tag, cls) => {
16389
- const n = doc.createElement(tag);
16390
- n.className = cls;
16391
- return n;
16392
- };
16393
- const look = {
16394
- ...options.preset ? { preset: options.preset } : {},
16395
- ...options.theme ? { theme: options.theme } : {},
16396
- ...options.appearance ? { appearance: options.appearance } : {}
16397
- };
16398
- const root = el("div", "knobs-player");
16399
- root.setAttribute("role", "group");
16400
- root.setAttribute("aria-label", options.label ?? "Video");
16401
- applyLook2(root, options);
16402
- const frame = el("div", "knobs-player-frame");
16403
- frame.style.setProperty("--kp-aspect", String(options.aspect ?? 16 / 9));
16404
- const v = el("video", "knobs-player-media");
16405
- v.muted = options.muted ?? true;
16406
- v.defaultMuted = v.muted;
16407
- v.playsInline = true;
16408
- v.preload = "metadata";
16409
- v.setAttribute("playsinline", "");
16410
- if (options.poster)
16411
- v.poster = options.poster;
16412
- if (options.captions) {
16413
- const track = doc.createElement("track");
16414
- track.kind = "captions";
16415
- track.label = "Captions";
16416
- track.srclang = doc.documentElement.lang || "en";
16417
- track.default = true;
16418
- track.src = options.captions;
16419
- v.append(track);
16420
- }
16421
- frame.append(v);
16422
- const host = el("div", "knobs-player-line");
16423
- root.append(frame, host);
16424
- container.append(root);
16425
- const markers = (options.chapters ?? []).map((c, i) => ({
16426
- id: c.id ?? `c${i}`,
16427
- time: c.start,
16428
- label: c.label
16429
- }));
16430
- const tl = timeline(host, {
16431
- ...look,
16432
- variant: "line",
16433
- duration: 1,
16434
- fps: options.fps ?? 30,
16435
- markers,
16436
- repeat: options.loop ?? true,
16437
- arrows: { step: 5, shift: 10 },
16438
- label: options.label ? `${options.label} time` : "Video time",
16439
- ...options.hints === false ? { hints: false } : {}
16440
- });
16441
- const onFrame = () => root.setAttribute("data-ready", "");
16442
- let pending = null;
16443
- const seekVideo = (t) => {
16444
- if (v.readyState < 1)
16445
- return;
16446
- if (v.seeking)
16447
- pending = t;
16448
- else
16449
- v.currentTime = t;
16450
- };
16451
- let reported = 0;
16452
- const onSeeked = () => {
16453
- if (pending !== null) {
16454
- const t = pending;
16455
- pending = null;
16456
- v.currentTime = t;
16457
- return;
16458
- }
16459
- if (!v.paused || tl.playing || Math.abs(v.currentTime - reported) < 0.1)
16460
- return;
16461
- reported = v.currentTime;
16462
- options.onSeek?.(reported);
16463
- };
16464
- const onMeta = () => {
16465
- onFrame();
16466
- if (v.videoWidth)
16467
- frame.style.setProperty("--kp-aspect", String(v.videoWidth / v.videoHeight));
16468
- if (Number.isFinite(v.duration))
16469
- tl.update({ duration: v.duration });
16470
- };
16471
- v.addEventListener("loadedmetadata", onMeta);
16472
- v.addEventListener("loadeddata", onFrame);
16473
- v.addEventListener("seeked", onSeeked);
16474
- const unlisten = tl.listen({
16475
- onSeek(t, source) {
16476
- if (source === "play") {
16477
- if (Math.abs(v.currentTime - t) > 0.25)
16478
- v.currentTime = t;
16479
- return;
16480
- }
16481
- if (Math.abs(v.currentTime - t) < 1 / (options.fps ?? 30))
16482
- return;
16483
- seekVideo(t);
16484
- },
16485
- onPlay(on) {
16486
- if (on)
16487
- v.play().catch(() => tl.pause());
16488
- else {
16489
- v.pause();
16490
- reported = tl.time;
16491
- }
16492
- root.toggleAttribute("data-playing", on);
16493
- options.onPlay?.(on, tl.time);
16494
- },
16495
- onScrub(on) {
16496
- if (on)
16497
- v.pause();
16498
- }
16499
- });
16500
- tl.drive(() => {
16501
- if (v.readyState < 1)
16502
- return Number.NaN;
16503
- if (v.seeking)
16504
- return tl.time;
16505
- return v.ended ? Number.POSITIVE_INFINITY : v.currentTime;
16506
- });
16507
- const onClick = () => tl.focus();
16508
- frame.addEventListener("click", onClick);
16509
- v.src = options.src;
16510
- const api = {
16511
- element: root,
16512
- video: v,
16513
- timeline: tl,
16514
- get time() {
16515
- return tl.time;
16516
- },
16517
- get playing() {
16518
- return tl.playing;
16519
- },
16520
- get duration() {
16521
- return Number.isFinite(v.duration) ? v.duration : 0;
16522
- },
16523
- play: () => tl.play(),
16524
- pause: () => tl.pause(),
16525
- toggle: () => tl.toggle(),
16526
- seek: (t, o) => tl.seek(t, o?.glide ? { glide: true } : undefined),
16527
- seekBy: (d) => tl.seek(Math.min(tl.duration, Math.max(0, tl.time + d)), { glide: true }),
16528
- focus: () => tl.focus(),
16529
- dispose() {
16530
- unlisten();
16531
- tl.drive(null);
16532
- tl.dispose();
16533
- v.pause();
16534
- frame.removeEventListener("click", onClick);
16535
- v.removeEventListener("loadedmetadata", onMeta);
16536
- v.removeEventListener("loadeddata", onFrame);
16537
- v.removeEventListener("seeked", onSeeked);
16538
- v.removeAttribute("src");
16539
- v.load();
16540
- root.remove();
16541
- }
16542
- };
16543
- return api;
16544
- }
16545
16549
  // src/ui/history.ts
16546
16550
  init_motion();
16547
16551
  init_glide();
@@ -17414,25 +17418,63 @@ function normalizeWalkthrough(doc) {
17414
17418
  })),
17415
17419
  sections: withKickers(sections, doc.sections, doc.kickers),
17416
17420
  steps,
17417
- prs: knownPRs(doc, sections),
17421
+ prs: knownPRs(doc, sections, steps),
17418
17422
  ...repoOf(doc) ? { repo: repoOf(doc) } : {}
17419
17423
  };
17420
17424
  }
17421
17425
  var prNumber = (href) => /\/(?:pull|merge_requests)\/(\d+)(?:[/?#]|$)/.exec(href)?.[1] ?? null;
17422
17426
  var repoOf = (doc) => doc.repo?.trim().replace(/^https?:\/\/github\.com\//, "").replace(/\/+$/, "") || undefined;
17423
- function knownPRs(doc, sections) {
17427
+ var PR_STATES = ["merged", "open", "draft", "closed"];
17428
+ function prStatus(status) {
17429
+ const words = (status ?? "").trim().toLowerCase();
17430
+ const first = /^[a-z]+/.exec(words)?.[0] ?? "";
17431
+ const state = PR_STATES.includes(first) ? first : first === "shipped" || first === "done" ? "merged" : null;
17432
+ return { state, checks: /\b(green|passing|passed|ci|checks? pass)/.test(words) };
17433
+ }
17434
+ function knownPRs(doc, sections, steps = []) {
17424
17435
  const out = {};
17425
17436
  const repo = repoOf(doc);
17426
- for (const s of sections) {
17427
- const n = s.link && prNumber(s.link.href);
17428
- if (n)
17429
- out[n] = { href: s.link.href.replace(/(\/pull\/\d+).*$/, "$1") };
17430
- }
17437
+ const add = (n, pr) => {
17438
+ const known = out[n] ?? {};
17439
+ const next = { ...known };
17440
+ for (const [k, v] of Object.entries(pr))
17441
+ if (v !== undefined && next[k] === undefined)
17442
+ next[k] = v;
17443
+ out[n] = next;
17444
+ };
17431
17445
  for (const [k, pr] of Object.entries(doc.prs ?? {})) {
17432
17446
  const n = k.replace(/^#/, "");
17433
17447
  if (!/^\d+$/.test(n))
17434
17448
  continue;
17435
- out[n] = { ...out[n], ...pr };
17449
+ add(n, pr);
17450
+ }
17451
+ for (const step of steps)
17452
+ for (const m of step.media) {
17453
+ if (m.kind !== "stack")
17454
+ continue;
17455
+ for (const it of m.items) {
17456
+ const n = it.pr.replace(/^#/, "");
17457
+ if (!/^\d+$/.test(n))
17458
+ continue;
17459
+ const said = prStatus(it.status);
17460
+ add(n, {
17461
+ ...said.state ? { state: said.state } : {},
17462
+ ...said.state && said.checks ? { checks: true } : {},
17463
+ ...it.title ? { title: plainMarkdown(it.title) } : {},
17464
+ ...it.href ? { href: it.href } : {}
17465
+ });
17466
+ }
17467
+ }
17468
+ for (const s of sections) {
17469
+ const n = s.link && prNumber(s.link.href);
17470
+ if (!n)
17471
+ continue;
17472
+ const said = prStatus(s.status);
17473
+ add(n, {
17474
+ href: s.link.href.replace(/(\/pull\/\d+).*$/, "$1"),
17475
+ ...said.state ? { state: said.state } : {},
17476
+ title: plainMarkdown(s.title)
17477
+ });
17436
17478
  }
17437
17479
  if (repo) {
17438
17480
  for (const n of Object.keys(out))
@@ -17441,13 +17483,17 @@ function knownPRs(doc, sections) {
17441
17483
  }
17442
17484
  return out;
17443
17485
  }
17444
- function findPR(prs, n, repo) {
17445
- const known = prs[n];
17446
- const href = repo ? `https://github.com/${repo}/pull/${n}` : undefined;
17486
+ function findPR(prs, n, repo, kind = "pr") {
17487
+ const entry = prs[n];
17488
+ const known = entry && (entry.kind ?? "pr") === kind ? entry : undefined;
17489
+ const path = kind === "issue" ? "issues" : "pull";
17490
+ const href = repo ? `https://github.com/${repo}/${path}/${n}` : undefined;
17491
+ const base = kind === "issue" ? { kind } : {};
17447
17492
  if (known)
17448
- return known.href || !href ? known : { ...known, href };
17449
- return href ? { href } : null;
17493
+ return known.href || !href ? { ...base, ...known } : { ...base, ...known, href };
17494
+ return href ? { ...base, href } : kind === "issue" ? { kind } : null;
17450
17495
  }
17496
+ var issueNumber = (href) => /^https?:\/\/[^/]+\/.+\/issues\/(\d+)\/?(?:[?#].*)?$/.exec(href)?.[1] ?? null;
17451
17497
  var isPullRequest = (href) => /\/(?:pull|merge_requests)\/\d+(?:[/?#]|$)/.test(href);
17452
17498
  function withKickers(sections, source, kickers = "pr") {
17453
17499
  if (kickers === false)
@@ -22340,12 +22386,7 @@ var CHECK3 = "M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11
22340
22386
  var svg12 = (d, evenodd = false) => `<svg class="knobs-icon" viewBox="0 0 256 256" width="12" height="12" fill="currentColor"${evenodd ? ' fill-rule="evenodd"' : ""} aria-hidden="true" focusable="false"><path d="${d}"/></svg>`;
22341
22387
  var prGlyph = (state) => state === "merged" ? svg12(PR_PATHS.merge) : state === "draft" ? svg12(DRAFT, true) : state === "closed" ? svg12(X) : svg12(PR_PATHS.pull);
22342
22388
  var PR_STATE = { merged: "Merged", open: "Open", draft: "Draft", closed: "Closed" };
22343
- function prStatus(status) {
22344
- const words = (status ?? "").trim().toLowerCase();
22345
- const first = /^[a-z]+/.exec(words)?.[0] ?? "";
22346
- const state = first in PR_STATE ? first : null;
22347
- return { state, checks: /\b(green|passing|passed|ci|checks? pass)/.test(words) };
22348
- }
22389
+ var ISSUE = "M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-88a16,16,0,1,1-16-16A16,16,0,0,1,144,128Z";
22349
22390
  function prStateLabel(doc, state, checks, title) {
22350
22391
  const s = el2(doc, "span", "knobs-wt-prstate");
22351
22392
  s.dataset.state = state;
@@ -22369,27 +22410,55 @@ function prChip(doc, n, pr) {
22369
22410
  chip.setAttribute("target", "_blank");
22370
22411
  chip.setAttribute("rel", "noreferrer");
22371
22412
  }
22413
+ const issue = pr.kind === "issue";
22414
+ if (issue)
22415
+ chip.dataset.kind = "issue";
22372
22416
  if (pr.state)
22373
22417
  chip.dataset.state = pr.state;
22374
- chip.innerHTML = prGlyph(pr.state);
22418
+ chip.innerHTML = issue ? svg12(ISSUE) : prGlyph(pr.state);
22375
22419
  chip.append(text2(doc, "span", "", `#${n}`));
22376
- const tip = [`#${n}`, pr.title, pr.state ? PR_STATE[pr.state] : ""].filter(Boolean).join(" · ");
22377
- chip.title = tip;
22378
- chip.setAttribute("aria-label", `Pull request ${tip}`);
22420
+ if (pr.checks) {
22421
+ const c = el2(doc, "span", "knobs-wt-prchecks");
22422
+ c.innerHTML = svg12(CHECK3);
22423
+ chip.append(c);
22424
+ }
22425
+ const words = [
22426
+ `${issue ? "Issue" : "Pull request"} #${n}`,
22427
+ pr.title,
22428
+ pr.state ? PR_STATE[pr.state] : "",
22429
+ pr.checks ? "checks pass" : ""
22430
+ ].filter(Boolean);
22431
+ chip.title = words.slice(1).length ? `#${n} · ${words.slice(1).join(" · ")}` : `#${n}`;
22432
+ chip.setAttribute("aria-label", words.join(" · "));
22379
22433
  return chip;
22380
22434
  }
22381
22435
  var PR_URL = /^https?:\/\/[^/]+\/.+\/(?:pull|merge_requests)\/(\d+)\/?(?:[?#].*)?$/;
22382
- var PR_REF = /(^|[^\w&/#])#(\d{3,7})\b/g;
22436
+ var ISSUE_URL = /^https?:\/\/[^/]+\/.+\/issues\/(\d+)\/?(?:[?#].*)?$/;
22437
+ var REF = /(^|[^\w&/#:])(issue:)?#(\d{3,7})\b/gi;
22438
+ var WHOLE_REF = /^(issue:)?#(\d{1,7})$/i;
22439
+ var KEEP = 'a,pre,button,.knobs-wt-pr,.knobs-wt-slot:not([data-kind="table"]),.knobs-wt-stack';
22383
22440
  function linkPRs(root, find) {
22384
22441
  const doc = root.ownerDocument;
22442
+ const chip = (n, kind, href) => prChip(doc, n, { ...kind === "issue" ? { kind } : {}, ...find(n, kind), ...href ? { href } : {} });
22385
22443
  for (const a of Array.from(root.querySelectorAll("a[href]"))) {
22386
- if (a.classList.contains("knobs-wt-pr") || a.closest(".knobs-wt-slot,button"))
22444
+ if (a.classList.contains("knobs-wt-pr") || a.parentElement?.closest(KEEP))
22445
+ continue;
22446
+ const url = a.getAttribute("href") ?? "";
22447
+ const pr = PR_URL.exec(url)?.[1];
22448
+ const n = pr ?? ISSUE_URL.exec(url)?.[1];
22449
+ if (!n)
22387
22450
  continue;
22388
- const n = PR_URL.exec(a.getAttribute("href") ?? "")?.[1];
22389
- const words = a.textContent?.trim() ?? "";
22390
- if (!n || !(words === `#${n}` || words === n || words === a.getAttribute("href")))
22451
+ const words = (a.textContent ?? "").trim().replace(/^issue:/i, "");
22452
+ if (!(words === `#${n}` || words === n || words === url))
22391
22453
  continue;
22392
- a.replaceWith(prChip(doc, n, { href: a.href, ...find(n) }));
22454
+ a.replaceWith(chip(n, pr ? "pr" : "issue", a.href));
22455
+ }
22456
+ for (const c of Array.from(root.querySelectorAll("code"))) {
22457
+ if (c.closest("pre") || c.parentElement?.closest(KEEP))
22458
+ continue;
22459
+ const m = WHOLE_REF.exec((c.textContent ?? "").trim());
22460
+ if (m)
22461
+ c.replaceWith(chip(m[2], m[1] ? "issue" : "pr"));
22393
22462
  }
22394
22463
  const walker = doc.createTreeWalker(root, 5);
22395
22464
  const hits = [];
@@ -22397,29 +22466,22 @@ function linkPRs(root, find) {
22397
22466
  if (t.nodeType !== 3)
22398
22467
  continue;
22399
22468
  const p = t.parentElement;
22400
- if (!p || p.closest("a,code,pre,button,.knobs-wt-slot,.knobs-wt-pr"))
22469
+ if (!p || p.closest(`${KEEP},code`))
22401
22470
  continue;
22402
- PR_REF.lastIndex = 0;
22403
- if (PR_REF.test(t.nodeValue ?? ""))
22471
+ REF.lastIndex = 0;
22472
+ if (REF.test(t.nodeValue ?? ""))
22404
22473
  hits.push(t);
22405
22474
  }
22406
22475
  for (const t of hits) {
22407
22476
  const value = t.nodeValue ?? "";
22408
22477
  const frag = doc.createDocumentFragment();
22409
22478
  let last = 0;
22410
- let changed = false;
22411
- PR_REF.lastIndex = 0;
22412
- for (let m = PR_REF.exec(value);m; m = PR_REF.exec(value)) {
22413
- const pr = find(m[2]);
22414
- if (!pr)
22415
- continue;
22479
+ REF.lastIndex = 0;
22480
+ for (let m = REF.exec(value);m; m = REF.exec(value)) {
22416
22481
  const at = m.index + m[1].length;
22417
- frag.append(value.slice(last, at), prChip(doc, m[2], pr));
22418
- last = at + m[2].length + 1;
22419
- changed = true;
22482
+ frag.append(value.slice(last, at), chip(m[3], m[2] ? "issue" : "pr"));
22483
+ last = m.index + m[0].length;
22420
22484
  }
22421
- if (!changed)
22422
- continue;
22423
22485
  frag.append(value.slice(last));
22424
22486
  t.replaceWith(frag);
22425
22487
  }
@@ -22438,7 +22500,7 @@ var snappy3 = springCss(SPRINGS.snappy);
22438
22500
  var calm = springCss(SPRINGS.calm);
22439
22501
  var state = springCss(SPRINGS.default);
22440
22502
  var quick = springCss(FADE);
22441
- var fade2 = "-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 1.5em),transparent);mask-image:linear-gradient(to right,#000 calc(100% - 1.5em),transparent)";
22503
+ var fade2 = "-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 16px),transparent);mask-image:linear-gradient(to right,#000 calc(100% - 16px),transparent)";
22442
22504
  var walkthroughCss = `
22443
22505
  :where(.knobs-walkthrough,.knobs-walkthrough *,.knobs-wt-slot,.knobs-wt-slot *){box-sizing:border-box}
22444
22506
  .knobs-ui:is(.knobs-walkthrough,.knobs-wt-slot){--k-glass:0;--k-glow:0;--kw-quick:${quick.easing};--kw-quick-ms:${quick.duration}ms}
@@ -22450,7 +22512,7 @@ var walkthroughCss = `
22450
22512
  .knobs-walkthrough [hidden]{display:none!important}
22451
22513
  /* (0,0,1): a host's element rules stop here, and every class rule below wins over it. */
22452
22514
  :where(.knobs-walkthrough) :is(h1,h2,h3,p,ol,ul,dl,dd){margin:0;font:inherit;letter-spacing:inherit}
22453
- .knobs-walkthrough a{color:inherit;text-decoration:underline;text-decoration-color:color-mix(in srgb,currentColor 35%,transparent);text-underline-offset:2px;transition:text-decoration-color .15s}
22515
+ .knobs-walkthrough a{cursor:default;color:inherit;text-decoration:underline;text-decoration-color:color-mix(in srgb,currentColor 35%,transparent);text-underline-offset:2px;transition:text-decoration-color calc(var(--_k-hover-out) * 1ms)}
22454
22516
  .knobs-walkthrough a:hover{text-decoration-color:currentColor}
22455
22517
  .knobs-walkthrough code,.knobs-wt-slot code{font:var(--_k-type-label) var(--_k-mono);padding:1px 5px;border-radius:calc(var(--_k-radius) / 2);background:var(--_k-fill)}
22456
22518
  :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;
@@ -22478,14 +22540,17 @@ var walkthroughCss = `
22478
22540
  .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)}
22479
22541
  .knobs-walkthrough .knobs-wt-metaline a:not(.knobs-wt-pr):hover{color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
22480
22542
  .knobs-wt-metaline svg{align-self:center;width:12px;height:12px;margin-left:2px}
22481
- /* A pull request: a small chip, its state's icon and number in a quiet tint of the state (open
22482
- the success hue, merged the violet series, draft and closed neutral), its title on hover. */
22483
- .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;
22543
+ /* A pull request or issue: a capsule, its state's icon and number in a quiet tint of the state
22544
+ (open the success hue, merged the violet series, draft and closed faint, unknown neutral; a
22545
+ closed issue is done, so violet), a faint check where its checks pass, its title on hover. */
22546
+ .knobs-ui .knobs-wt-pr{--kw-pr:var(--_k-fg-2);display:inline-flex;align-items:center;gap:3px;height:18px;padding:0 6px 0 5px;margin:0 1px;border-radius:9px;vertical-align:1px;
22484
22547
  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;
22485
22548
  transition:background calc(var(--_k-hover-out) * 1ms)}
22486
22549
  .knobs-ui .knobs-wt-pr[data-state="open"]{--kw-pr:color-mix(in oklch,var(--_k-ok) 60%,var(--_k-fg-2))}
22487
22550
  .knobs-ui .knobs-wt-pr[data-state="merged"]{--kw-pr:color-mix(in oklch,var(--_k-series-5) 60%,var(--_k-fg-2))}
22488
22551
  .knobs-ui .knobs-wt-pr:is([data-state="draft"],[data-state="closed"]){--kw-pr:var(--_k-fg-3)}
22552
+ .knobs-ui .knobs-wt-pr[data-kind="issue"][data-state="closed"]{--kw-pr:color-mix(in oklch,var(--_k-series-5) 60%,var(--_k-fg-2))}
22553
+ .knobs-ui .knobs-wt-pr .knobs-wt-prchecks{margin-left:0}
22489
22554
  .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)}
22490
22555
  .knobs-wt-pr svg{flex:none}
22491
22556
  .knobs-wt-metaline .knobs-wt-pr{align-self:center;margin:0}
@@ -22544,14 +22609,14 @@ var walkthroughCss = `
22544
22609
  .knobs-wt-fi-ask{max-width:var(--kw-measure);color:var(--_k-fg);font-weight:var(--kw-strong);text-wrap:pretty}
22545
22610
  .knobs-wt-fi-text{max-width:var(--kw-measure);color:var(--_k-fg);text-wrap:pretty}
22546
22611
  .knobs-wt-fi-answer{max-width:var(--kw-measure);color:var(--_k-fg-2);text-wrap:pretty}
22547
- :where(.knobs-ui) .knobs-wt-fi-step{all:revert;box-sizing:border-box;font:inherit;color:inherit;background:transparent;border:0;margin:0;padding:0;cursor:pointer;outline:none;
22612
+ :where(.knobs-ui) .knobs-wt-fi-step{all:revert;box-sizing:border-box;font:inherit;color:inherit;background:transparent;border:0;margin:0;padding:0;cursor:default;outline:none;
22548
22613
  transition:color calc(var(--_k-hover-out) * 1ms)}
22549
22614
  .knobs-ui .knobs-wt-fi-step:is(:hover,:focus-visible){color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
22550
22615
 
22551
22616
  /* ——— a disclosure: a small faint toggle with a caret, no fill. The body's height springs (in
22552
22617
  script) and its content fades in place, top-anchored, under a soft bottom mask ——— */
22553
22618
  .knobs-wt-disc{display:flex;flex-direction:column;min-width:0}
22554
- .knobs-ui .knobs-wt-disc-toggle{display:inline-flex;align-items:center;gap:var(--_k-space-inside);width:max-content;height:20px;color:var(--_k-fg-3);font-size:var(--_k-type-small);line-height:var(--_k-leading-small);cursor:pointer;
22619
+ .knobs-ui .knobs-wt-disc-toggle{display:inline-flex;align-items:center;gap:var(--_k-space-inside);width:max-content;height:20px;color:var(--_k-fg-3);font-size:var(--_k-type-small);line-height:var(--_k-leading-small);cursor:default;
22555
22620
  border-radius:calc(var(--_k-radius) / 2);transition:color calc(var(--_k-hover-out) * 1ms)}
22556
22621
  .knobs-ui .knobs-wt-disc-toggle:is(:hover,:focus-visible),.knobs-wt-disc[data-open]>.knobs-wt-disc-toggle{color:var(--_k-fg-2);transition-duration:calc(var(--_k-hover-in) * 1ms)}
22557
22622
  .knobs-ui .knobs-wt-disc-toggle:focus-visible{box-shadow:0 0 0 2px var(--_k-focus)}
@@ -22578,7 +22643,7 @@ var walkthroughCss = `
22578
22643
  .knobs-wt-n[data-leaving]{opacity:0;filter:blur(3px);padding-block:0}
22579
22644
  .knobs-wt-n+.knobs-wt-n{box-shadow:inset 0 1px 0 var(--_k-border-subtle)}
22580
22645
  .knobs-wt-n-meta{flex:none;min-width:44px;color:var(--_k-fg-3);font:var(--_k-weight) var(--_k-type-small)/var(--_k-leading-body) var(--_k-mono)}
22581
- .knobs-wt-n-meta[data-seek]{cursor:pointer}
22646
+ .knobs-wt-n-meta[data-seek]{cursor:default}
22582
22647
  .knobs-wt-n-meta[data-seek]:hover{color:var(--_k-fg)}
22583
22648
  .knobs-wt-n-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--_k-space-sibling)}
22584
22649
  .knobs-wt-n-voice{height:30px;max-width:320px}
@@ -22595,7 +22660,7 @@ var walkthroughCss = `
22595
22660
  /* Collapsed: the launcher and the bar swap in place, a quick fade with a little scale and blur
22596
22661
  from the launcher's corner; the exit is the shorter. */
22597
22662
  :where(.knobs-ui) .knobs-wt-launch{all:revert;box-sizing:border-box;justify-self:end;align-self:end;display:inline-flex;align-items:center;gap:6px;height:32px;margin:0;padding:0 10px 0 9px;border:0;border-radius:var(--_k-round);
22598
- background:var(--_k-bg);color:var(--_k-fg-2);box-shadow:var(--_ku-pane-ring),var(--_k-shadow-overlay);font:var(--kw-strong) var(--_k-type-small)/1 var(--_k-font);cursor:pointer;outline:none;transform-origin:100% 100%;
22663
+ background:var(--_k-bg);color:var(--_k-fg-2);box-shadow:var(--_ku-pane-ring),var(--_k-shadow-overlay);font:var(--kw-strong) var(--_k-type-small)/1 var(--_k-font);cursor:default;outline:none;transform-origin:100% 100%;
22599
22664
  transition:color calc(var(--_k-hover-out) * 1ms),opacity var(--kw-quick-ms) var(--kw-quick),scale var(--kw-quick-ms) var(--kw-quick),filter var(--kw-quick-ms) var(--kw-quick)}
22600
22665
  .knobs-ui .knobs-wt-launch:is(:hover,:focus-visible){color:var(--_k-fg)}
22601
22666
  .knobs-ui .knobs-wt-launch:focus-visible{box-shadow:var(--_k-focus-ring)}
@@ -22615,11 +22680,11 @@ var walkthroughCss = `
22615
22680
  .knobs-wt-attach{display:flex;gap:4px;align-items:center;align-self:center}
22616
22681
  .knobs-wt-attach:empty{display:none}
22617
22682
  .knobs-wt-chip-a{display:inline-flex;align-items:center;gap:2px;height:24px;padding:0 2px 0 9px;border-radius:var(--_k-round);background:var(--_k-fill-selected);font-size:var(--_k-type-small);white-space:nowrap}
22618
- .knobs-ui .knobs-wt-x{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;color:var(--_k-fg-3);cursor:pointer;transition:background .15s,color .15s}
22683
+ .knobs-ui .knobs-wt-x{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;color:var(--_k-fg-3);cursor:default;transition:background .15s,color .15s}
22619
22684
  .knobs-ui .knobs-wt-x:is(:hover,:focus-visible){background:var(--_ku-wash-hi);color:var(--_k-fg)}
22620
22685
  .knobs-wt-x svg{width:12px;height:12px}
22621
22686
  .knobs-ui .knobs-wt-tool{flex:none;display:inline-flex;align-items:center;justify-content:center;gap:5px;min-width:32px;height:32px;padding:0 8px;border-radius:calc(var(--_k-radius) + 2px);
22622
- color:var(--_k-fg-2);cursor:pointer;transition:background .15s,color .15s}
22687
+ color:var(--_k-fg-2);cursor:default;transition:background .15s,color .15s}
22623
22688
  .knobs-ui .knobs-wt-tool:is(:hover,:focus-visible){background:var(--_ku-wash-hi);color:var(--_k-fg)}
22624
22689
  .knobs-ui .knobs-wt-tool[aria-pressed="true"]{background:var(--_k-fill-selected);color:var(--_k-fg)}
22625
22690
  .knobs-wt-tool svg{width:16px;height:16px}
@@ -22629,7 +22694,7 @@ var walkthroughCss = `
22629
22694
  .knobs-ui .knobs-wt-tool[data-send][data-ready]{background:var(--_k-accent);color:var(--_k-bg)}
22630
22695
  /* Back: in place over the bar for a few seconds after a note moves the page on. */
22631
22696
  .knobs-ui .knobs-wt-back{position:absolute;left:8px;bottom:calc(100% + 8px);display:inline-flex;align-items:center;gap:var(--_k-space-inside);max-width:min(360px,calc(100% - 16px));height:28px;padding:0 12px 0 9px;
22632
- border-radius:var(--_k-round);background:var(--_k-bg);color:var(--_k-fg);box-shadow:var(--_ku-pane-ring),var(--_k-shadow-overlay);font-size:var(--_k-type-small);font-weight:var(--kw-strong);cursor:pointer;
22697
+ border-radius:var(--_k-round);background:var(--_k-bg);color:var(--_k-fg);box-shadow:var(--_ku-pane-ring),var(--_k-shadow-overlay);font-size:var(--_k-type-small);font-weight:var(--kw-strong);cursor:default;
22633
22698
  opacity:0;scale:.96;filter:blur(3px);pointer-events:none;transition:opacity .11s cubic-bezier(.4,0,1,1),scale .11s cubic-bezier(.4,0,1,1),filter .11s cubic-bezier(.4,0,1,1)}
22634
22699
  .knobs-ui .knobs-wt-back[data-on]{opacity:1;scale:1;filter:none;pointer-events:auto;transition:opacity .15s cubic-bezier(.2,.8,.2,1),scale .15s cubic-bezier(.2,.8,.2,1),filter .15s cubic-bezier(.2,.8,.2,1)}
22635
22700
  .knobs-ui .knobs-wt-back:hover{background:var(--_k-fill-selected)}
@@ -22656,6 +22721,9 @@ var walkthroughCss = `
22656
22721
  .knobs-wt-step[data-full] .knobs-wt-body{flex:1;min-height:0}
22657
22722
  .knobs-wt-step[data-full] .knobs-wt-fit{height:100%;transition:none}
22658
22723
  .knobs-wt-step[data-full] .knobs-wt-frame{height:100%;aspect-ratio:auto}
22724
+ .knobs-wt-step[data-full] .knobs-wt-slot[data-kind="video"]>.knobs-wt-body{display:flex;flex-direction:column}
22725
+ .knobs-wt-step[data-full] .knobs-wt-slot[data-kind="video"] .knobs-player{flex:1;min-height:0}
22726
+ .knobs-wt-step[data-full] .knobs-wt-slot[data-kind="video"] .knobs-player-frame{flex:1;min-height:0;aspect-ratio:auto}
22659
22727
 
22660
22728
  /* ——— a taste test: the options side by side, Kit's pick checked and the rest stepping back. On
22661
22729
  the step's card, so no fill of their own ——— */
@@ -22664,7 +22732,7 @@ var walkthroughCss = `
22664
22732
  .knobs-wt-option{position:relative;display:flex;flex-direction:column;gap:var(--_k-space-sibling);min-width:0}
22665
22733
  .knobs-wt-option>:not(.knobs-wt-pick){transition:opacity var(--kw-snappy-ms) var(--kw-snappy)}
22666
22734
  .knobs-wt-options[data-has-pick] .knobs-wt-option:not([data-picked])>:not(.knobs-wt-pick){opacity:.55}
22667
- .knobs-ui .knobs-wt-pick{display:flex;align-items:center;gap:8px;width:100%;min-height:28px;margin:0 -6px;width:calc(100% + 12px);padding:0 6px;border-radius:var(--_k-radius);cursor:pointer;color:var(--_k-fg-2);text-align:left;
22735
+ .knobs-ui .knobs-wt-pick{display:flex;align-items:center;gap:8px;width:100%;min-height:28px;margin:0 -6px;width:calc(100% + 12px);padding:0 6px;border-radius:var(--_k-radius);cursor:default;color:var(--_k-fg-2);text-align:left;
22668
22736
  transition:background calc(var(--_k-hover-out) * 1ms)}
22669
22737
  .knobs-ui .knobs-wt-pick:is(:hover,:focus-visible){color:var(--_k-fg);background:var(--_k-fill);transition-duration:calc(var(--_k-hover-in) * 1ms)}
22670
22738
  .knobs-wt-letter{flex:none;font-weight:var(--kw-strong);color:var(--_k-fg)}
@@ -22687,17 +22755,18 @@ var walkthroughCss = `
22687
22755
  .knobs-wt-bar{position:relative;height:4px;border-radius:2px;overflow:hidden;background:var(--_k-fill)}
22688
22756
  .knobs-wt-fill{position:absolute;inset:0;border-radius:inherit;background:var(--_k-accent);transform-origin:0 50%;transform:scaleX(var(--kw-done,0))}
22689
22757
  .knobs-walkthrough[data-settled] .knobs-wt-fill{transition:transform var(--kw-calm-ms) var(--kw-calm)}
22690
- /* The contents: every row on one text edge and in one colour, title rows (32 high, the control
22691
- radius) and the steps under them (20 high, half of it), bled out so their text sits on the
22692
- rail's edge; the selection pill slides to the current step, and base rows never brighten. */
22693
- .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}
22758
+ /* The contents: every row on one text edge and in one colour, title rows (28 high, the control
22759
+ radius) and the steps under them (20 high, half of it), bled 6 px out so their text sits on the
22760
+ rail's edge and the first title's words on the column's top line; a narrow fade where a title
22761
+ runs out of room; the selection pill slides to the current step, and base rows never brighten. */
22762
+ .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:-4px -6px 0;padding:0 0 8px}
22694
22763
  .knobs-wt-toc::-webkit-scrollbar{display:none}
22695
22764
  .knobs-wt-toc-group{display:flex;flex-direction:column;min-width:0}
22696
- .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;
22697
- 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;
22698
- transition:color calc(var(--_k-hover-out) * 1ms)}
22765
+ .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:28px;
22766
+ padding:0 6px;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:default;
22767
+ transition:color calc(var(--_k-hover-out) * 1ms),background calc(var(--_k-hover-out) * 1ms)}
22699
22768
  .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)}
22700
- .knobs-ui .knobs-wt-toc-row:is(:hover,:focus-visible){color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
22769
+ .knobs-ui .knobs-wt-toc-row:is(:hover,:focus-visible){color:var(--_k-fg);background:var(--_k-fill-hover);transition-duration:calc(var(--_k-hover-in) * 1ms)}
22701
22770
  .knobs-ui .knobs-wt-toc-row:focus-visible{box-shadow:inset 0 0 0 1px var(--_k-focus)}
22702
22771
  .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)}
22703
22772
  .knobs-wt-toc-label{min-width:0;overflow:hidden;${fade2}}
@@ -22709,7 +22778,7 @@ var walkthroughCss = `
22709
22778
  .knobs-wt-toc-glyph[data-done]{background:var(--kw-tone)}
22710
22779
  .knobs-wt-toc-glyph[data-tone="none"]{box-shadow:inset 0 0 0 1px var(--_k-border)}
22711
22780
  .knobs-wt-actions{display:flex;flex-wrap:wrap;gap:4px}
22712
- .knobs-ui .knobs-wt-btn{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 9px;border-radius:var(--_k-radius);color:var(--_k-fg-2);cursor:pointer;white-space:nowrap;
22781
+ .knobs-ui .knobs-wt-btn{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 9px;border-radius:var(--_k-radius);color:var(--_k-fg-2);cursor:default;white-space:nowrap;
22713
22782
  transition:background .15s,color .15s}
22714
22783
  .knobs-ui .knobs-wt-btn:is(:hover,:focus-visible){background:var(--_ku-wash-hi);color:var(--_k-fg)}
22715
22784
  .knobs-ui .knobs-wt-btn[data-primary]{background:var(--_k-fill-selected);color:var(--_k-fg)}
@@ -22743,11 +22812,10 @@ var walkthroughCss = `
22743
22812
  /* One frame for every slot that has one (a picture, a video, a code block, a compare's stage, a
22744
22813
  slot still loading): the panel radius and a quiet fill, no ring; concentric on a card. */
22745
22814
  .knobs-wt-slot .knobs-cmp-stage{border-radius:var(--kw-frame-r)}
22746
- /* A compare is exactly as tall as its pictures at this width, so there's no letterbox: the two
22747
- sides edge to edge (side) or one (overlaid), plus its mode bar, the timeline and their two gaps
22815
+ /* A compare is as tall as its two pictures side by side at this width, in every mode (Wipe fuses
22816
+ them at one side's size, so nothing grows or re-measures), plus its mode bar, the line and gaps
22748
22817
  (92 px), and the frame strip with its gap (158 px). */
22749
22818
  .knobs-wt-fit{width:100%;height:calc(100cqw / 2 / var(--kw-aspect,1.7778) + var(--kw-chrome,92px))}
22750
- .knobs-wt-fit[data-overlay]{height:calc(100cqw / var(--kw-aspect,1.7778) + var(--kw-chrome,92px))}
22751
22819
  .knobs-wt-fit[data-strip]{--kw-chrome:158px}
22752
22820
  .knobs-wt-fit[data-settled]{transition:height var(--k-wt-calm-ms,${calm.duration}ms) ${calm.easing}}
22753
22821
  .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)}
@@ -22756,8 +22824,12 @@ var walkthroughCss = `
22756
22824
  .knobs-wt-frame[data-natural]>img{position:static;height:auto}
22757
22825
  /* Diff, code and terminal: one plain container (no glass): a flat inset fill, a hairline ring, the
22758
22826
  control radius and room inside, so a diff's buttons and the code under them can breathe. */
22759
- .knobs-wt-slot[data-kind="diff"]>.knobs-wt-body{padding:var(--_k-space-sibling) var(--_k-pad-card) var(--_k-pad-card);border-radius:var(--_k-radius);background:var(--_k-bg-inset);box-shadow:inset 0 0 0 1px var(--_k-border-subtle)}
22760
- .knobs-wt-code{margin:0;padding:var(--_k-pad-card);max-height:420px;overflow:auto;border-radius:var(--_k-radius);background:var(--_k-bg-inset);box-shadow:inset 0 0 0 1px var(--_k-border-subtle);color:var(--_k-fg);
22827
+ .knobs-wt-slot[data-kind="diff"]>.knobs-wt-body{padding:var(--_k-pad-card);border-radius:var(--_k-radius-panel);background:var(--_k-bg-inset);box-shadow:inset 0 0 0 1px var(--_k-border-subtle)}
22828
+ /* The diff inside it: its header row 28 high on the container's padding, a hairline under it, and
22829
+ no line of its own under the code (the container's ring closes it). */
22830
+ .knobs-wt-slot[data-kind="diff"] .knobs-ui.knobs-diff:not([data-on="pane"]){--kd-head:28px;--kd-x:0px}
22831
+ .knobs-wt-slot[data-kind="diff"] .knobs-ui.knobs-diff:not([data-on="pane"])::after{display:none}
22832
+ .knobs-wt-code{margin:0;padding:var(--_k-pad-card);max-height:420px;overflow:auto;border-radius:var(--_k-radius-panel);background:var(--_k-bg-inset);box-shadow:inset 0 0 0 1px var(--_k-border-subtle);color:var(--_k-fg);
22761
22833
  font:var(--_k-type-label)/var(--_k-leading-body) var(--_k-mono);white-space:pre;tab-size:2;scrollbar-width:thin}
22762
22834
  .knobs-surface :is(.knobs-wt-code,.knobs-wt-slot[data-kind="diff"]>.knobs-wt-body){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)}
22763
22835
  .knobs-wt-code[data-terminal]{white-space:pre-wrap;overflow-wrap:anywhere}
@@ -22803,7 +22875,7 @@ var walkthroughCss = `
22803
22875
  .knobs-wt-mark:is([data-shape="dot"],[data-shape="arrow"]) .knobs-wt-mark-label{left:12px;bottom:auto;top:10px}
22804
22876
  .knobs-wt-mark[data-shape="arrow"] .knobs-wt-mark-label{left:auto;right:30px;top:auto;bottom:30px}
22805
22877
  .knobs-ui .knobs-wt-anno-toggle{all:revert;box-sizing:border-box;position:absolute;top:8px;right:8px;display:grid;place-items:center;width:28px;height:28px;padding:0;border:0;margin:0;
22806
- border-radius:var(--_k-radius);background:color-mix(in srgb,var(--_k-bg) 72%,transparent);color:var(--_k-fg-2);cursor:pointer;opacity:.6;outline:none;
22878
+ border-radius:var(--_k-radius);background:color-mix(in srgb,var(--_k-bg) 72%,transparent);color:var(--_k-fg-2);cursor:default;opacity:.6;outline:none;
22807
22879
  transition:opacity calc(var(--_k-hover-out) * 1ms),color calc(var(--_k-hover-out) * 1ms)}
22808
22880
  .knobs-ui .knobs-wt-anno-toggle:is(:hover,:focus-visible){opacity:1;color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
22809
22881
  .knobs-ui .knobs-wt-anno-toggle:focus-visible{box-shadow:inset 0 0 0 1px var(--_k-focus)}
@@ -22824,7 +22896,7 @@ var walkthroughCss = `
22824
22896
  .knobs-wt-srow{display:grid;grid-template-columns:auto minmax(0,1fr) auto;column-gap:10px;align-items:start;margin:0 -8px;padding:var(--kw-spt) 8px var(--kw-spb);
22825
22897
  border-radius:max(0px,calc(var(--_k-radius-panel) - 8px));transition:background calc(var(--_k-hover-out) * 1ms)}
22826
22898
  .knobs-wt-srow[data-trunk]{--kw-spb:0px;grid-template-columns:auto minmax(0,1fr)}
22827
- .knobs-wt-srow[data-go]{cursor:pointer}
22899
+ .knobs-wt-srow[data-go]{cursor:default}
22828
22900
  .knobs-wt-srow[data-go]:hover{background:var(--_ku-wash);transition-duration:calc(var(--_k-hover-in) * 1ms)}
22829
22901
  .knobs-wt-lanes{display:flex;align-self:stretch;margin-block:calc(var(--kw-spt) * -1) calc(var(--kw-spb) * -1)}
22830
22902
  .knobs-wt-lane{position:relative;flex:none;width:16px}
@@ -22840,7 +22912,7 @@ var walkthroughCss = `
22840
22912
  .knobs-ui .knobs-wt-spr{font:var(--_k-weight-label) var(--_k-type-label)/var(--kw-line) var(--_k-mono);color:var(--_k-fg-2);text-decoration:none;white-space:nowrap}
22841
22913
  .knobs-ui a.knobs-wt-spr:is(:hover,:focus-visible){color:var(--_k-fg);text-decoration:underline}
22842
22914
  .knobs-ui .knobs-wt-stitle{font-weight:var(--_k-weight-label);line-height:var(--kw-line);color:var(--_k-fg);text-align:left}
22843
- .knobs-ui button.knobs-wt-stitle{cursor:pointer;text-decoration:underline;text-decoration-color:transparent;text-underline-offset:3px;transition:text-decoration-color calc(var(--_k-hover-out) * 1ms)}
22915
+ .knobs-ui button.knobs-wt-stitle{cursor:default;text-decoration:underline;text-decoration-color:transparent;text-underline-offset:3px;transition:text-decoration-color calc(var(--_k-hover-out) * 1ms)}
22844
22916
  .knobs-ui :is(.knobs-wt-srow[data-go]:hover button.knobs-wt-stitle,button.knobs-wt-stitle:focus-visible){text-decoration-color:color-mix(in oklch,currentColor 45%,transparent);transition-duration:calc(var(--_k-hover-in) * 1ms)}
22845
22917
  .knobs-ui .knobs-wt-ssummary{margin:0;max-width:var(--k-walkthrough-measure,68ch);color:var(--_k-fg-2)}
22846
22918
  .knobs-wt-smeta{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 12px;font:var(--_k-weight) var(--_k-type-small)/var(--kw-line) var(--_k-mono);font-variant-numeric:tabular-nums;color:var(--_k-fg-3)}
@@ -22872,7 +22944,7 @@ var walkthroughCss = `
22872
22944
  .knobs-wt-tablebox{min-width:0}
22873
22945
  /* A row with a step goes there: the whole row, a wash under the pointer, no arrow, so rows with and
22874
22946
  without one line up. */
22875
- .knobs-wt-tablebox [data-go]{cursor:pointer;outline:none}
22947
+ .knobs-wt-tablebox [data-go]{cursor:default;outline:none}
22876
22948
  .knobs-wt-tablebox tr[data-go]>td,.knobs-wt-trow[data-go]{transition:background calc(var(--_k-hover-out) * 1ms)}
22877
22949
  .knobs-wt-tablebox tr[data-go]:is(:hover,:focus-visible)>td,.knobs-wt-trow[data-go]:is(:hover,:focus-visible){background:var(--_ku-wash);transition-duration:calc(var(--_k-hover-in) * 1ms)}
22878
22950
  .knobs-wt-tablebox [data-go]:focus-visible{box-shadow:inset 0 0 0 1px var(--_k-focus)}
@@ -22928,13 +23000,18 @@ var walkthroughCss = `
22928
23000
  .knobs-wt-body:has(>.knobs-wt-chapters){display:flex;flex-direction:column;gap:var(--_k-space-title)}
22929
23001
  .knobs-wt-chapters{position:relative;display:flex;flex-direction:column;margin:0 calc(var(--_k-space-sibling) * -1)}
22930
23002
  .knobs-ui .knobs-wt-chapter{position:relative;display:grid;grid-template-columns:2ch minmax(0,1fr) auto;column-gap:var(--_k-space-sibling);align-items:center;height:32px;padding:0 var(--_k-space-sibling);
22931
- border-radius:var(--_k-radius);color:var(--_k-fg-2);text-align:left;white-space:nowrap;cursor:pointer;transition:color calc(var(--_k-hover-out) * 1ms)}
22932
- .knobs-ui .knobs-wt-chapter:is(:hover,:focus-visible){color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
23003
+ border-radius:var(--_k-radius);color:var(--_k-fg-2);text-align:left;white-space:nowrap;cursor:default;transition:color calc(var(--_k-hover-out) * 1ms),background calc(var(--_k-hover-out) * 1ms)}
23004
+ .knobs-ui .knobs-wt-chapter:is(:hover,:focus-visible){color:var(--_k-fg);background:var(--_k-fill-hover);transition-duration:calc(var(--_k-hover-in) * 1ms)}
22933
23005
  .knobs-ui .knobs-wt-chapter:focus-visible{box-shadow:inset 0 0 0 1px var(--_k-focus)}
22934
23006
  .knobs-wt-chapter-n{font-size:var(--_k-type-small);color:var(--_k-fg-3)}
22935
23007
  .knobs-wt-chapter-label{min-width:0;overflow:hidden;${fade2}}
22936
23008
  .knobs-wt-chapter-time{font:var(--_k-weight) var(--_k-type-small)/1 var(--_k-mono);color:var(--_k-fg-3)}
22937
23009
  .knobs-pick-copy :is(.knobs-wt-chapter-n,.knobs-wt-chapter-time){color:var(--_k-fg-2)}
23010
+ /* Every hover in the walkthrough lands at once (hover-in, 0 ms) and eases out (hover-out): links,
23011
+ buttons, chips, rail, chapter, table and stack rows (its own; the pieces inside keep theirs). */
23012
+ :is(.knobs-walkthrough,.knobs-wt-slot) :is(a,button,[data-go])[class*="knobs-wt-"]:is(:hover,:focus-visible),
23013
+ .knobs-walkthrough a:not([class]):hover,
23014
+ :is(.knobs-walkthrough,.knobs-wt-slot) :is(.knobs-wt-srow,tr)[data-go]:is(:hover,:focus-visible) :is(td,.knobs-wt-stitle){transition-duration:calc(var(--_k-hover-in) * 1ms)}
22938
23015
  @media(prefers-reduced-motion:reduce){:is(.knobs-walkthrough,.knobs-wt-slot) :where(*,::before,::after){transition-duration:.15s!important;transition-property:opacity,background,color!important}}
22939
23016
  @media(prefers-reduced-motion:reduce){.knobs-wt-disc-content{transition:opacity .15s}}
22940
23017
  `;
@@ -23035,7 +23112,6 @@ async function mountCompare(host, m, look, alive, onEvent) {
23035
23112
  fit.style.setProperty("--kw-aspect", String(known ?? DEFAULT_ASPECT));
23036
23113
  const width = host.getBoundingClientRect().width;
23037
23114
  const mode = m.mode ?? (width && width < 560 ? "wipe" : "side");
23038
- fit.toggleAttribute("data-overlay", mode !== "side");
23039
23115
  host.append(fit);
23040
23116
  const [aspect, lib] = await Promise.all([
23041
23117
  known ? Promise.resolve(known) : probeAspect(doc, after.src),
@@ -23059,7 +23135,6 @@ async function mountCompare(host, m, look, alive, onEvent) {
23059
23135
  ...aspect ? { aspect } : {},
23060
23136
  ...m.strip ? { strip: true } : {},
23061
23137
  onMode: (next) => {
23062
- fit.toggleAttribute("data-overlay", next !== "side");
23063
23138
  onEvent?.({ what: "mode", detail: next });
23064
23139
  const layer = marks?.element;
23065
23140
  if (layer) {
@@ -23083,6 +23158,12 @@ async function mountCompare(host, m, look, alive, onEvent) {
23083
23158
  }
23084
23159
  });
23085
23160
  const stage = fit.querySelector(".knobs-cmp-stage");
23161
+ if (stage)
23162
+ doc.defaultView?.requestAnimationFrame(() => {
23163
+ const chrome = fit.clientHeight - stage.clientHeight;
23164
+ if (alive() && stage.clientHeight > 0 && chrome > 0)
23165
+ fit.style.setProperty("--kw-chrome", `${Math.round(chrome)}px`);
23166
+ });
23086
23167
  if (m.annotations?.length && stage) {
23087
23168
  const shape = { w: aspect ?? DEFAULT_ASPECT, h: 1 };
23088
23169
  marks = annotationLayer(stage, m.annotations, () => {
@@ -23158,45 +23239,38 @@ async function mountCompare(host, m, look, alive, onEvent) {
23158
23239
  var round2 = (v) => Math.round(v * 100) / 100;
23159
23240
  var MARK_GLIDE = 450;
23160
23241
  var zoomedIn = (z) => z.w < 0.999 || z.h < 0.999;
23161
- function mountVideo(host, m, onEvent) {
23242
+ async function mountVideo(host, m, look, alive, onEvent) {
23162
23243
  const doc = host.ownerDocument;
23163
- const frame = div(doc, "knobs-wt-frame");
23244
+ await Promise.resolve().then(() => init_player());
23245
+ if (!alive())
23246
+ return { dispose() {} };
23164
23247
  const aspect = parseAspect(m.aspect);
23165
- if (aspect)
23166
- frame.style.setProperty("--kw-aspect", String(aspect));
23167
- const v = doc.createElement("video");
23168
- v.controls = true;
23169
- v.loop = true;
23170
- v.muted = true;
23171
- v.playsInline = true;
23172
- v.preload = "metadata";
23173
- if (m.poster)
23174
- v.poster = m.poster;
23175
- if (!aspect)
23176
- v.addEventListener("loadedmetadata", () => v.videoWidth && frame.style.setProperty("--kw-aspect", String(v.videoWidth / v.videoHeight)), { once: true });
23177
- if (m.captions) {
23178
- const track = doc.createElement("track");
23179
- track.kind = "captions";
23180
- track.label = "Captions";
23181
- track.srclang = doc.documentElement.lang || "en";
23182
- track.default = true;
23183
- track.src = m.captions;
23184
- v.append(track);
23185
- }
23186
- v.src = m.src;
23187
- v.addEventListener("play", () => onEvent?.({ what: "play", time: round2(v.currentTime) }));
23188
- v.addEventListener("pause", () => onEvent?.({ what: "pause", time: round2(v.currentTime) }));
23189
- v.addEventListener("seeked", () => {
23190
- if (v.paused)
23191
- onEvent?.({ what: "seek", time: round2(v.currentTime) });
23248
+ let pills = null;
23249
+ let marking = null;
23250
+ const p = player(host, {
23251
+ ...look,
23252
+ src: m.src,
23253
+ ...m.poster ? { poster: m.poster } : {},
23254
+ ...m.captions ? { captions: m.captions } : {},
23255
+ ...aspect ? { aspect } : {},
23256
+ ...m.label ? { label: m.label } : {},
23257
+ ...m.chapters?.length ? { chapters: m.chapters.map((c) => ({ label: c.label, start: c.start, ...c.id ? { id: c.id } : {} })) } : {},
23258
+ onPlay: (on, time) => {
23259
+ marking?.kick();
23260
+ onEvent?.({ what: on ? "play" : "pause", time: round2(time) });
23261
+ },
23262
+ onSeek: (time) => {
23263
+ pills?.sync();
23264
+ marking?.kick();
23265
+ onEvent?.({ what: "seek", time: round2(time) });
23266
+ }
23192
23267
  });
23193
- frame.append(v);
23194
- host.append(frame);
23195
- const pills = m.chapters?.length ? chapterPills(host, m.chapters, {
23196
- now: () => v.currentTime,
23268
+ const v = p.video;
23269
+ pills = m.chapters?.length ? chapterPills(host, m.chapters, {
23270
+ now: () => p.time,
23197
23271
  seek: (t) => {
23198
- v.currentTime = t;
23199
- v.play().catch(() => {});
23272
+ p.seek(t, { glide: true });
23273
+ p.play();
23200
23274
  },
23201
23275
  onEvent
23202
23276
  }) : null;
@@ -23204,36 +23278,33 @@ function mountVideo(host, m, onEvent) {
23204
23278
  if (pills)
23205
23279
  for (const type of ["timeupdate", "seeking", "seeked"])
23206
23280
  v.addEventListener(type, sync);
23281
+ const frame = p.element.querySelector(".knobs-player-frame") ?? p.element;
23207
23282
  const marks = m.annotations?.length ? annotationLayer(frame, m.annotations, () => ({
23208
23283
  rects: [
23209
23284
  contain({ w: frame.clientWidth, h: frame.clientHeight }, { w: v.videoWidth, h: v.videoHeight })
23210
23285
  ],
23211
- time: v.currentTime
23286
+ time: p.time
23212
23287
  })) : null;
23213
- const marking = marks ? follow(doc.defaultView, () => !v.paused, () => marks.update()) : null;
23214
- if (marking)
23215
- for (const type of ["play", "seeked", "loadedmetadata", "timeupdate"])
23216
- v.addEventListener(type, () => marking.kick());
23288
+ if (marks) {
23289
+ marking = follow(doc.defaultView, () => p.playing, () => marks.update());
23290
+ for (const type of ["seeked", "loadedmetadata", "timeupdate"])
23291
+ v.addEventListener(type, () => marking?.kick());
23292
+ }
23217
23293
  return {
23218
- pause: () => v.pause(),
23219
- seekBy: (d) => {
23220
- v.currentTime = Math.max(0, Math.min(v.duration || Infinity, v.currentTime + d));
23221
- },
23294
+ pause: () => p.pause(),
23295
+ seekBy: (d) => p.seekBy(d),
23222
23296
  context: () => ({
23223
23297
  kind: "video",
23224
- time: round2(v.currentTime),
23225
- ...Number.isFinite(v.duration) ? { duration: round2(v.duration) } : {},
23226
- playing: !v.paused,
23298
+ time: round2(p.time),
23299
+ ...p.duration ? { duration: round2(p.duration) } : {},
23300
+ playing: p.playing,
23227
23301
  ...pills?.current ? { chapter: pills.current.label } : {}
23228
23302
  }),
23229
23303
  dispose: () => {
23230
23304
  marking?.stop();
23231
23305
  marks?.dispose();
23232
23306
  pills?.dispose();
23233
- v.pause();
23234
- v.removeAttribute("src");
23235
- v.load();
23236
- frame.remove();
23307
+ p.dispose();
23237
23308
  }
23238
23309
  };
23239
23310
  }
@@ -23494,7 +23565,7 @@ function mediaSlot(container, options) {
23494
23565
  case "diff":
23495
23566
  return void mountDiff(host, m, look, alive, options.onEvent).then(done, fail("diff"));
23496
23567
  case "video":
23497
- return done(mountVideo(host, m, options.onEvent));
23568
+ return void mountVideo(host, m, look, alive, options.onEvent).then(done, fail("video"));
23498
23569
  case "image":
23499
23570
  return done(mountImage(host, m));
23500
23571
  case "code":
@@ -24477,11 +24548,12 @@ function walkthrough(container, options) {
24477
24548
  line.append(s);
24478
24549
  return s;
24479
24550
  };
24480
- const findPRIn = (n) => findPR(page.prs, n, page.repo);
24551
+ const findPRIn = (n, kind = "pr") => findPR(page.prs, n, page.repo, kind);
24481
24552
  const metaLink = (line, text, href) => {
24482
24553
  const n = prNumber(href);
24483
- if (n) {
24484
- const chip = prChip(doc, n, { href, ...findPRIn(n) });
24554
+ const issue = n ? null : issueNumber(href);
24555
+ if (n || issue) {
24556
+ const chip = n ? prChip(doc, n, { ...findPRIn(n), href }) : prChip(doc, issue, { ...findPRIn(issue, "issue"), href });
24485
24557
  line.append(chip);
24486
24558
  return chip;
24487
24559
  }