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

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
@@ -4995,7 +4995,7 @@ function planPill(state, next, flags = {}) {
4995
4995
  if (t && t.x === next.x && t.y === next.y && t.w === next.w && t.h === next.h)
4996
4996
  return { kind: "stay" };
4997
4997
  const d = Math.hypot(next.x - state.at.x, next.y - state.at.y);
4998
- return { kind: "travel", box: next, spring: pillSpring(d) };
4998
+ return { kind: "travel", box: next, spring: pillSpring(d, flags.spring) };
4999
4999
  }
5000
5000
  function selectionPill(container, options) {
5001
5001
  const doc = container.ownerDocument;
@@ -5019,7 +5019,7 @@ function selectionPill(container, options) {
5019
5019
  container.prepend(pill);
5020
5020
  if (copyOn)
5021
5021
  container.append(copy);
5022
- const { omega, zeta } = springMotion(SPRINGS.default);
5022
+ const { omega, zeta } = springMotion(options.spring ?? SPRINGS.default);
5023
5023
  const springs = [0, 0, 0, 0].map(() => new Spring(0, omega, zeta, 0.01));
5024
5024
  const [sx, sy, sw, sh] = springs;
5025
5025
  let shown = false;
@@ -5120,7 +5120,8 @@ function selectionPill(container, options) {
5120
5120
  const plan = planPill({ shown, at: shown ? at() : null, target }, next, {
5121
5121
  first,
5122
5122
  reduced: reducedMotion(win),
5123
- ...o?.snap ? { snap: true } : {}
5123
+ ...o?.snap ? { snap: true } : {},
5124
+ ...options.spring ? { spring: options.spring } : {}
5124
5125
  });
5125
5126
  first = false;
5126
5127
  for (const n of clones.values())
@@ -8341,6 +8342,7 @@ function compare(container, options) {
8341
8342
  let loupeHeld = false;
8342
8343
  let pointer = null;
8343
8344
  let drag = null;
8345
+ let tap = null;
8344
8346
  const touches = new Map;
8345
8347
  let pinch = null;
8346
8348
  const zu = new Spring(0, ZOOM.omega, ZOOM.zeta, 0.00001);
@@ -9172,15 +9174,18 @@ function compare(container, options) {
9172
9174
  touches.set(e.pointerId, p);
9173
9175
  stage.setPointerCapture(e.pointerId);
9174
9176
  if (touches.size >= 2) {
9177
+ tap = null;
9175
9178
  e.preventDefault();
9176
9179
  return startPinch();
9177
9180
  }
9181
+ tap = e.target === handle ? null : { id: e.pointerId, x: p.x, y: p.y };
9178
9182
  if (mode === "wipe") {
9179
9183
  const wr = wipeRect(layoutNow().L.overlay);
9180
9184
  const inside = p.x >= wr.x - SLOP && p.x <= wr.x + wr.w + SLOP && p.y >= wr.y && p.y <= wr.y + wr.h;
9181
9185
  const grab = wipeGrab(p.x, wr, wipeLine.x, SLOP);
9182
9186
  if (inside && (grab.onLine || !zoomed())) {
9183
9187
  drag = { kind: "wipe", offset: grab.offset };
9188
+ tap = null;
9184
9189
  root.dataset.dragging = "";
9185
9190
  gripAt.snap(wr.h > 0 ? (p.y - wr.y) / wr.h : 0.5);
9186
9191
  e.preventDefault();
@@ -9200,6 +9205,8 @@ function compare(container, options) {
9200
9205
  const onMove = (e) => {
9201
9206
  const p = local(e);
9202
9207
  pointer = p;
9208
+ if (tap && Math.hypot(p.x - tap.x, p.y - tap.y) > TAP_SLOP)
9209
+ tap = null;
9203
9210
  if (touches.has(e.pointerId))
9204
9211
  touches.set(e.pointerId, p);
9205
9212
  if (pinch && touches.size >= 2) {
@@ -9232,6 +9239,11 @@ function compare(container, options) {
9232
9239
  touches.delete(e.pointerId);
9233
9240
  if (touches.size < 2)
9234
9241
  pinch = null;
9242
+ if (tap?.id === e.pointerId) {
9243
+ tap = null;
9244
+ if (e.type === "pointerup" && sides.some((s) => s.kind === "video"))
9245
+ api.toggle();
9246
+ }
9235
9247
  if (!drag)
9236
9248
  return;
9237
9249
  if (drag.kind === "wipe") {
@@ -9633,7 +9645,7 @@ function compare(container, options) {
9633
9645
  wake();
9634
9646
  return api;
9635
9647
  }
9636
- var COMPARE_FFMPEG = "ffmpeg -i in.mov -vf fps=30 -fps_mode cfr -c:v libx264 -preset slow -crf 18 -g 15 -keyint_min 15 -pix_fmt yuv420p -movflags +faststart -an out.mp4", LAYOUT, FADE2, FADE_REDUCED, ZOOM, WIPE_CATCH, SLOP = 12, INSET = 8, GRIP, MARK, LOUPE, FAST_SEEK_FRAMES = 15, RELEASE_MS = 3000, MODE_LABEL;
9648
+ var COMPARE_FFMPEG = "ffmpeg -i in.mov -vf fps=30 -fps_mode cfr -c:v libx264 -preset slow -crf 18 -g 15 -keyint_min 15 -pix_fmt yuv420p -movflags +faststart -an out.mp4", LAYOUT, FADE2, FADE_REDUCED, ZOOM, WIPE_CATCH, SLOP = 12, TAP_SLOP = 4, INSET = 8, GRIP, MARK, LOUPE, FAST_SEEK_FRAMES = 15, RELEASE_MS = 3000, MODE_LABEL;
9637
9649
  var init_compare = __esm(() => {
9638
9650
  init_motion();
9639
9651
  init_page_hints();
@@ -9784,7 +9796,10 @@ function player(container, options) {
9784
9796
  return tl.time;
9785
9797
  return v.ended ? Number.POSITIVE_INFINITY : v.currentTime;
9786
9798
  });
9787
- const onClick = () => tl.focus();
9799
+ const onClick = () => {
9800
+ tl.focus();
9801
+ tl.toggle();
9802
+ };
9788
9803
  frame.addEventListener("click", onClick);
9789
9804
  v.src = options.src;
9790
9805
  const api = {
@@ -16584,7 +16599,7 @@ var historyCss = `
16584
16599
  .knobs-hy-layer{position:absolute;inset:0;grid-column:2;grid-row:1/-1;pointer-events:none}
16585
16600
 
16586
16601
  /* Bands and month lines behind the lanes; today, the selection and the pointer's line over them. */
16587
- .knobs-hy-band{position:absolute;top:0;bottom:0;background:color-mix(in oklch,var(--_k-fg) 3.5%,transparent);box-shadow:inset 1px 0 0 var(--_k-border-subtle)}
16602
+ .knobs-hy-band{position:absolute;top:0;bottom:0;background:var(--_k-fill);box-shadow:inset 1px 0 0 var(--_k-border-subtle)}
16588
16603
  :is(.knobs-hy-grid,.knobs-hy-today,.knobs-hy-hover,.knobs-hy-sel){position:absolute;left:0;top:calc(var(--hy-bandrow) + var(--hy-axis));bottom:0;width:1px;margin-left:-.5px}
16589
16604
  .knobs-hy-grid{background:var(--_k-border-subtle)}
16590
16605
  .knobs-hy-today{top:calc(var(--hy-bandrow) + var(--hy-axis) - 4px);background:var(--_k-accent)}
@@ -17137,10 +17152,59 @@ function fillMedia(m, base) {
17137
17152
  };
17138
17153
  case "table":
17139
17154
  return fillTable(m);
17155
+ case "facts":
17156
+ return {
17157
+ kind: "facts",
17158
+ ...m.title ? { title: m.title } : {},
17159
+ groups: [
17160
+ ...m.facts ? [{ facts: fillFacts(m.facts) }] : [],
17161
+ ...(m.groups ?? []).map((g) => ({
17162
+ ...g.title ? { title: g.title } : {},
17163
+ facts: fillFacts(g.facts)
17164
+ }))
17165
+ ]
17166
+ };
17167
+ case "decision":
17168
+ return { kind: "decision", options: fillDecision(m.options, m.noun, base) };
17140
17169
  default:
17141
17170
  return m;
17142
17171
  }
17143
17172
  }
17173
+ function fillFacts(facts) {
17174
+ const list = Array.isArray(facts) ? facts : Object.entries(facts).map(([term, value]) => ({ term, value }));
17175
+ return list.map((f) => {
17176
+ const code = f.code === true;
17177
+ const raw = typeof f.value === "string" ? [f.value] : [...f.value];
17178
+ const value = (code ? raw.flatMap((v) => v.split(/\s*,\s*/)) : raw).map((v) => v.trim()).filter(Boolean);
17179
+ return { term: f.term, value, code };
17180
+ });
17181
+ }
17182
+ function optionColumns(count, width, min = 280, gap = 48) {
17183
+ for (let c = Math.min(count, 4);c > 1; c--)
17184
+ if (count % c === 0 && (width - gap * (c - 1)) / c >= min)
17185
+ return c;
17186
+ return 1;
17187
+ }
17188
+ function fillDecision(options, noun = "Option", base) {
17189
+ const unique = uniqueIds();
17190
+ return options.map((o, i) => {
17191
+ if ("number" in o)
17192
+ return o;
17193
+ const extra = [
17194
+ ...o.pros?.length ? [{ term: "For", value: o.pros }] : [],
17195
+ ...o.cons?.length ? [{ term: "Against", value: o.cons }] : []
17196
+ ];
17197
+ return {
17198
+ id: unique(o.id ?? slug(o.title)),
17199
+ number: `${noun} ${i + 1}`,
17200
+ title: o.title,
17201
+ ...o.summary ? { summary: o.summary } : {},
17202
+ ...o.media !== undefined ? { media: fillMedia(o.media, base) } : {},
17203
+ facts: [...o.facts ? fillFacts(o.facts) : [], ...fillFacts(extra)],
17204
+ recommended: o.recommended === true
17205
+ };
17206
+ });
17207
+ }
17144
17208
  function historyData(m) {
17145
17209
  const events = m.events ?? [];
17146
17210
  if (m.lanes || m.bands || m.states || m.axis)
@@ -17345,7 +17409,8 @@ function normalizeWalkthrough(doc) {
17345
17409
  letter: String.fromCharCode(65 + oi % 26),
17346
17410
  ...o.caption ? { caption: o.caption } : {},
17347
17411
  ...o.media !== undefined ? { media: fillMedia(o.media, doc.base) } : {},
17348
- suggested: o.suggested === true
17412
+ ...o.facts ? { facts: fillFacts(o.facts) } : {},
17413
+ suggested: o.suggested === true || o.recommended === true
17349
17414
  }));
17350
17415
  const list = step.media === undefined ? [] : Array.isArray(step.media) ? step.media : [step.media];
17351
17416
  const asked = step.ask === "verdict" ? "note" : step.ask;
@@ -21804,6 +21869,230 @@ init_motion();
21804
21869
  init_page_hints();
21805
21870
  init_icons();
21806
21871
  init_look();
21872
+
21873
+ // src/review/foundations.ts
21874
+ init_motion();
21875
+ var TRACKING = 0.03;
21876
+ var MEASURE = "66ch";
21877
+ var FONT = {
21878
+ sans: 'system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI Variable Text","Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif',
21879
+ mono: 'ui-monospace,"SF Mono",SFMono-Regular,"Cascadia Mono","JetBrains Mono",Menlo,Consolas,monospace'
21880
+ };
21881
+ var FEATURES = {
21882
+ figures: "tabular-nums",
21883
+ data: '"tnum" 1,"ss01" 1',
21884
+ code: "none"
21885
+ };
21886
+ var MOTION2 = [
21887
+ {
21888
+ name: "hover",
21889
+ spring: null,
21890
+ ms: "0 in, 120 out",
21891
+ use: "A hover's fill: on at once, off softly"
21892
+ },
21893
+ {
21894
+ name: "quick",
21895
+ spring: FADE,
21896
+ use: "Replacing a whole thing in place: fade, scale .98, blur 3 px"
21897
+ },
21898
+ { name: "close", spring: CLOSE, use: "A popover closing" },
21899
+ { name: "snappy", spring: SPRINGS.snappy, use: "Controls: a toggle, a pick, a chapter row" },
21900
+ { name: "state", spring: SPRINGS.default, use: "The selection pill, a disclosure opening" },
21901
+ {
21902
+ name: "calm",
21903
+ spring: SPRINGS.calm,
21904
+ use: "Layout: a compare's height, the page scrolling to a step"
21905
+ },
21906
+ { name: "smooth", spring: SPRINGS.smooth, use: "A step opening to the full window" }
21907
+ ];
21908
+ var foundationCss = `
21909
+ :where(.knobs-walkthrough,.knobs-wt-slot,.knobs-review){
21910
+ --_kr-track:calc((var(--_k-type-body) - 1em) * ${TRACKING});
21911
+ --_kr-measure:var(--k-walkthrough-measure,${MEASURE});
21912
+ --_kr-block:calc(var(--_k-space-title) * 2);
21913
+ --_kr-data:${FEATURES.data};
21914
+ letter-spacing:var(--_kr-track);font-variant-numeric:${FEATURES.figures};font-optical-sizing:auto;font-kerning:normal}
21915
+ :where(.knobs-walkthrough,.knobs-wt-slot,.knobs-review) :where(*){letter-spacing:var(--_kr-track)}
21916
+ :where(.knobs-walkthrough,.knobs-wt-slot,.knobs-review) :where(code,pre,kbd,samp,.knobs-wt-mono){letter-spacing:0;font-variant-ligatures:${FEATURES.code}}
21917
+ `;
21918
+
21919
+ // src/themes/review.ts
21920
+ init_define();
21921
+
21922
+ // src/themes/void.ts
21923
+ init_define();
21924
+ init_knobs();
21925
+ var voidTheme = defineTheme({
21926
+ name: "void",
21927
+ extends: knobs,
21928
+ gray: { hue: 0, chroma: 0 },
21929
+ accent: { dark: "#ffffff", light: "#1c1c1c" },
21930
+ material: "flat",
21931
+ tokens: {
21932
+ dark: {
21933
+ "bg-page": "#000000",
21934
+ "bg-inset": "#0a0a0a",
21935
+ bg: "#000000",
21936
+ "bg-raised": "#1c1c1e",
21937
+ "bg-overlay": "#2c2c2e",
21938
+ fg: "#ededed",
21939
+ "fg-2": "#cecece",
21940
+ "fg-3": "#989898",
21941
+ "fg-4": "#7a7a7a",
21942
+ "compare-stage": "#000000"
21943
+ }
21944
+ }
21945
+ });
21946
+
21947
+ // src/themes/review.ts
21948
+ var reviewTheme = defineTheme({
21949
+ name: "review",
21950
+ extends: voidTheme,
21951
+ font: FONT,
21952
+ tokens: {
21953
+ dark: {
21954
+ fill: "#ededed0f",
21955
+ "fill-hover": "#ededed1a",
21956
+ "fill-selected": "#ededed24",
21957
+ "border-subtle": "#ededed47",
21958
+ border: "#ededed4f",
21959
+ "border-strong": "#585858"
21960
+ },
21961
+ light: {
21962
+ "bg-page": "#ffffff",
21963
+ "bg-inset": "#f7f7f7",
21964
+ bg: "#ffffff",
21965
+ "bg-raised": "#ffffff",
21966
+ "bg-overlay": "#ffffff",
21967
+ fill: "#1919190a",
21968
+ "fill-hover": "#19191912",
21969
+ "fill-selected": "#1919191c",
21970
+ "border-subtle": "#19191917",
21971
+ border: "#1919191d",
21972
+ "border-strong": "#dcdcdc"
21973
+ }
21974
+ }
21975
+ });
21976
+
21977
+ // src/ui/review-look.ts
21978
+ var MODES2 = { dark: "dark", light: "light", auto: "system" };
21979
+ var THEMED = '[data-knobs-theme]:not([data-knobs-theme="dark"],[data-knobs-theme="light"])';
21980
+ function reviewLook(options, container) {
21981
+ const t = options.theme;
21982
+ if (typeof t === "object")
21983
+ return options;
21984
+ if (!t && container?.closest(THEMED))
21985
+ return options;
21986
+ const appearance = options.appearance ?? (t ? MODES2[t] : undefined);
21987
+ return { ...options, theme: reviewTheme, ...appearance ? { appearance } : {} };
21988
+ }
21989
+
21990
+ // src/ui/review-options.ts
21991
+ var el2 = (doc, tag, cls) => {
21992
+ const n = doc.createElement(tag);
21993
+ if (cls)
21994
+ n.className = cls;
21995
+ return n;
21996
+ };
21997
+ function renderFacts(doc, groups) {
21998
+ const box = el2(doc, "div", "knobs-wt-facts");
21999
+ for (const g of groups) {
22000
+ if (!g.facts.length && !g.title)
22001
+ continue;
22002
+ const group = el2(doc, "section", "knobs-wt-fgroup");
22003
+ if (g.title) {
22004
+ const head = el2(doc, "h3", "knobs-wt-fhead");
22005
+ head.innerHTML = inlineMarkdown(g.title);
22006
+ group.append(head);
22007
+ }
22008
+ if (g.facts.length)
22009
+ group.append(factList(doc, g.facts));
22010
+ box.append(group);
22011
+ }
22012
+ return box;
22013
+ }
22014
+ function factList(doc, facts) {
22015
+ const dl = el2(doc, "dl", "knobs-wt-fterms");
22016
+ for (const f of facts) {
22017
+ const row = el2(doc, "div", "knobs-wt-fact");
22018
+ const dt = el2(doc, "dt");
22019
+ dt.innerHTML = inlineMarkdown(f.term);
22020
+ const dd = el2(doc, "dd");
22021
+ if (f.code) {
22022
+ dd.dataset.code = "";
22023
+ for (const v of f.value) {
22024
+ const chip = el2(doc, "code");
22025
+ chip.textContent = v;
22026
+ dd.append(chip);
22027
+ }
22028
+ } else
22029
+ for (const v of f.value) {
22030
+ const line = el2(doc, "div");
22031
+ line.innerHTML = inlineMarkdown(v);
22032
+ dd.append(line);
22033
+ }
22034
+ row.append(dt, dd);
22035
+ dl.append(row);
22036
+ }
22037
+ return dl;
22038
+ }
22039
+ function optionTile(doc, o, options = {}) {
22040
+ const tile = el2(doc, "div", "knobs-wt-option");
22041
+ const head = options.head ?? el2(doc, "div");
22042
+ head.classList.add("knobs-wt-ohead");
22043
+ const kicker = el2(doc, "span", "knobs-wt-okicker");
22044
+ const n = el2(doc, "span");
22045
+ n.textContent = o.number;
22046
+ kicker.append(n);
22047
+ if (o.recommended) {
22048
+ const rec = el2(doc, "span", "knobs-wt-orec");
22049
+ rec.textContent = "Recommended";
22050
+ kicker.append(rec);
22051
+ }
22052
+ const title = el2(doc, "span", "knobs-wt-otitle");
22053
+ title.innerHTML = inlineMarkdown(o.title);
22054
+ head.append(kicker, title);
22055
+ tile.append(head);
22056
+ if (o.summary) {
22057
+ const p = el2(doc, "p", "knobs-wt-osum");
22058
+ p.innerHTML = inlineMarkdown(o.summary);
22059
+ tile.append(p);
22060
+ }
22061
+ options.media?.(tile);
22062
+ if (o.facts?.length)
22063
+ tile.append(renderFacts(doc, [{ facts: o.facts }]));
22064
+ return { tile, head, kicker };
22065
+ }
22066
+ function layoutOptions(root, tiles) {
22067
+ let cols = 0;
22068
+ const layout = () => {
22069
+ const width = root.clientWidth;
22070
+ if (!width)
22071
+ return;
22072
+ const next = optionColumns(tiles.length, width);
22073
+ if (next === cols)
22074
+ return;
22075
+ cols = next;
22076
+ root.style.setProperty("--kw-cols", String(cols));
22077
+ const rows = Math.ceil(tiles.length / cols);
22078
+ tiles.forEach((t, i) => {
22079
+ const row = Math.floor(i / cols);
22080
+ t.dataset.col = String(i % cols);
22081
+ t.dataset.row = String(row);
22082
+ t.toggleAttribute("data-end", i % cols === cols - 1);
22083
+ t.toggleAttribute("data-last-row", row === rows - 1);
22084
+ });
22085
+ };
22086
+ layout();
22087
+ const win = root.ownerDocument.defaultView;
22088
+ if (!win || typeof win.ResizeObserver !== "function")
22089
+ return () => {};
22090
+ const ro = new win.ResizeObserver(layout);
22091
+ ro.observe(root);
22092
+ return () => ro.disconnect();
22093
+ }
22094
+
22095
+ // src/ui/walkthrough.ts
21807
22096
  init_selection_pill();
21808
22097
 
21809
22098
  // src/ui/walkthrough-annotations.ts
@@ -21924,19 +22213,19 @@ init_selection_pill();
21924
22213
  var GLYPH = {
21925
22214
  caret: icon("caretRight", { small: true })
21926
22215
  };
21927
- var el2 = (doc, tag, cls) => {
22216
+ var el3 = (doc, tag, cls) => {
21928
22217
  const n = doc.createElement(tag);
21929
22218
  if (cls)
21930
22219
  n.className = cls;
21931
22220
  return n;
21932
22221
  };
21933
22222
  var text2 = (doc, tag, cls, value) => {
21934
- const n = el2(doc, tag, cls);
22223
+ const n = el3(doc, tag, cls);
21935
22224
  n.textContent = value;
21936
22225
  return n;
21937
22226
  };
21938
22227
  var prose = (doc, tag, cls, value) => {
21939
- const n = el2(doc, tag, cls);
22228
+ const n = el3(doc, tag, cls);
21940
22229
  n.innerHTML = inlineMarkdown(value);
21941
22230
  return n;
21942
22231
  };
@@ -21992,10 +22281,10 @@ function mountHistory(host, m, steps) {
21992
22281
  function mountStack(host, m, steps, findPR) {
21993
22282
  const doc = host.ownerDocument;
21994
22283
  const layout = stackLayout(m);
21995
- const root = el2(doc, "div", "knobs-wt-stack");
22284
+ const root = el3(doc, "div", "knobs-wt-stack");
21996
22285
  root.toggleAttribute("data-flat", layout.flat);
21997
22286
  const lane = (kind) => {
21998
- const s = el2(doc, "span", "knobs-wt-lane");
22287
+ const s = el3(doc, "span", "knobs-wt-lane");
21999
22288
  s.dataset.rail = kind;
22000
22289
  return s;
22001
22290
  };
@@ -22006,9 +22295,9 @@ function mountStack(host, m, steps, findPR) {
22006
22295
  };
22007
22296
  const from = layout.trunk ? 0 : 1;
22008
22297
  if (layout.trunk && layout.rows.length) {
22009
- const row = el2(doc, "div", "knobs-wt-srow");
22298
+ const row = el3(doc, "div", "knobs-wt-srow");
22010
22299
  row.dataset.trunk = "";
22011
- const lanes = el2(doc, "span", "knobs-wt-lanes");
22300
+ const lanes = el3(doc, "span", "knobs-wt-lanes");
22012
22301
  lanes.setAttribute("aria-hidden", "true");
22013
22302
  lanes.append(node(true));
22014
22303
  row.append(lanes, text2(doc, "span", "knobs-wt-trunk", layout.trunk));
@@ -22016,9 +22305,9 @@ function mountStack(host, m, steps, findPR) {
22016
22305
  }
22017
22306
  for (const r of layout.rows) {
22018
22307
  const it = r.item;
22019
- const row = el2(doc, "div", "knobs-wt-srow");
22308
+ const row = el3(doc, "div", "knobs-wt-srow");
22020
22309
  if (!layout.flat) {
22021
- const lanes = el2(doc, "span", "knobs-wt-lanes");
22310
+ const lanes = el3(doc, "span", "knobs-wt-lanes");
22022
22311
  lanes.setAttribute("aria-hidden", "true");
22023
22312
  for (let k = from;k < r.depth; k++)
22024
22313
  lanes.append(lane(r.rails[k] ? "pass" : "none"));
@@ -22027,8 +22316,8 @@ function mountStack(host, m, steps, findPR) {
22027
22316
  lanes.append(node(r.children > 0));
22028
22317
  row.append(lanes);
22029
22318
  }
22030
- const main = el2(doc, "div", "knobs-wt-smain");
22031
- const line = el2(doc, "div", "knobs-wt-sline");
22319
+ const main = el3(doc, "div", "knobs-wt-smain");
22320
+ const line = el3(doc, "div", "knobs-wt-sline");
22032
22321
  if (it.order)
22033
22322
  line.append(text2(doc, "span", "knobs-wt-sord", it.order));
22034
22323
  const known = findPR?.(it.pr.replace(/^#/, "")) ?? null;
@@ -22059,7 +22348,7 @@ function mountStack(host, m, steps, findPR) {
22059
22348
  main.append(line);
22060
22349
  if (it.summary)
22061
22350
  main.append(prose(doc, "p", "knobs-wt-ssummary", it.summary));
22062
- const meta = el2(doc, "div", "knobs-wt-smeta");
22351
+ const meta = el3(doc, "div", "knobs-wt-smeta");
22063
22352
  if (it.head) {
22064
22353
  const branch = r.offBase ? `${it.head} → ${r.offBase}` : it.head;
22065
22354
  meta.append(text2(doc, "span", "knobs-wt-sbranch", branch));
@@ -22084,7 +22373,7 @@ function mountStack(host, m, steps, findPR) {
22084
22373
  return { dispose: () => root.remove(), context: () => ({ kind: "stack" }) };
22085
22374
  }
22086
22375
  function delta(doc, d) {
22087
- const s = el2(doc, "span", "knobs-wt-delta");
22376
+ const s = el3(doc, "span", "knobs-wt-delta");
22088
22377
  if (typeof d === "string") {
22089
22378
  s.textContent = d;
22090
22379
  return s;
@@ -22124,13 +22413,13 @@ var cellAttrs = (td, c) => {
22124
22413
  function mountTable(host, m, steps) {
22125
22414
  const doc = host.ownerDocument;
22126
22415
  const win = doc.defaultView;
22127
- const box = el2(doc, "div", "knobs-wt-tablebox");
22128
- const scroller = el2(doc, "div", "knobs-wt-table");
22129
- const table = el2(doc, "table");
22416
+ const box = el3(doc, "div", "knobs-wt-tablebox");
22417
+ const scroller = el3(doc, "div", "knobs-wt-table");
22418
+ const table = el3(doc, "table");
22130
22419
  if (m.caption)
22131
22420
  table.append(text2(doc, "caption", "", m.caption));
22132
- const head = el2(doc, "thead");
22133
- const hr = el2(doc, "tr");
22421
+ const head = el3(doc, "thead");
22422
+ const hr = el3(doc, "tr");
22134
22423
  for (const c of m.columns) {
22135
22424
  const th = text2(doc, "th", "", c.label);
22136
22425
  th.scope = "col";
@@ -22138,11 +22427,11 @@ function mountTable(host, m, steps) {
22138
22427
  hr.append(th);
22139
22428
  }
22140
22429
  head.append(hr);
22141
- const body = el2(doc, "tbody");
22430
+ const body = el3(doc, "tbody");
22142
22431
  for (const r of m.rows) {
22143
- const tr = el2(doc, "tr");
22432
+ const tr = el3(doc, "tr");
22144
22433
  for (const c of m.columns) {
22145
- const td = el2(doc, "td");
22434
+ const td = el3(doc, "td");
22146
22435
  cellAttrs(td, c);
22147
22436
  const cell = r[c.key];
22148
22437
  if (cell) {
@@ -22157,24 +22446,24 @@ function mountTable(host, m, steps) {
22157
22446
  }
22158
22447
  table.append(head, body);
22159
22448
  scroller.append(table);
22160
- const stack = el2(doc, "div", "knobs-wt-tstack");
22449
+ const stack = el3(doc, "div", "knobs-wt-tstack");
22161
22450
  stack.setAttribute("role", "list");
22162
22451
  if (m.caption)
22163
22452
  stack.setAttribute("aria-label", m.caption);
22164
22453
  const primary = m.columns.find((c) => c.key === m.primary) ?? m.columns[0];
22165
22454
  for (const r of m.rows) {
22166
- const item = el2(doc, "div", "knobs-wt-trow");
22455
+ const item = el3(doc, "div", "knobs-wt-trow");
22167
22456
  item.setAttribute("role", "listitem");
22168
- const title = el2(doc, "div", "knobs-wt-ttitle");
22457
+ const title = el3(doc, "div", "knobs-wt-ttitle");
22169
22458
  if (primary) {
22170
22459
  cellAttrs(title, primary);
22171
22460
  const cell = r[primary.key];
22172
22461
  if (cell)
22173
22462
  cellContent(doc, title, cell);
22174
22463
  }
22175
- const line = el2(doc, "div", "knobs-wt-tline");
22464
+ const line = el3(doc, "div", "knobs-wt-tline");
22176
22465
  line.append(title);
22177
- const badges = el2(doc, "div", "knobs-wt-tbadges");
22466
+ const badges = el3(doc, "div", "knobs-wt-tbadges");
22178
22467
  for (const c of m.columns) {
22179
22468
  const cell = r[c.key];
22180
22469
  if (c !== primary && cell?.badge)
@@ -22183,14 +22472,14 @@ function mountTable(host, m, steps) {
22183
22472
  if (badges.childNodes.length)
22184
22473
  line.append(badges);
22185
22474
  item.append(line);
22186
- const terms = el2(doc, "dl", "knobs-wt-tterms");
22475
+ const terms = el3(doc, "dl", "knobs-wt-tterms");
22187
22476
  for (const c of m.columns) {
22188
22477
  if (c === primary)
22189
22478
  continue;
22190
22479
  const cell = r[c.key];
22191
22480
  if (!cell || cell.text === "" || cell.badge)
22192
22481
  continue;
22193
- const dd = el2(doc, "dd");
22482
+ const dd = el3(doc, "dd");
22194
22483
  cellAttrs(dd, c);
22195
22484
  dd.removeAttribute("data-align");
22196
22485
  cellContent(doc, dd, cell);
@@ -22256,12 +22545,12 @@ function mountTable(host, m, steps) {
22256
22545
  }
22257
22546
  function chapterPills(host, chapters, io) {
22258
22547
  const doc = host.ownerDocument;
22259
- const list = el2(doc, "div", "knobs-wt-chapters");
22548
+ const list = el3(doc, "div", "knobs-wt-chapters");
22260
22549
  list.setAttribute("role", "group");
22261
22550
  list.setAttribute("aria-label", "Chapters");
22262
22551
  let at = -2;
22263
22552
  const rows = chapters.map((c, i) => {
22264
- const b = el2(doc, "button", "knobs-wt-chapter");
22553
+ const b = el3(doc, "button", "knobs-wt-chapter");
22265
22554
  b.type = "button";
22266
22555
  b.append(text2(doc, "span", "knobs-wt-chapter-n", String(i + 1)));
22267
22556
  b.append(text2(doc, "span", "knobs-wt-chapter-label", c.label));
@@ -22277,7 +22566,8 @@ function chapterPills(host, chapters, io) {
22277
22566
  host.append(list);
22278
22567
  const pill = selectionPill(list, {
22279
22568
  selected: () => rows[at] ?? null,
22280
- rows: () => rows
22569
+ rows: () => rows,
22570
+ spring: SPRINGS.snappy
22281
22571
  });
22282
22572
  const select = (i) => {
22283
22573
  if (i === at)
@@ -22303,13 +22593,13 @@ function disclosure(host, options) {
22303
22593
  const doc = host.ownerDocument;
22304
22594
  const win = doc.defaultView;
22305
22595
  let open = options.open === true;
22306
- const root = el2(doc, "div", "knobs-wt-disc");
22307
- const toggle = el2(doc, "button", "knobs-wt-disc-toggle");
22596
+ const root = el3(doc, "div", "knobs-wt-disc");
22597
+ const toggle = el3(doc, "button", "knobs-wt-disc-toggle");
22308
22598
  toggle.type = "button";
22309
22599
  toggle.innerHTML = GLYPH.caret;
22310
22600
  toggle.append(text2(doc, "span", "", options.label));
22311
- const viewport = el2(doc, "div", "knobs-wt-disc-body");
22312
- const content = el2(doc, "div", "knobs-wt-disc-content");
22601
+ const viewport = el3(doc, "div", "knobs-wt-disc-body");
22602
+ const content = el3(doc, "div", "knobs-wt-disc-content");
22313
22603
  content.append(options.content);
22314
22604
  viewport.append(content);
22315
22605
  root.append(toggle, viewport);
@@ -22388,12 +22678,12 @@ var prGlyph = (state) => state === "merged" ? svg12(PR_PATHS.merge) : state ===
22388
22678
  var PR_STATE = { merged: "Merged", open: "Open", draft: "Draft", closed: "Closed" };
22389
22679
  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";
22390
22680
  function prStateLabel(doc, state, checks, title) {
22391
- const s = el2(doc, "span", "knobs-wt-prstate");
22681
+ const s = el3(doc, "span", "knobs-wt-prstate");
22392
22682
  s.dataset.state = state;
22393
22683
  s.innerHTML = prGlyph(state);
22394
22684
  s.append(text2(doc, "span", "", PR_STATE[state]));
22395
22685
  if (checks) {
22396
- const c = el2(doc, "span", "knobs-wt-prchecks");
22686
+ const c = el3(doc, "span", "knobs-wt-prchecks");
22397
22687
  c.innerHTML = svg12(CHECK3);
22398
22688
  c.title = "Checks pass";
22399
22689
  c.setAttribute("aria-label", "checks pass");
@@ -22404,7 +22694,7 @@ function prStateLabel(doc, state, checks, title) {
22404
22694
  return s;
22405
22695
  }
22406
22696
  function prChip(doc, n, pr) {
22407
- const chip = el2(doc, pr.href ? "a" : "span", "knobs-wt-pr");
22697
+ const chip = el3(doc, pr.href ? "a" : "span", "knobs-wt-pr");
22408
22698
  if (pr.href) {
22409
22699
  chip.setAttribute("href", pr.href);
22410
22700
  chip.setAttribute("target", "_blank");
@@ -22418,7 +22708,7 @@ function prChip(doc, n, pr) {
22418
22708
  chip.innerHTML = issue ? svg12(ISSUE) : prGlyph(pr.state);
22419
22709
  chip.append(text2(doc, "span", "", `#${n}`));
22420
22710
  if (pr.checks) {
22421
- const c = el2(doc, "span", "knobs-wt-prchecks");
22711
+ const c = el3(doc, "span", "knobs-wt-prchecks");
22422
22712
  c.innerHTML = svg12(CHECK3);
22423
22713
  chip.append(c);
22424
22714
  }
@@ -22501,21 +22791,25 @@ var calm = springCss(SPRINGS.calm);
22501
22791
  var state = springCss(SPRINGS.default);
22502
22792
  var quick = springCss(FADE);
22503
22793
  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)";
22504
- var walkthroughCss = `
22794
+ var walkthroughCss = `${foundationCss}
22505
22795
  :where(.knobs-walkthrough,.knobs-walkthrough *,.knobs-wt-slot,.knobs-wt-slot *){box-sizing:border-box}
22506
22796
  .knobs-ui:is(.knobs-walkthrough,.knobs-wt-slot){--k-glass:0;--k-glow:0;--kw-quick:${quick.easing};--kw-quick-ms:${quick.duration}ms}
22507
22797
  .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;
22508
- --kw-measure:var(--k-walkthrough-measure,68ch);--kw-width:var(--k-walkthrough-width,960px);--kw-rail-w:220px;--kw-rail-gap:calc(var(--_k-space-group) + var(--_k-space-column) / 1.5);--kw-good:var(--k-walkthrough-good,var(--_k-ok));--kw-info:var(--k-walkthrough-info,var(--_k-info));
22798
+ --kw-measure:var(--_kr-measure);--kw-width:var(--k-walkthrough-width,960px);--kw-rail-w:220px;--kw-rail-gap:calc(var(--_k-space-group) + var(--_k-space-column) / 1.5);--kw-good:var(--k-walkthrough-good,var(--_k-ok));--kw-info:var(--k-walkthrough-info,var(--_k-info));
22509
22799
  --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);
22510
22800
  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;
22511
- letter-spacing:normal;-webkit-font-smoothing:antialiased}
22801
+ letter-spacing:var(--_kr-track);-webkit-font-smoothing:antialiased}
22512
22802
  .knobs-walkthrough [hidden]{display:none!important}
22803
+ /* Prose: wrapped to look set by hand, quotes hung in the margin (Safari). Data: figures that line
22804
+ up and can't be misread. */
22805
+ :where(.knobs-wt-summary,.knobs-wt-ssum,.knobs-wt-caption,.knobs-wt-more-text,.knobs-wt-fi-text,.knobs-wt-fi-answer,.knobs-wt-ocap,.knobs-wt-text,.knobs-wt-callout,.knobs-wt-ssummary,.knobs-wt-osum){text-wrap:pretty;hanging-punctuation:first allow-end last}
22806
+ :where(.knobs-wt-table table,.knobs-wt-tterms,.knobs-wt-facts,.knobs-wt-smeta,.knobs-wt-metaline,.knobs-wt-meta,.knobs-wt-pr,.knobs-wt-prstate,.knobs-wt-sord){font-feature-settings:var(--_kr-data)}
22513
22807
  /* (0,0,1): a host's element rules stop here, and every class rule below wins over it. */
22514
- :where(.knobs-walkthrough) :is(h1,h2,h3,p,ol,ul,dl,dd){margin:0;font:inherit;letter-spacing:inherit}
22808
+ :where(.knobs-walkthrough) :is(h1,h2,h3,p,ol,ul,dl,dd){margin:0;font:inherit;letter-spacing:var(--_kr-track)}
22515
22809
  .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)}
22516
22810
  .knobs-walkthrough a:hover{text-decoration-color:currentColor}
22517
22811
  .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)}
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;
22812
+ :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:var(--_kr-track);
22519
22813
  background:transparent;border:0;border-radius:0;margin:0;padding:0;min-width:0;box-shadow:none;outline:none}
22520
22814
 
22521
22815
  /* ——— layout: the contents rail sticky at the left edge (right if asked), and the column centred
@@ -22673,7 +22967,7 @@ var walkthroughCss = `
22673
22967
  box-shadow:var(--_ku-pane-ring),var(--_k-shadow-overlay)}
22674
22968
  .knobs-wt-on{padding-top:3px;color:var(--_k-fg-3);font-size:var(--_k-type-small);line-height:var(--_k-leading-small);white-space:nowrap;overflow:hidden;${fade2}}
22675
22969
  .knobs-wt-row{display:flex;align-items:flex-end;gap:4px}
22676
- :where(.knobs-ui) :is(.knobs-wt-input,.knobs-wt-tool,.knobs-wt-x,.knobs-wt-back){all:revert;box-sizing:border-box;font:inherit;color:inherit;letter-spacing:inherit;background:transparent;border:0;
22970
+ :where(.knobs-ui) :is(.knobs-wt-input,.knobs-wt-tool,.knobs-wt-x,.knobs-wt-back){all:revert;box-sizing:border-box;font:inherit;color:inherit;letter-spacing:var(--_kr-track);background:transparent;border:0;
22677
22971
  border-radius:0;margin:0;padding:0;min-width:0;box-shadow:none;outline:none}
22678
22972
  .knobs-ui .knobs-wt-input{flex:1;min-width:0;height:32px;max-height:140px;padding:6px 0;resize:none;color:var(--_k-fg);caret-color:var(--_k-accent);line-height:var(--_k-leading-body);font-size:var(--_k-type-body)}
22679
22973
  .knobs-ui .knobs-wt-input::placeholder{color:var(--_k-fg-3)}
@@ -22725,27 +23019,53 @@ var walkthroughCss = `
22725
23019
  .knobs-wt-step[data-full] .knobs-wt-slot[data-kind="video"] .knobs-player{flex:1;min-height:0}
22726
23020
  .knobs-wt-step[data-full] .knobs-wt-slot[data-kind="video"] .knobs-player-frame{flex:1;min-height:0;aspect-ratio:auto}
22727
23021
 
22728
- /* ——— a taste test: the options side by side, Kit's pick checked and the rest stepping back. On
22729
- the step's card, so no fill of their own ——— */
22730
- .knobs-wt-options{display:grid;grid-template-columns:repeat(var(--kw-n,2),minmax(0,1fr));gap:var(--_k-space-group) var(--_k-space-column)}
22731
- @container knobs-wt-step (max-width:640px){.knobs-wt-options{grid-template-columns:minmax(0,1fr)}}
22732
- .knobs-wt-option{position:relative;display:flex;flex-direction:column;gap:var(--_k-space-sibling);min-width:0}
22733
- .knobs-wt-option>:not(.knobs-wt-pick){transition:opacity var(--kw-snappy-ms) var(--kw-snappy)}
22734
- .knobs-wt-options[data-has-pick] .knobs-wt-option:not([data-picked])>:not(.knobs-wt-pick){opacity:.55}
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;
22736
- transition:background calc(var(--_k-hover-out) * 1ms)}
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)}
22738
- .knobs-wt-letter{flex:none;font-weight:var(--kw-strong);color:var(--_k-fg)}
22739
- .knobs-wt-olabel{flex:1;min-width:0;font-weight:var(--kw-strong);color:var(--_k-fg)}
22740
- .knobs-wt-suggested{flex:none;font-size:var(--_k-type-small);color:var(--_k-fg-3)}
22741
- .knobs-wt-check{flex:none;display:flex;width:18px;height:18px;align-items:center;justify-content:center;border-radius:50%;background:var(--_k-fill);color:transparent;
22742
- transition:background var(--kw-snappy-ms) var(--kw-snappy),color var(--kw-snappy-ms) var(--kw-snappy)}
23022
+ /* ——— options: a decision, or a taste test when Kit picks. Columns that fill whole rows (4 → 4, 2
23023
+ or 1 across; 3 → 3 or 1), a hairline between neighbours and between rows, no card of their own.
23024
+ Each: a kicker ("Option 1 · Recommended", the mark a quiet word in text colour), its title, a
23025
+ sentence, its media and what changes as facts. A pick dims the rest ——— */
23026
+ .knobs-wt-options{--kw-ogap:var(--_k-space-group);display:grid;grid-template-columns:repeat(var(--kw-cols,1),minmax(0,1fr));gap:var(--kw-ogap);min-width:0}
23027
+ .knobs-wt-option{position:relative;display:flex;flex-direction:column;gap:var(--_k-space-title);min-width:0}
23028
+ .knobs-wt-option[data-col]:not([data-col="0"])::before{content:"";position:absolute;top:0;bottom:0;left:calc(var(--kw-ogap) / -2);width:1px;background:var(--_k-border-subtle);pointer-events:none}
23029
+ .knobs-wt-option[data-col]:not([data-row="0"])::before{top:calc(var(--kw-ogap) / -2)}
23030
+ .knobs-wt-option[data-col]:not([data-last-row])::before{bottom:calc(var(--kw-ogap) / -2)}
23031
+ .knobs-wt-option[data-col]:not([data-row="0"])::after{content:"";position:absolute;top:calc(var(--kw-ogap) / -2);left:calc(var(--kw-ogap) / -2);right:calc(var(--kw-ogap) / -2);height:1px;background:var(--_k-border-subtle);pointer-events:none}
23032
+ .knobs-wt-option[data-col="0"]::after{left:0}
23033
+ .knobs-wt-option[data-end]::after{right:0}
23034
+ .knobs-wt-option>:not(.knobs-wt-ohead){transition:opacity var(--kw-snappy-ms) var(--kw-snappy)}
23035
+ .knobs-wt-options[data-has-pick] .knobs-wt-option:not([data-picked])>:not(.knobs-wt-ohead){opacity:.55}
23036
+ .knobs-wt-ohead{display:flex;flex-direction:column;gap:var(--_k-space-inside);min-width:0;text-align:left}
23037
+ .knobs-wt-okicker{display:flex;align-items:center;gap:var(--_k-space-inside);min-width:0;height:var(--_k-leading-small);color:var(--_k-fg-3);font-size:var(--_k-type-small);line-height:var(--_k-leading-small);white-space:nowrap}
23038
+ .knobs-wt-orec{color:var(--_k-fg);font-weight:var(--kw-strong)}
23039
+ .knobs-wt-orec::before{content:"·";margin-right:var(--_k-space-inside);color:var(--_k-fg-3);font-weight:var(--_k-weight)}
23040
+ .knobs-wt-otitle{color:var(--_k-fg);font-weight:var(--kw-strong);text-wrap:balance}
23041
+ .knobs-wt-osum{max-width:var(--kw-measure);color:var(--_k-fg-2)}
23042
+ /* Picking: the head is the radio, bled out so its words keep the column's edge; a fill under the
23043
+ pointer at once; a check at the kicker's end that fills when picked. */
23044
+ .knobs-ui .knobs-wt-pick{margin:-6px -8px;padding:6px 8px;border-radius:var(--_k-radius);cursor:default;transition:background calc(var(--_k-hover-out) * 1ms)}
23045
+ .knobs-ui .knobs-wt-pick:is(:hover,:focus-visible){background:var(--_k-fill-hover);transition-duration:calc(var(--_k-hover-in) * 1ms)}
23046
+ .knobs-ui .knobs-wt-pick:focus-visible{box-shadow:inset 0 0 0 1px var(--_k-focus)}
23047
+ .knobs-wt-check{flex:none;display:flex;width:16px;height:16px;margin-left:auto;align-items:center;justify-content:center;border-radius:50%;box-shadow:inset 0 0 0 1px var(--_k-border-strong);color:transparent;
23048
+ transition:background var(--kw-snappy-ms) var(--kw-snappy),color var(--kw-snappy-ms) var(--kw-snappy),box-shadow var(--kw-snappy-ms) var(--kw-snappy)}
22743
23049
  .knobs-wt-check svg{width:12px;height:12px}
22744
- .knobs-wt-option[data-picked] .knobs-wt-check{background:var(--_k-accent);color:var(--_k-bg)}
22745
- .knobs-wt-key{display:inline-flex;align-items:center;justify-content:center;min-width:15px;height:15px;padding:0 3px;border-radius:4px;background:var(--_k-fill);color:var(--_k-fg-3);
22746
- font:var(--kw-strong) var(--_k-type-small)/1 var(--_k-font);opacity:.5;transition:opacity .15s}
22747
- .knobs-wt-step[data-current] .knobs-wt-key{opacity:1}
22748
- .knobs-wt-ocap{color:var(--_k-fg-2);max-width:var(--kw-measure)}
23050
+ .knobs-wt-option[data-picked] .knobs-wt-check{background:var(--_k-accent);box-shadow:none;color:var(--_k-on-accent)}
23051
+
23052
+ /* ——— facts: terms in a fixed gutter beside their values, sharing a baseline; groups under a
23053
+ heading, a hairline between them. Code and evidence are small mono chips. Too narrow for the
23054
+ gutter, each term sits over its value ——— */
23055
+ .knobs-wt-facts{--kw-term:144px;display:flex;flex-direction:column;gap:var(--_kr-block);min-width:0}
23056
+ .knobs-wt-fgroup{position:relative;display:flex;flex-direction:column;gap:var(--_k-space-title);min-width:0}
23057
+ .knobs-wt-fgroup+.knobs-wt-fgroup::before{content:"";position:absolute;left:0;right:0;top:calc(var(--_kr-block) / -2);height:1px;background:var(--_k-border-subtle);pointer-events:none}
23058
+ .knobs-wt-fhead{margin:0;color:var(--_k-fg);font-weight:var(--kw-strong);text-wrap:balance}
23059
+ .knobs-wt-fterms{display:grid;grid-template-columns:var(--kw-term) minmax(0,1fr);gap:var(--_k-space-sibling) var(--_kr-block);align-items:baseline;margin:0;min-width:0}
23060
+ .knobs-wt-fact{display:contents}
23061
+ .knobs-wt-fterms dt{margin:0;color:var(--_k-fg-3);font-size:var(--_k-type-small);line-height:var(--_k-leading-small);text-wrap:balance}
23062
+ .knobs-wt-fterms dd{display:flex;flex-direction:column;gap:var(--_k-space-inside);min-width:0;margin:0;color:var(--_k-fg-2);overflow-wrap:anywhere;text-wrap:pretty}
23063
+ .knobs-wt-fterms dd[data-code]{flex-flow:row wrap}
23064
+ .knobs-wt-fterms dd[data-code]>code{padding:2px 6px;line-height:var(--_k-leading-label);color:var(--_k-fg)}
23065
+ @container knobs-wt-slot (max-width:400px){
23066
+ .knobs-wt-fterms{display:flex;flex-direction:column;gap:var(--_k-space-sibling)}
23067
+ .knobs-wt-fact{display:flex;flex-direction:column;gap:var(--_k-space-inside)}
23068
+ }
22749
23069
 
22750
23070
  /* ——— the rail: progress, contents, export ——— */
22751
23071
  .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)}
@@ -22799,11 +23119,11 @@ var walkthroughCss = `
22799
23119
  set apart by space, hairlines and their own colours. On a card (.knobs-surface) code drops its
22800
23120
  well for hairlines, so nothing draws a fill and a ring of its own there ——— */
22801
23121
  .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);
22802
- 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);
23122
+ font:var(--_k-weight) var(--_k-type-body)/var(--_k-leading-body) var(--_k-font);font-variant-numeric:tabular-nums;--kw-measure:var(--_kr-measure);
22803
23123
  --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)}
22804
23124
  .knobs-surface .knobs-wt-slot{--kw-frame-r:calc(var(--_k-radius-panel) - 4px)}
22805
23125
  /* A slot's label: sentence case as written, small and faint, never a heading. */
22806
- .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}}
23126
+ .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);text-transform:none;color:var(--_k-fg-3);${fade2}}
22807
23127
  .knobs-wt-body{min-width:0}
22808
23128
  /* Media mounting after the first paint, and a picture still decoding, come into focus: a fade with
22809
23129
  a small blur on the quick spring, from the top; at rest, nothing. */
@@ -22822,14 +23142,15 @@ var walkthroughCss = `
22822
23142
  .knobs-wt-frame>:is(video,img){position:absolute;inset:0;display:block;width:100%;height:100%;object-fit:contain;margin:0;border:0}
22823
23143
  .knobs-wt-frame[data-natural]{aspect-ratio:auto;background:transparent}
22824
23144
  .knobs-wt-frame[data-natural]>img{position:static;height:auto}
22825
- /* Diff, code and terminal: one plain container (no glass): a flat inset fill, a hairline ring, the
22826
- control radius and room inside, so a diff's buttons and the code under them can breathe. */
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)}
23145
+ /* Diff, code and terminal: one plain well, drawn by its hairline alone (a fill and a ring would be
23146
+ two edges for one box), the panel radius and room inside, so a diff's buttons and the code under
23147
+ them can breathe. */
23148
+ .knobs-wt-slot[data-kind="diff"]>.knobs-wt-body{padding:var(--_k-pad-card);border-radius:var(--_k-radius-panel);box-shadow:inset 0 0 0 1px var(--_k-border-subtle)}
22828
23149
  /* The diff inside it: its header row 28 high on the container's padding, a hairline under it, and
22829
23150
  no line of its own under the code (the container's ring closes it). */
22830
23151
  .knobs-wt-slot[data-kind="diff"] .knobs-ui.knobs-diff:not([data-on="pane"]){--kd-head:28px;--kd-x:0px}
22831
23152
  .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);
23153
+ .knobs-wt-code{margin:0;padding:var(--_k-pad-card);max-height:420px;overflow:auto;border-radius:var(--_k-radius-panel);box-shadow:inset 0 0 0 1px var(--_k-border-subtle);color:var(--_k-fg);
22833
23154
  font:var(--_k-type-label)/var(--_k-leading-body) var(--_k-mono);white-space:pre;tab-size:2;scrollbar-width:thin}
22834
23155
  .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)}
22835
23156
  .knobs-wt-code[data-terminal]{white-space:pre-wrap;overflow-wrap:anywhere}
@@ -22838,17 +23159,17 @@ var walkthroughCss = `
22838
23159
  .knobs-wt-out{color:var(--_k-fg-2)}
22839
23160
  .knobs-wt-metric{display:flex;flex-direction:column;gap:var(--_k-space-sibling)}
22840
23161
  .knobs-wt-mlabel{color:var(--_k-fg-2)}
22841
- .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"}
23162
+ .knobs-wt-mvalue{display:flex;align-items:baseline;flex-wrap:wrap;gap:var(--_k-space-sibling);font-feature-settings:var(--_kr-data)}
22842
23163
  .knobs-wt-mfrom{color:var(--_k-fg-3)}
22843
23164
  .knobs-wt-mto{font-size:var(--_k-type-title);line-height:var(--_k-leading-title);font-weight:var(--_k-weight-label)}
22844
23165
  .knobs-wt-mof{color:var(--_k-fg-3)}
22845
23166
  .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)}
22846
23167
  .knobs-wt-mdelta[data-tone="good"]{--kw-tone:var(--kw-good)}
22847
23168
  .knobs-wt-mdelta[data-tone="bad"]{--kw-tone:var(--_k-danger)}
22848
- .knobs-wt-mtrack{position:relative;height:4px;border-radius:2px;background:color-mix(in srgb,var(--_k-fg) 8%,transparent)}
23169
+ .knobs-wt-mtrack{position:relative;height:4px;border-radius:2px;background:var(--_k-fill-selected)}
22849
23170
  .knobs-wt-mtrack>span{position:absolute;top:0;bottom:0;left:0;border-radius:inherit}
22850
23171
  .knobs-wt-mtrack>.knobs-wt-mafter{background:var(--_k-accent)}
22851
- .knobs-wt-mtrack>.knobs-wt-mbefore{background:color-mix(in srgb,var(--_k-fg) 30%,transparent)}
23172
+ .knobs-wt-mtrack>.knobs-wt-mbefore{background:var(--_k-fg-4)}
22852
23173
  .knobs-wt-callout{--kw-tone:var(--kw-info);display:flex;gap:var(--_k-space-sibling);max-width:var(--kw-measure);color:var(--_k-fg)}
22853
23174
  .knobs-wt-callout::before{content:"";flex:none;width:6px;height:6px;margin-top:7px;border-radius:50%;background:var(--kw-tone)}
22854
23175
  .knobs-wt-callout[data-tone="good"]{--kw-tone:var(--kw-good)}
@@ -22914,7 +23235,7 @@ var walkthroughCss = `
22914
23235
  .knobs-ui .knobs-wt-stitle{font-weight:var(--_k-weight-label);line-height:var(--kw-line);color:var(--_k-fg);text-align:left}
22915
23236
  .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)}
22916
23237
  .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)}
22917
- .knobs-ui .knobs-wt-ssummary{margin:0;max-width:var(--k-walkthrough-measure,68ch);color:var(--_k-fg-2)}
23238
+ .knobs-ui .knobs-wt-ssummary{margin:0;max-width:var(--kw-measure);color:var(--_k-fg-2)}
22918
23239
  .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)}
22919
23240
  .knobs-wt-delta{display:inline-flex;gap:6px}
22920
23241
  .knobs-wt-add{color:var(--_k-diff-add)}
@@ -23130,7 +23451,7 @@ async function mountCompare(host, m, look, alive, onEvent) {
23130
23451
  before: side(before, "Before"),
23131
23452
  after: side(after, "After"),
23132
23453
  mode,
23133
- timeline: true,
23454
+ timeline: mediaKind(before.src) === "video" || mediaKind(after.src) === "video",
23134
23455
  lazy: false,
23135
23456
  ...aspect ? { aspect } : {},
23136
23457
  ...m.strip ? { strip: true } : {},
@@ -23254,7 +23575,13 @@ async function mountVideo(host, m, look, alive, onEvent) {
23254
23575
  ...m.captions ? { captions: m.captions } : {},
23255
23576
  ...aspect ? { aspect } : {},
23256
23577
  ...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 } : {} })) } : {},
23578
+ ...m.chapters?.length ? {
23579
+ chapters: m.chapters.map((c) => ({
23580
+ label: c.label,
23581
+ start: c.start,
23582
+ ...c.id ? { id: c.id } : {}
23583
+ }))
23584
+ } : {},
23258
23585
  onPlay: (on, time) => {
23259
23586
  marking?.kick();
23260
23587
  onEvent?.({ what: on ? "play" : "pause", time: round2(time) });
@@ -23487,12 +23814,39 @@ function mountProse(host, m) {
23487
23814
  host.append(el);
23488
23815
  return { dispose: () => el.remove() };
23489
23816
  }
23817
+ function mountFacts(host, m) {
23818
+ const box = renderFacts(host.ownerDocument, m.groups);
23819
+ host.append(box);
23820
+ return { dispose: () => box.remove() };
23821
+ }
23822
+ function mountDecision(host, m, slot) {
23823
+ const root = div(host.ownerDocument, "knobs-wt-options");
23824
+ const slots = [];
23825
+ const tiles = m.options.map((o) => optionTile(host.ownerDocument, o, {
23826
+ media: (tile) => {
23827
+ if (o.media)
23828
+ slots.push(slot(tile, o.media));
23829
+ }
23830
+ }).tile);
23831
+ root.append(...tiles);
23832
+ host.append(root);
23833
+ const unlayout = layoutOptions(root, tiles);
23834
+ return {
23835
+ pause: () => slots.forEach((s) => s.pause()),
23836
+ dispose: () => {
23837
+ unlayout();
23838
+ slots.forEach((s) => s.dispose());
23839
+ root.remove();
23840
+ }
23841
+ };
23842
+ }
23490
23843
  var HEAVY = new Set(["compare", "video", "diff", "mount"]);
23491
23844
  var RELEASES = new Set(["compare", "video"]);
23492
- var labelOf3 = (m) => ("label" in m) && m.kind !== "metric" ? m.label : undefined;
23493
- function mediaSlot(container, options) {
23845
+ var labelOf3 = (m) => m.kind === "facts" ? m.title : ("label" in m) && m.kind !== "metric" ? m.label : undefined;
23846
+ function mediaSlot(container, given) {
23494
23847
  const doc = container.ownerDocument;
23495
23848
  injectWalkthroughStyles(doc);
23849
+ const options = reviewLook(given, container);
23496
23850
  const look = {
23497
23851
  ...options.preset ? { preset: options.preset } : {},
23498
23852
  ...options.theme ? { theme: options.theme } : {}
@@ -23502,7 +23856,7 @@ function mediaSlot(container, options) {
23502
23856
  root.dataset.kind = m.kind;
23503
23857
  applyLook2(root, options);
23504
23858
  const label = labelOf3(m);
23505
- if (label && (m.kind === "video" || m.kind === "image" || m.kind === "code" || m.kind === "terminal")) {
23859
+ if (label && (m.kind === "video" || m.kind === "image" || m.kind === "code" || m.kind === "terminal" || m.kind === "facts")) {
23506
23860
  const l = doc.createElement("span");
23507
23861
  l.className = "knobs-wt-slot-label";
23508
23862
  l.textContent = label;
@@ -23582,6 +23936,16 @@ function mediaSlot(container, options) {
23582
23936
  return done(mountStack(host, m, options.steps, options.findPR));
23583
23937
  case "table":
23584
23938
  return done(mountTable(host, m, options.steps));
23939
+ case "facts":
23940
+ return done(mountFacts(host, m));
23941
+ case "decision":
23942
+ return done(mountDecision(host, m, (tile, media) => mediaSlot(tile, {
23943
+ ...look,
23944
+ media,
23945
+ ...options.lazy === false ? { lazy: false } : {},
23946
+ ...options.steps ? { steps: options.steps } : {},
23947
+ ...options.onEvent ? { onEvent: options.onEvent } : {}
23948
+ })));
23585
23949
  case "mount": {
23586
23950
  const cleanup = m.mount(host);
23587
23951
  return done({ dispose: () => cleanup ? cleanup() : host.textContent = "" });
@@ -23634,16 +23998,35 @@ function mediaSlot(container, options) {
23634
23998
  };
23635
23999
  }
23636
24000
  var CHECK4 = icon("check", { small: true });
24001
+ function facts(container, options) {
24002
+ const { title, facts: list, groups, ...look } = options;
24003
+ return mediaSlot(container, {
24004
+ ...look,
24005
+ media: {
24006
+ kind: "facts",
24007
+ ...title ? { title } : {},
24008
+ ...list ? { facts: list } : {},
24009
+ ...groups ? { groups } : {}
24010
+ }
24011
+ });
24012
+ }
24013
+ function decision(container, options) {
24014
+ const { options: list, noun, ...rest } = options;
24015
+ return mediaSlot(container, {
24016
+ ...rest,
24017
+ media: { kind: "decision", options: list, ...noun ? { noun } : {} }
24018
+ });
24019
+ }
23637
24020
 
23638
24021
  // src/ui/walkthrough.ts
23639
- var el3 = (doc, tag, cls) => {
24022
+ var el4 = (doc, tag, cls) => {
23640
24023
  const n = doc.createElement(tag);
23641
24024
  if (cls)
23642
24025
  n.className = cls;
23643
24026
  return n;
23644
24027
  };
23645
24028
  var prose2 = (doc, tag, cls, text) => {
23646
- const n = el3(doc, tag, cls);
24029
+ const n = el4(doc, tag, cls);
23647
24030
  n.innerHTML = inlineMarkdown(text);
23648
24031
  return n;
23649
24032
  };
@@ -23668,9 +24051,10 @@ var DOCK_GAP = 12;
23668
24051
  var CALM2 = springMotion(SPRINGS.calm);
23669
24052
  var SMOOTH = springMotion(SPRINGS.smooth);
23670
24053
  var BACK_MS = 4500;
23671
- function tasteTest(container, options) {
24054
+ function tasteTest(container, given) {
23672
24055
  const doc = container.ownerDocument;
23673
24056
  injectWalkthroughStyles(doc);
24057
+ const options = reviewLook(given, container);
23674
24058
  const look = lookOf2(options);
23675
24059
  const list = options.options.map((o, i) => ("letter" in o) ? o : {
23676
24060
  id: o.id ?? String.fromCharCode(97 + i),
@@ -23678,66 +24062,55 @@ function tasteTest(container, options) {
23678
24062
  letter: String.fromCharCode(65 + i),
23679
24063
  ...o.caption ? { caption: o.caption } : {},
23680
24064
  ...o.media !== undefined ? { media: fillMedia(o.media) } : {},
23681
- suggested: o.suggested === true
24065
+ ...o.facts ? { facts: fillFacts(o.facts) } : {},
24066
+ suggested: o.suggested === true || o.recommended === true
23682
24067
  });
23683
24068
  const pickable = options.pickable !== false;
23684
24069
  let picked = pickable ? options.picked ?? null : null;
23685
- const root = el3(doc, "div", "knobs-wt-options");
24070
+ const root = el4(doc, "div", "knobs-wt-options");
23686
24071
  if (pickable) {
23687
24072
  root.setAttribute("role", "radiogroup");
23688
24073
  root.setAttribute("aria-label", "Options");
23689
24074
  } else
23690
24075
  root.dataset.static = "";
23691
- root.style.setProperty("--kw-n", String(Math.min(list.length, 3)));
23692
24076
  if (!container.closest(".knobs-ui"))
23693
24077
  applyLook2(root, options);
23694
24078
  const slots = [];
23695
24079
  const tiles = list.map((o, i) => {
23696
- const tile = el3(doc, "div", "knobs-wt-option");
23697
- if (!pickable) {
23698
- const name = el3(doc, "span", "knobs-wt-slot-label");
23699
- name.textContent = o.label;
23700
- tile.append(name);
23701
- }
23702
- const pick = el3(doc, "button", "knobs-wt-pick");
23703
- pick.type = "button";
23704
- pick.setAttribute("role", "radio");
23705
- if (i < 9)
23706
- pick.setAttribute("aria-keyshortcuts", String(i + 1));
23707
- const letter = el3(doc, "span", "knobs-wt-letter");
23708
- letter.textContent = o.letter;
23709
- const name = el3(doc, "span", "knobs-wt-olabel");
23710
- name.textContent = o.label;
23711
- pick.append(letter, name);
23712
- if (o.suggested) {
23713
- const s = el3(doc, "span", "knobs-wt-suggested");
23714
- s.textContent = "Suggested";
23715
- pick.append(s);
23716
- }
23717
- if (i < 9) {
23718
- const key = el3(doc, "span", "knobs-wt-key");
23719
- key.textContent = String(i + 1);
23720
- pick.append(key);
23721
- }
23722
- const check = el3(doc, "span", "knobs-wt-check");
23723
- check.innerHTML = CHECK4;
23724
- pick.append(check);
23725
- pick.addEventListener("click", () => {
23726
- const next = picked === o.id ? null : o.id;
23727
- api.pick(next);
23728
- options.onPick?.(next);
24080
+ const button = pickable ? el4(doc, "button", "knobs-wt-pick") : undefined;
24081
+ const { tile, kicker } = optionTile(doc, {
24082
+ number: `Option ${i + 1}`,
24083
+ title: o.label,
24084
+ ...o.caption ? { summary: o.caption } : {},
24085
+ recommended: o.suggested,
24086
+ ...o.facts ? { facts: o.facts } : {}
24087
+ }, {
24088
+ ...button ? { head: button } : {},
24089
+ media: (t) => {
24090
+ if (o.media)
24091
+ slots.push(mediaSlot(t, {
24092
+ ...look,
24093
+ media: o.media,
24094
+ ...options.lazy === false ? { lazy: false } : {},
24095
+ ...options.steps ? { steps: options.steps } : {}
24096
+ }));
24097
+ }
23729
24098
  });
23730
- if (pickable)
23731
- tile.append(pick);
23732
- if (o.media)
23733
- slots.push(mediaSlot(tile, {
23734
- ...look,
23735
- media: o.media,
23736
- ...options.lazy === false ? { lazy: false } : {},
23737
- ...options.steps ? { steps: options.steps } : {}
23738
- }));
23739
- if (o.caption)
23740
- tile.append(prose2(doc, "p", "knobs-wt-ocap", o.caption));
24099
+ const pick = button ?? el4(doc, "button");
24100
+ if (button) {
24101
+ button.type = "button";
24102
+ button.setAttribute("role", "radio");
24103
+ if (i < 9)
24104
+ button.setAttribute("aria-keyshortcuts", String(i + 1));
24105
+ const check = el4(doc, "span", "knobs-wt-check");
24106
+ check.innerHTML = CHECK4;
24107
+ kicker.append(check);
24108
+ button.addEventListener("click", () => {
24109
+ const next = picked === o.id ? null : o.id;
24110
+ api.pick(next);
24111
+ options.onPick?.(next);
24112
+ });
24113
+ }
23741
24114
  root.append(tile);
23742
24115
  return { tile, pick };
23743
24116
  });
@@ -23751,6 +24124,7 @@ function tasteTest(container, options) {
23751
24124
  };
23752
24125
  sync();
23753
24126
  container.append(root);
24127
+ const unlayout = layoutOptions(root, tiles.map((t) => t.tile));
23754
24128
  const api = {
23755
24129
  element: root,
23756
24130
  get picked() {
@@ -23763,6 +24137,7 @@ function tasteTest(container, options) {
23763
24137
  },
23764
24138
  pause: () => slots.forEach((s) => s.pause()),
23765
24139
  dispose() {
24140
+ unlayout();
23766
24141
  slots.forEach((s) => s.dispose());
23767
24142
  root.remove();
23768
24143
  }
@@ -23779,13 +24154,14 @@ function scrollerOf(el) {
23779
24154
  }
23780
24155
  return doc.scrollingElement ?? doc.documentElement;
23781
24156
  }
23782
- function walkthrough(container, options) {
24157
+ function walkthrough(container, given) {
23783
24158
  const doc = container.ownerDocument;
23784
24159
  const win = doc.defaultView;
23785
24160
  injectWalkthroughStyles(doc);
24161
+ const options = reviewLook(given, container);
23786
24162
  const look = lookOf2(options);
23787
24163
  const storage = options.storage === undefined ? browserStorage() : options.storage;
23788
- const root = el3(doc, "div", "knobs-walkthrough");
24164
+ const root = el4(doc, "div", "knobs-walkthrough");
23789
24165
  applyLook2(root, options);
23790
24166
  container.append(root);
23791
24167
  let page = normalizeWalkthrough(options.doc);
@@ -23998,7 +24374,7 @@ function walkthrough(container, options) {
23998
24374
  return true;
23999
24375
  return false;
24000
24376
  };
24001
- const scrim = el3(doc, "div", "knobs-wt-scrim");
24377
+ const scrim = el4(doc, "div", "knobs-wt-scrim");
24002
24378
  scrim.addEventListener("click", () => setFull(false));
24003
24379
  const fullRect = () => ({
24004
24380
  x: DOCK_INSET,
@@ -24048,7 +24424,7 @@ function walkthrough(container, options) {
24048
24424
  }
24049
24425
  if (!full) {
24050
24426
  const r = v.card.getBoundingClientRect();
24051
- const hold = el3(doc, "div", "knobs-wt-hold-card");
24427
+ const hold = el4(doc, "div", "knobs-wt-hold-card");
24052
24428
  hold.style.height = `${r.height}px`;
24053
24429
  v.card.before(hold);
24054
24430
  v.card.dataset.full = "";
@@ -24198,7 +24574,7 @@ function walkthrough(container, options) {
24198
24574
  toast("Couldn't reach the microphone", { tone: "warn", look, doc });
24199
24575
  }
24200
24576
  };
24201
- const backPill = el3(doc, "button", "knobs-wt-back");
24577
+ const backPill = el4(doc, "button", "knobs-wt-back");
24202
24578
  backPill.type = "button";
24203
24579
  let backTo = null;
24204
24580
  let backTimer = 0;
@@ -24218,7 +24594,7 @@ function walkthrough(container, options) {
24218
24594
  return;
24219
24595
  backTo = key;
24220
24596
  backPill.innerHTML = ICON.back;
24221
- const words = el3(doc, "span", "knobs-wt-back-label");
24597
+ const words = el4(doc, "span", "knobs-wt-back-label");
24222
24598
  words.textContent = `Back to ${plainMarkdown(v.step.title)}`;
24223
24599
  backPill.append(words);
24224
24600
  backPill.dataset.on = "";
@@ -24226,16 +24602,16 @@ function walkthrough(container, options) {
24226
24602
  backTimer = win.setTimeout(hideBack, BACK_MS);
24227
24603
  };
24228
24604
  const bar = (() => {
24229
- const dock = el3(doc, "div", "knobs-wt-dock");
24230
- const box = el3(doc, "div", "knobs-wt-notebar");
24231
- const on = el3(doc, "div", "knobs-wt-on");
24232
- const row = el3(doc, "div", "knobs-wt-row");
24233
- const input = el3(doc, "textarea", "knobs-wt-input");
24605
+ const dock = el4(doc, "div", "knobs-wt-dock");
24606
+ const box = el4(doc, "div", "knobs-wt-notebar");
24607
+ const on = el4(doc, "div", "knobs-wt-on");
24608
+ const row = el4(doc, "div", "knobs-wt-row");
24609
+ const input = el4(doc, "textarea", "knobs-wt-input");
24234
24610
  input.rows = 1;
24235
24611
  input.setAttribute("aria-label", "Note");
24236
- const attach = el3(doc, "div", "knobs-wt-attach");
24612
+ const attach = el4(doc, "div", "knobs-wt-attach");
24237
24613
  const btn = (icon, label, run) => {
24238
- const b = el3(doc, "button", "knobs-wt-tool");
24614
+ const b = el4(doc, "button", "knobs-wt-tool");
24239
24615
  b.type = "button";
24240
24616
  b.innerHTML = icon;
24241
24617
  b.setAttribute("aria-label", label);
@@ -24246,17 +24622,17 @@ function walkthrough(container, options) {
24246
24622
  const fullBtn = btn(ICON.full, "Full window · f", () => setFull(!full));
24247
24623
  const penBtn = btn(ICON.pen, "Draw on the media", () => setInk(!ink.on));
24248
24624
  const micBtn = btn(ICON.mic, "Record a voice note", () => void toggleVoice());
24249
- const micTime = el3(doc, "span", "knobs-wt-rec");
24625
+ const micTime = el4(doc, "span", "knobs-wt-rec");
24250
24626
  micBtn.append(micTime);
24251
24627
  const sendBtn = btn(ICON.send, "Send · Enter", () => send());
24252
24628
  sendBtn.dataset.send = "";
24253
24629
  row.append(input, attach, fullBtn, penBtn, micBtn, sendBtn);
24254
24630
  box.append(on, row);
24255
- const launch = el3(doc, "button", "knobs-wt-launch");
24631
+ const launch = el4(doc, "button", "knobs-wt-launch");
24256
24632
  launch.type = "button";
24257
24633
  launch.innerHTML = ICON.pen;
24258
24634
  launch.append("Note");
24259
- const kbd = el3(doc, "kbd");
24635
+ const kbd = el4(doc, "kbd");
24260
24636
  kbd.textContent = "n";
24261
24637
  launch.append(kbd);
24262
24638
  launch.setAttribute("aria-label", "Write a note (n)");
@@ -24308,9 +24684,9 @@ function walkthrough(container, options) {
24308
24684
  }
24309
24685
  });
24310
24686
  const chip = (text, clear) => {
24311
- const c = el3(doc, "span", "knobs-wt-chip-a");
24687
+ const c = el4(doc, "span", "knobs-wt-chip-a");
24312
24688
  c.append(text);
24313
- const x = el3(doc, "button", "knobs-wt-x");
24689
+ const x = el4(doc, "button", "knobs-wt-x");
24314
24690
  x.type = "button";
24315
24691
  x.innerHTML = ICON.close;
24316
24692
  x.setAttribute("aria-label", `Remove ${text}`);
@@ -24459,9 +24835,9 @@ function walkthrough(container, options) {
24459
24835
  }
24460
24836
  };
24461
24837
  const renderNote = (v, n, entering = false) => {
24462
- const row = el3(doc, "div", "knobs-wt-n");
24838
+ const row = el4(doc, "div", "knobs-wt-n");
24463
24839
  row.dataset.id = n.id;
24464
- const meta = el3(doc, "span", "knobs-wt-n-meta");
24840
+ const meta = el4(doc, "span", "knobs-wt-n-meta");
24465
24841
  const m = n.context.media;
24466
24842
  meta.textContent = m?.time !== undefined ? formatTime(m.time) : m?.file ? m.file.split("/").pop() : "·";
24467
24843
  if (m?.time !== undefined) {
@@ -24477,11 +24853,11 @@ function walkthrough(container, options) {
24477
24853
  });
24478
24854
  }
24479
24855
  row.append(meta);
24480
- const body = el3(doc, "div", "knobs-wt-n-body");
24856
+ const body = el4(doc, "div", "knobs-wt-n-body");
24481
24857
  if (n.text)
24482
24858
  body.append(prose2(doc, "p", "knobs-wt-n-text", n.text));
24483
24859
  if (n.voice) {
24484
- const a = el3(doc, "audio", "knobs-wt-n-voice");
24860
+ const a = el4(doc, "audio", "knobs-wt-n-voice");
24485
24861
  a.controls = true;
24486
24862
  a.preload = "none";
24487
24863
  a.src = n.voice.url;
@@ -24501,7 +24877,7 @@ function walkthrough(container, options) {
24501
24877
  body.append(thumb);
24502
24878
  }
24503
24879
  row.append(body);
24504
- const x = el3(doc, "button", "knobs-wt-x");
24880
+ const x = el4(doc, "button", "knobs-wt-x");
24505
24881
  x.type = "button";
24506
24882
  x.innerHTML = ICON.close;
24507
24883
  x.setAttribute("aria-label", "Delete this note");
@@ -24541,9 +24917,9 @@ function walkthrough(container, options) {
24541
24917
  row.addEventListener("transitionend", done, { once: true });
24542
24918
  win.setTimeout(done, 600);
24543
24919
  };
24544
- const metaLine = (cls) => el3(doc, "div", `knobs-wt-metaline ${cls}`);
24920
+ const metaLine = (cls) => el4(doc, "div", `knobs-wt-metaline ${cls}`);
24545
24921
  const metaText = (line, text, cls) => {
24546
- const s = el3(doc, "span", cls);
24922
+ const s = el4(doc, "span", cls);
24547
24923
  s.textContent = text;
24548
24924
  line.append(s);
24549
24925
  return s;
@@ -24557,7 +24933,7 @@ function walkthrough(container, options) {
24557
24933
  line.append(chip);
24558
24934
  return chip;
24559
24935
  }
24560
- const a = el3(doc, "a");
24936
+ const a = el4(doc, "a");
24561
24937
  a.href = href;
24562
24938
  a.target = "_blank";
24563
24939
  a.rel = "noreferrer";
@@ -24567,9 +24943,9 @@ function walkthrough(container, options) {
24567
24943
  return a;
24568
24944
  };
24569
24945
  const feedItem = (f, now) => {
24570
- const li = el3(doc, "li", "knobs-wt-fi");
24946
+ const li = el4(doc, "li", "knobs-wt-fi");
24571
24947
  li.dataset.kind = f.kind;
24572
- const q = el3(doc, "div", "knobs-wt-fi-q");
24948
+ const q = el4(doc, "div", "knobs-wt-fi-q");
24573
24949
  const meta = metaLine("knobs-wt-fi-meta");
24574
24950
  if (f.at !== null) {
24575
24951
  const when = metaText(meta, ago(f.at, now));
@@ -24578,7 +24954,7 @@ function walkthrough(container, options) {
24578
24954
  const step = f.step ? page.steps.find((s) => s.key === f.step) : undefined;
24579
24955
  if (step) {
24580
24956
  const section = page.sections.find((s) => s.id === step.section);
24581
- const b = el3(doc, "button", "knobs-wt-fi-step");
24957
+ const b = el4(doc, "button", "knobs-wt-fi-step");
24582
24958
  b.type = "button";
24583
24959
  b.textContent = section?.link?.label ?? plainMarkdown(step.title);
24584
24960
  b.title = `Go to “${plainMarkdown(step.title)}”`;
@@ -24600,19 +24976,19 @@ function walkthrough(container, options) {
24600
24976
  q.append(prose2(doc, "p", "knobs-wt-fi-text", f.text));
24601
24977
  li.append(q);
24602
24978
  if (f.q || f.trail.length) {
24603
- const a = el3(doc, "div", "knobs-wt-fi-a");
24979
+ const a = el4(doc, "div", "knobs-wt-fi-a");
24604
24980
  if (f.q)
24605
24981
  a.append(prose2(doc, "div", "knobs-wt-fi-answer", f.text));
24606
24982
  if (f.trail.length) {
24607
- const list = el3(doc, "ol", "knobs-wt-trail");
24983
+ const list = el4(doc, "ol", "knobs-wt-trail");
24608
24984
  for (const line of f.trail) {
24609
24985
  const i = line.indexOf(" · ");
24610
- const row = el3(doc, "li");
24611
- const words = el3(doc, "span", "knobs-wt-trail-text");
24986
+ const row = el4(doc, "li");
24987
+ const words = el4(doc, "span", "knobs-wt-trail-text");
24612
24988
  words.textContent = i < 0 ? line : line.slice(i + 3);
24613
24989
  row.append(words);
24614
24990
  if (i >= 0) {
24615
- const at = el3(doc, "span", "knobs-wt-trail-at");
24991
+ const at = el4(doc, "span", "knobs-wt-trail-at");
24616
24992
  at.textContent = line.slice(0, i);
24617
24993
  row.append(at);
24618
24994
  }
@@ -24646,11 +25022,11 @@ function walkthrough(container, options) {
24646
25022
  root.dataset.rail = options.rail === false ? "none" : options.rail === "right" ? "right" : "left";
24647
25023
  root.toggleAttribute("data-cards", options.cards === true);
24648
25024
  root.toggleAttribute("data-review", review);
24649
- const layout = el3(doc, "div", "knobs-wt-layout");
24650
- const main = el3(doc, "div", "knobs-wt-main");
24651
- const flow = el3(doc, "div", "knobs-wt-flow");
24652
- const head = el3(doc, "header", "knobs-wt-head");
24653
- const h1 = el3(doc, "h1", "knobs-wt-title");
25025
+ const layout = el4(doc, "div", "knobs-wt-layout");
25026
+ const main = el4(doc, "div", "knobs-wt-main");
25027
+ const flow = el4(doc, "div", "knobs-wt-flow");
25028
+ const head = el4(doc, "header", "knobs-wt-head");
25029
+ const h1 = el4(doc, "h1", "knobs-wt-title");
24654
25030
  h1.textContent = page.title;
24655
25031
  head.append(h1);
24656
25032
  if (page.summary)
@@ -24658,13 +25034,13 @@ function walkthrough(container, options) {
24658
25034
  if (page.more)
24659
25035
  head.append(more(page.more));
24660
25036
  if (page.meta.length) {
24661
- const meta = el3(doc, "div", "knobs-wt-meta");
25037
+ const meta = el4(doc, "div", "knobs-wt-meta");
24662
25038
  for (const m of page.meta) {
24663
- const s = el3(doc, "span");
25039
+ const s = el4(doc, "span");
24664
25040
  s.append(m.label);
24665
- const b = el3(doc, "b");
25041
+ const b = el4(doc, "b");
24666
25042
  if (m.href) {
24667
- const a = el3(doc, "a");
25043
+ const a = el4(doc, "a");
24668
25044
  a.href = m.href;
24669
25045
  a.target = "_blank";
24670
25046
  a.rel = "noreferrer";
@@ -24680,14 +25056,14 @@ function walkthrough(container, options) {
24680
25056
  flow.append(head);
24681
25057
  let feedEl = null;
24682
25058
  if (page.feed.length) {
24683
- feedEl = el3(doc, "section", "knobs-wt-feed");
25059
+ feedEl = el4(doc, "section", "knobs-wt-feed");
24684
25060
  feedEl.id = "wt-latest";
24685
25061
  feedEl.setAttribute("aria-label", "Updates and answers");
24686
- const fh = el3(doc, "header", "knobs-wt-feed-head");
24687
- const h = el3(doc, "h2", "knobs-wt-feed-title");
25062
+ const fh = el4(doc, "header", "knobs-wt-feed-head");
25063
+ const h = el4(doc, "h2", "knobs-wt-feed-title");
24688
25064
  h.textContent = page.feed.some((f) => f.kind === "qa") ? "Updates & Q&A" : "Updates";
24689
25065
  fh.append(h);
24690
- const list = el3(doc, "ol", "knobs-wt-feed-list");
25066
+ const list = el4(doc, "ol", "knobs-wt-feed-list");
24691
25067
  const now = Date.now();
24692
25068
  for (const f of page.feed)
24693
25069
  list.append(feedItem(f, now));
@@ -24697,21 +25073,21 @@ function walkthrough(container, options) {
24697
25073
  let rail = null;
24698
25074
  count = fill = tocEl = null;
24699
25075
  if (options.rail !== false) {
24700
- rail = el3(doc, "aside", "knobs-wt-rail");
25076
+ rail = el4(doc, "aside", "knobs-wt-rail");
24701
25077
  rail.setAttribute("aria-label", "Contents");
24702
- tocEl = el3(doc, "nav", "knobs-wt-toc");
25078
+ tocEl = el4(doc, "nav", "knobs-wt-toc");
24703
25079
  tocEl.setAttribute("aria-label", "Contents");
24704
25080
  if (feedEl) {
24705
- const group = el3(doc, "div", "knobs-wt-toc-group");
25081
+ const group = el4(doc, "div", "knobs-wt-toc-group");
24706
25082
  const target = feedEl;
24707
25083
  group.append(tocRow("Latest", false, () => target.scrollIntoView({ block: "start", behavior: reduced() ? "auto" : "smooth" })));
24708
25084
  tocEl.append(group);
24709
25085
  }
24710
25086
  if (review) {
24711
- const progress = el3(doc, "div", "knobs-wt-progress");
24712
- count = el3(doc, "div", "knobs-wt-count");
24713
- const meter = el3(doc, "div", "knobs-wt-bar");
24714
- fill = el3(doc, "div", "knobs-wt-fill");
25087
+ const progress = el4(doc, "div", "knobs-wt-progress");
25088
+ count = el4(doc, "div", "knobs-wt-count");
25089
+ const meter = el4(doc, "div", "knobs-wt-bar");
25090
+ fill = el4(doc, "div", "knobs-wt-fill");
24715
25091
  meter.append(fill);
24716
25092
  progress.append(count, meter);
24717
25093
  rail.append(progress);
@@ -24720,15 +25096,15 @@ function walkthrough(container, options) {
24720
25096
  if (review)
24721
25097
  rail.append(actions(true));
24722
25098
  if (review && options.keys !== false) {
24723
- const keys = el3(doc, "div", "knobs-wt-keys");
25099
+ const keys = el4(doc, "div", "knobs-wt-keys");
24724
25100
  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" : "");
24725
25101
  rail.append(keys);
24726
25102
  }
24727
25103
  }
24728
25104
  for (const section of page.sections) {
24729
- const sec = el3(doc, "section", "knobs-wt-section");
25105
+ const sec = el4(doc, "section", "knobs-wt-section");
24730
25106
  sec.id = `wt-${section.id}`;
24731
- const sh = el3(doc, "header", "knobs-wt-shead");
25107
+ const sh = el4(doc, "header", "knobs-wt-shead");
24732
25108
  const status = review ? section.status : undefined;
24733
25109
  if (section.kicker || section.link || status) {
24734
25110
  const meta = metaLine("knobs-wt-kicker");
@@ -24742,10 +25118,10 @@ function walkthrough(container, options) {
24742
25118
  }
24743
25119
  sh.append(meta);
24744
25120
  }
24745
- const h2 = el3(doc, "h2", "knobs-wt-heading");
25121
+ const h2 = el4(doc, "h2", "knobs-wt-heading");
24746
25122
  h2.innerHTML = inlineMarkdown(section.title);
24747
25123
  sh.append(h2);
24748
- const intro = el3(doc, "div", "knobs-wt-sintro");
25124
+ const intro = el4(doc, "div", "knobs-wt-sintro");
24749
25125
  intro.append(sh);
24750
25126
  if (section.summary)
24751
25127
  intro.append(prose2(doc, "p", "knobs-wt-ssum", section.summary));
@@ -24754,7 +25130,7 @@ function walkthrough(container, options) {
24754
25130
  sec.append(intro);
24755
25131
  let group = null;
24756
25132
  if (tocEl) {
24757
- group = el3(doc, "div", "knobs-wt-toc-group");
25133
+ group = el4(doc, "div", "knobs-wt-toc-group");
24758
25134
  const first = section.steps[0]?.key;
24759
25135
  group.append(tocRow(plainMarkdown(section.title), false, () => first ? go(first) : undefined, section.id));
24760
25136
  tocEl.append(group);
@@ -24782,7 +25158,8 @@ function walkthrough(container, options) {
24782
25158
  if (tocEl)
24783
25159
  pill = selectionPill(tocEl, {
24784
25160
  selected: () => viewOf(current)?.toc ?? null,
24785
- rows: () => tocRows
25161
+ rows: () => tocRows,
25162
+ spring: SPRINGS.snappy
24786
25163
  });
24787
25164
  syncProgress();
24788
25165
  current = null;
@@ -24796,8 +25173,8 @@ function walkthrough(container, options) {
24796
25173
  }));
24797
25174
  };
24798
25175
  const more = (text) => {
24799
- const box = el3(doc, "div", "knobs-wt-more");
24800
- const body = el3(doc, "div", "knobs-wt-more-text");
25176
+ const box = el4(doc, "div", "knobs-wt-more");
25177
+ const body = el4(doc, "div", "knobs-wt-more-text");
24801
25178
  for (const para of text.split(/\n\s*\n/))
24802
25179
  if (para.trim())
24803
25180
  body.append(prose2(doc, "p", "", para.trim()));
@@ -24805,25 +25182,25 @@ function walkthrough(container, options) {
24805
25182
  return box;
24806
25183
  };
24807
25184
  const tocRow = (label, sub, run, section) => {
24808
- const b = el3(doc, "button", sub ? "knobs-wt-toc-row knobs-wt-toc-sub" : "knobs-wt-toc-row");
25185
+ const b = el4(doc, "button", sub ? "knobs-wt-toc-row knobs-wt-toc-sub" : "knobs-wt-toc-row");
24809
25186
  b.type = "button";
24810
25187
  if (section)
24811
25188
  b.dataset.section = section;
24812
- const t = el3(doc, "span", "knobs-wt-toc-label");
25189
+ const t = el4(doc, "span", "knobs-wt-toc-label");
24813
25190
  t.textContent = label;
24814
25191
  b.title = label;
24815
25192
  b.append(t);
24816
25193
  if (review)
24817
- b.append(el3(doc, "span", "knobs-wt-toc-end"));
25194
+ b.append(el4(doc, "span", "knobs-wt-toc-end"));
24818
25195
  b.addEventListener("mousedown", (ev) => ev.preventDefault());
24819
25196
  b.addEventListener("click", run);
24820
25197
  tocRows.push(b);
24821
25198
  return b;
24822
25199
  };
24823
25200
  const actions = (rail) => {
24824
- const row = el3(doc, "div", "knobs-wt-actions");
25201
+ const row = el4(doc, "div", "knobs-wt-actions");
24825
25202
  const btn = (text, run, primary = false) => {
24826
- const b = el3(doc, "button", "knobs-wt-btn");
25203
+ const b = el4(doc, "button", "knobs-wt-btn");
24827
25204
  b.type = "button";
24828
25205
  b.textContent = text;
24829
25206
  if (primary)
@@ -24861,13 +25238,13 @@ function walkthrough(container, options) {
24861
25238
  }
24862
25239
  };
24863
25240
  const stepView = (step, group) => {
24864
- const card = el3(doc, "article", options.cards ? "knobs-wt-step knobs-surface" : "knobs-wt-step");
25241
+ const card = el4(doc, "article", options.cards ? "knobs-wt-step knobs-surface" : "knobs-wt-step");
24865
25242
  card.id = `wt-${step.key.replace("/", "--")}`;
24866
25243
  card.tabIndex = -1;
24867
25244
  card.dataset.key = step.key;
24868
25245
  card.setAttribute("aria-label", plainMarkdown(step.title));
24869
- const head = el3(doc, "header", "knobs-wt-step-head");
24870
- const h3 = el3(doc, "h3", "knobs-wt-stepTitle");
25246
+ const head = el4(doc, "header", "knobs-wt-step-head");
25247
+ const h3 = el4(doc, "h3", "knobs-wt-stepTitle");
24871
25248
  h3.innerHTML = inlineMarkdown(step.title);
24872
25249
  head.append(h3);
24873
25250
  if (step.caption)
@@ -24879,7 +25256,7 @@ function walkthrough(container, options) {
24879
25256
  const onEvent = (e) => record({ ...e, step: step.key });
24880
25257
  let media = null;
24881
25258
  if (step.media.length) {
24882
- media = el3(doc, "div", "knobs-wt-media");
25259
+ media = el4(doc, "div", "knobs-wt-media");
24883
25260
  media.style.setProperty("--kw-n", String(Math.min(step.media.length, step.columns)));
24884
25261
  for (const m of step.media)
24885
25262
  slots.push(mediaSlot(media, { ...look, media: m, onEvent, steps: stepLinks, findPR: findPRIn }));
@@ -24899,7 +25276,7 @@ function walkthrough(container, options) {
24899
25276
  });
24900
25277
  media ??= taste.element;
24901
25278
  }
24902
- const notes = el3(doc, "div", "knobs-wt-notes");
25279
+ const notes = el4(doc, "div", "knobs-wt-notes");
24903
25280
  if (review)
24904
25281
  card.append(notes);
24905
25282
  card.addEventListener("focusin", () => {
@@ -24913,7 +25290,7 @@ function walkthrough(container, options) {
24913
25290
  if (group) {
24914
25291
  toc = tocRow(plainMarkdown(step.title), true, () => go(step.key));
24915
25292
  if (review) {
24916
- glyph = el3(doc, "span", "knobs-wt-toc-glyph");
25293
+ glyph = el4(doc, "span", "knobs-wt-toc-glyph");
24917
25294
  toc.querySelector(".knobs-wt-toc-end").append(glyph);
24918
25295
  }
24919
25296
  group.append(toc);
@@ -25117,6 +25494,7 @@ export {
25117
25494
  contextMenu,
25118
25495
  countChanges,
25119
25496
  currentMarker,
25497
+ decision,
25120
25498
  diffActivity,
25121
25499
  diffBar,
25122
25500
  diffCounts,
@@ -25129,6 +25507,7 @@ export {
25129
25507
  digitPick,
25130
25508
  drag as dragDivider,
25131
25509
  dropTarget,
25510
+ facts,
25132
25511
  feedbackMarkdown,
25133
25512
  fileKey,
25134
25513
  filesUnder,