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

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-solid.js CHANGED
@@ -575,13 +575,14 @@ var compareCss = `
575
575
  picked: the pill's bright copy is. */
576
576
  .knobs-cmp-modes{position:relative;display:inline-flex;height:var(--_k-control-h);border-radius:var(--_k-radius);background:var(--_k-fill)}
577
577
  .knobs-cmp-modes[hidden]{display:none}
578
- .knobs-ui .knobs-cmp-mode{display:inline-flex;align-items:center;height:100%;padding:0 8px;border-radius:var(--_k-radius);color:var(--_k-fg-2);cursor:pointer;white-space:nowrap;
579
- transition:color calc(var(--_k-hover-out) * 1ms)}
580
- .knobs-ui .knobs-cmp-mode:is(:hover,:focus-visible){color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
578
+ /* Hover is a quiet fill under the arrow cursor (no hand, no brightening), there at once. */
579
+ .knobs-ui .knobs-cmp-mode{display:inline-flex;align-items:center;height:100%;padding:0 8px;border-radius:var(--_k-radius);color:var(--_k-fg-2);cursor:default;white-space:nowrap;
580
+ transition:background calc(var(--_k-hover-out) * 1ms)}
581
+ .knobs-ui .knobs-cmp-mode:not([aria-checked="true"]):is(:hover,:focus-visible){background:var(--_k-fill-hover);transition-duration:calc(var(--_k-hover-in) * 1ms)}
581
582
  /* Toggles: icon buttons, the accent's wash when on. */
582
- .knobs-ui .knobs-cmp-toggle{display:inline-flex;align-items:center;justify-content:center;width:var(--_k-control-h);height:var(--_k-control-h);border-radius:var(--_k-radius);color:var(--_k-fg-3);cursor:pointer;
583
- transition:color calc(var(--_k-hover-out) * 1ms),background calc(var(--_k-hover-out) * 1ms)}
584
- .knobs-ui .knobs-cmp-toggle:is(:hover,:focus-visible){color:var(--_k-fg);background:var(--_ku-wash);transition-duration:calc(var(--_k-hover-in) * 1ms)}
583
+ .knobs-ui .knobs-cmp-toggle{display:inline-flex;align-items:center;justify-content:center;width:var(--_k-control-h);height:var(--_k-control-h);border-radius:var(--_k-radius);color:var(--_k-fg-2);cursor:default;
584
+ transition:background calc(var(--_k-hover-out) * 1ms)}
585
+ .knobs-ui .knobs-cmp-toggle:is(:hover,:focus-visible){background:var(--_k-fill-hover);transition-duration:calc(var(--_k-hover-in) * 1ms)}
585
586
  .knobs-ui .knobs-cmp-toggle[aria-pressed="true"]{color:var(--_k-accent-fg);background:var(--_k-accent-soft)}
586
587
  .knobs-cmp-toggle svg{display:block}
587
588
  .knobs-cmp-fill{flex:1}
@@ -595,11 +596,10 @@ var compareCss = `
595
596
  .knobs-cmp-stage{position:relative;flex:1;min-height:96px;overflow:hidden;border-radius:var(--_k-radius-panel);background:var(--_ku-cmp-stage);touch-action:pan-y;outline:none}
596
597
  .knobs-compare[data-frame="flat"] .knobs-cmp-stage{border-radius:0}
597
598
  .knobs-compare[data-mode="wipe"] .knobs-cmp-stage{cursor:ew-resize}
598
- /* Videos: a click on the stage plays and pauses. */
599
- .knobs-compare[data-video]:not([data-mode="wipe"],[data-zoomed]) .knobs-cmp-stage{cursor:pointer}
600
599
  /* Unzoomed, a finger still scrolls the page up and down; zoomed, the stage takes every gesture. */
601
- .knobs-compare[data-zoomed] .knobs-cmp-stage{cursor:grab;touch-action:none}
602
- .knobs-compare[data-panning],.knobs-compare[data-panning] *{cursor:grabbing!important}
600
+ /* Zoomed, it pans: a move cursor, never a hand. */
601
+ .knobs-compare[data-zoomed] .knobs-cmp-stage{cursor:move;touch-action:none}
602
+ .knobs-compare[data-panning],.knobs-compare[data-panning] *{cursor:move!important}
603
603
  /* Wiping, the grip stands in for the cursor. */
604
604
  .knobs-compare[data-dragging],.knobs-compare[data-dragging] *{cursor:none!important}
605
605
  .knobs-cmp-sizer{position:absolute;left:0;right:0;top:0;height:0;visibility:hidden;pointer-events:none}
@@ -646,8 +646,8 @@ var compareCss = `
646
646
  opacity:0;scale:.96;pointer-events:none;transition:opacity .16s cubic-bezier(.4,0,1,1),scale .16s cubic-bezier(.4,0,1,1)}
647
647
  .knobs-cmp-zoom[data-on]{opacity:1;scale:1;pointer-events:auto;transition:opacity .2s cubic-bezier(.2,.8,.2,1),scale .2s cubic-bezier(.2,.8,.2,1)}
648
648
  :where(.knobs-ui) .knobs-cmp-zoom-reset{all:revert;box-sizing:border-box;font:inherit;letter-spacing:inherit;margin:0;border:0;outline:none;box-shadow:none;
649
- display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border-radius:999px;background:transparent;color:var(--_k-fg-2);cursor:pointer;transition:color .15s,background .15s}
650
- :where(.knobs-ui) .knobs-cmp-zoom-reset:is(:hover,:focus-visible){color:var(--_k-fg);background:color-mix(in srgb,var(--_k-fg) 12%,transparent);transition-duration:calc(var(--_k-hover-in) * 1ms)}
649
+ display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border-radius:999px;background:transparent;color:var(--_k-fg-2);cursor:default;transition:background calc(var(--_k-hover-out) * 1ms)}
650
+ :where(.knobs-ui) .knobs-cmp-zoom-reset:is(:hover,:focus-visible){background:var(--_k-fill-hover);transition-duration:calc(var(--_k-hover-in) * 1ms)}
651
651
  .knobs-cmp-note{position:absolute;left:50%;top:50%;translate:-50% -50%;color:var(--_k-fg-3);font-size:var(--_k-text-meta);pointer-events:none}
652
652
  .knobs-cmp-note:empty{display:none}
653
653
  .knobs-cmp-strip-host{flex:none;height:60px;min-height:0}
@@ -665,23 +665,29 @@ var playerCss = `
665
665
  .knobs-ui.knobs-player{--_kp-stage:var(--_k-compare-stage);display:flex;flex-direction:column;gap:6px;width:100%;outline:none;
666
666
  font:var(--_k-weight) var(--_k-font-size)/1.35 var(--_k-font);color:var(--_k-fg);letter-spacing:normal;text-align:left}
667
667
  :where(.knobs-surface,.knobs-pane:not([data-nested])>.knobs-pane-clip) .knobs-ui.knobs-player,.knobs-ui.knobs-player[data-nested]{--_kp-stage:var(--k-compare-stage,transparent)}
668
- .knobs-player-frame{position:relative;width:100%;aspect-ratio:var(--kp-aspect,16/9);overflow:hidden;border-radius:var(--_k-radius-panel);background:var(--_kp-stage);cursor:pointer}
668
+ .knobs-player-frame{position:relative;width:100%;aspect-ratio:var(--kp-aspect,16/9);overflow:hidden;border-radius:var(--_k-radius-panel);background:var(--_kp-stage)}
669
669
  .knobs-player[data-frame="flat"] .knobs-player-frame{border-radius:0}
670
670
  .knobs-player-media{position:absolute;inset:0;display:block;width:100%;height:100%;margin:0;padding:0;border:0;object-fit:contain;
671
671
  opacity:0;filter:blur(4px);transition:opacity calc(var(--_k-motion-default-ms) * 1ms) var(--_k-motion-default),filter calc(var(--_k-motion-default-ms) * 1ms) var(--_k-motion-default)}
672
672
  .knobs-player[data-ready] .knobs-player-media{opacity:1;filter:none}
673
- .knobs-player-glyph{position:absolute;left:50%;top:50%;display:grid;place-items:center;width:44px;height:44px;margin:-22px 0 0 -22px;border-radius:50%;color:var(--_k-fg);pointer-events:none;
674
- opacity:0;filter:blur(4px);scale:.96;
675
- background:color-mix(in srgb,var(--_k-bg) 52%,transparent);-webkit-backdrop-filter:blur(14px) saturate(1.6);backdrop-filter:blur(14px) saturate(1.6);
676
- box-shadow:inset 0 0 0 .5px var(--_k-border),inset 0 1px 0 var(--_k-rim);
677
- transition:opacity calc(var(--_k-motion-snappy-ms) * 1ms) var(--_k-motion-snappy),filter calc(var(--_k-motion-snappy-ms) * 1ms) var(--_k-motion-snappy),scale calc(var(--_k-motion-snappy-ms) * 1ms) var(--_k-motion-snappy)}
678
- .knobs-player-glyph svg{width:16px;height:16px}
679
- .knobs-player-glyph[data-flash]{opacity:1;filter:none;scale:1}
680
673
  .knobs-player-line{flex:none;min-height:0}
681
- /* Hover in is immediate (0 ms), out eases. The timeline's own stylesheet is look.ts's; its hover
682
- states are set here, after it, so the player's line and every timeline share them. */
674
+ /* The timeline's own stylesheet is look.ts's; what the player's row and every timeline share
675
+ about the hand is set here, after it. No hand cursor anywhere (the arrow, or a resize where it
676
+ resizes); hover is a quiet fill (fill-hover), in at once (hover-in, 0 ms), out easing. */
677
+ .knobs-ui .knobs-tl-play,.knobs-ui.knobs-timeline :is(.knobs-tl-pin,.knobs-tl-rlabel),
678
+ .knobs-ui.knobs-timeline[data-dragging] .knobs-tl-marker :is(.knobs-tl-pin,.knobs-tl-mlabel){cursor:default}
683
679
  .knobs-ui .knobs-tl-play:hover,.knobs-tl-edge:hover::after,.knobs-tl-rlabel:hover,.knobs-tl-track:hover .knobs-tl-hover,
684
680
  .knobs-timeline[data-variant="line"] .knobs-tl-track:hover .knobs-tl-segs{transition-duration:calc(var(--_k-hover-in) * 1ms)}
681
+ /* The line's row: a minimal play/pause (the filled pair, a 28px target, no fill at rest), then
682
+ the line, centred on it. */
683
+ .knobs-ui.knobs-timeline[data-variant="line"]{gap:4px;align-items:center}
684
+ .knobs-timeline[data-variant="line"] .knobs-tl-transport{display:flex}
685
+ .knobs-ui.knobs-timeline[data-variant="line"] .knobs-tl-play{width:28px;height:28px;min-width:28px;min-height:28px;border-radius:var(--_k-radius);background:transparent;color:var(--_k-fg-2);
686
+ transition:background calc(var(--_k-hover-out) * 1ms),transform .15s var(--_ku-ease)}
687
+ .knobs-ui.knobs-timeline[data-variant="line"][data-playing] .knobs-tl-play{background:transparent;color:var(--_k-fg-2)}
688
+ .knobs-ui.knobs-timeline[data-variant="line"] .knobs-tl-play:hover{background:var(--_k-fill-hover);transition-duration:calc(var(--_k-hover-in) * 1ms)}
689
+ .knobs-ui.knobs-timeline[data-variant="line"] .knobs-tl-track{cursor:default;transition:background calc(var(--_k-hover-out) * 1ms)}
690
+ .knobs-ui.knobs-timeline[data-variant="line"] .knobs-tl-track:hover{background:var(--_k-fill-hover);transition-duration:calc(var(--_k-hover-in) * 1ms)}
685
691
  `;
686
692
 
687
693
  // src/ui/overlay-css.ts
@@ -3233,16 +3239,17 @@ var init_diff_css = __esm(() => {
3233
3239
  .knobs-diff-nav>span{min-width:3.6em;text-align:center}
3234
3240
  :where(.knobs-ui) :is(.knobs-diff-btn,.knobs-diff-choice,.knobs-diff-viewed,.knobs-diff-filter>input,.knobs-diff-filter>button){all:revert;box-sizing:border-box;font:inherit;color:inherit;letter-spacing:inherit;background:transparent;border:0;border-radius:0;
3235
3241
  margin:0;padding:0;min-width:0;box-shadow:none;outline:none}
3236
- .knobs-ui .knobs-diff-btn{flex:none;display:inline-flex;align-items:center;justify-content:center;width:var(--_k-control-h);height:var(--_k-control-h);border-radius:calc(var(--_k-radius) * 999);color:var(--_k-fg-3);cursor:pointer;
3237
- transition:background .15s,color .15s,opacity .15s}
3238
- .knobs-ui .knobs-diff-btn:is(:hover,:focus-visible){background:var(--_ku-wash-hi);color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
3242
+ .knobs-ui .knobs-diff-btn{flex:none;display:inline-flex;align-items:center;justify-content:center;width:var(--_k-control-h);height:var(--_k-control-h);border-radius:calc(var(--_k-radius) * 999);color:var(--_k-fg-2);cursor:default;
3243
+ transition:background calc(var(--_k-hover-out) * 1ms),opacity .15s}
3244
+ /* Hover is a quiet fill under the arrow cursor (no hand, no brightening), there at once. */
3245
+ .knobs-ui .knobs-diff-btn:is(:hover,:focus-visible){background:var(--_k-fill-hover);transition-duration:calc(var(--_k-hover-in) * 1ms)}
3239
3246
  .knobs-ui .knobs-diff-btn[aria-pressed="true"]{color:var(--_k-fg);background:var(--_ku-wash)}
3240
3247
  .knobs-ui .knobs-diff-btn:disabled{opacity:.35;cursor:default;background:transparent}
3241
3248
  .knobs-diff-btn svg{width:16px;height:16px}
3242
3249
  /* Viewed: a check with its word, as on GitHub; the path dims once it's ticked. */
3243
3250
  .knobs-ui .knobs-diff-viewed{flex:none;display:inline-flex;align-items:center;gap:5px;height:var(--_k-control-h);padding:0 8px 0 6px;border-radius:var(--kd-ctl);color:var(--_k-fg-2);
3244
- font-size:var(--_k-text-meta);font-weight:var(--_k-weight-label);white-space:nowrap;cursor:pointer;transition:background .15s,color .15s}
3245
- .knobs-ui .knobs-diff-viewed:is(:hover,:focus-visible){background:var(--_ku-wash-hi);color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
3251
+ font-size:var(--_k-text-meta);font-weight:var(--_k-weight-label);white-space:nowrap;cursor:default;transition:background calc(var(--_k-hover-out) * 1ms),color .15s}
3252
+ .knobs-ui .knobs-diff-viewed:is(:hover,:focus-visible){background:var(--_k-fill-hover);transition-duration:calc(var(--_k-hover-in) * 1ms)}
3246
3253
  .knobs-ui .knobs-diff-viewed[aria-pressed="true"]{color:var(--_k-fg);background:var(--_ku-wash)}
3247
3254
  .knobs-diff-viewed svg{width:16px;height:16px}
3248
3255
  .knobs-diff-viewed[aria-pressed="true"] svg{color:var(--kd-add)}
@@ -3261,8 +3268,8 @@ var init_diff_css = __esm(() => {
3261
3268
  choice isn't brighter for being picked. Choices are styled by class, as the copy holds them too. */
3262
3269
  .knobs-diff-seg{position:relative;flex:none;display:grid;grid-template-columns:1fr 1fr;height:var(--_k-control-h);padding:2px;border-radius:var(--kd-ctl);background:var(--_k-fill)}
3263
3270
  .knobs-ui .knobs-diff-choice{padding:0 9px;border-radius:max(0px,calc(var(--kd-ctl) - 2px));color:var(--_k-fg-2);font-size:var(--_k-text-meta);
3264
- font-weight:var(--_k-weight-label);white-space:nowrap;cursor:pointer;transition:color calc(var(--_k-hover-out) * 1ms)}
3265
- .knobs-ui .knobs-diff-choice:is(:hover,:focus-visible){color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
3271
+ font-weight:var(--_k-weight-label);white-space:nowrap;cursor:default;transition:background calc(var(--_k-hover-out) * 1ms)}
3272
+ .knobs-ui .knobs-diff-choice:not([aria-checked="true"]):is(:hover,:focus-visible){background:var(--_k-fill-hover);transition-duration:calc(var(--_k-hover-in) * 1ms)}
3266
3273
  /* The diff: one layer per file or layout on show, the old fading under the new. */
3267
3274
  .knobs-diff-body{position:relative;flex:1;min-height:0}
3268
3275
  .knobs-diff-probe{position:absolute;left:0;top:0;width:0;height:var(--kd-line);visibility:hidden;pointer-events:none}
@@ -3358,8 +3365,8 @@ var init_diff_css = __esm(() => {
3358
3365
  .knobs-diff-filter svg{width:12px;height:12px;flex:none}
3359
3366
  .knobs-ui .knobs-diff-filter>input{flex:1;min-width:0;height:100%;font:var(--_k-weight) var(--_k-font-size)/1 var(--_k-font);color:var(--_k-fg);caret-color:var(--_k-accent);background:transparent}
3360
3367
  .knobs-ui .knobs-diff-filter>input::placeholder{color:var(--_k-fg-3)}
3361
- .knobs-ui .knobs-diff-filter>button{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:999px;color:var(--_k-fg-3);cursor:pointer;transition:background .12s,color .12s,opacity .12s}
3362
- .knobs-ui .knobs-diff-filter>button:hover{background:var(--_ku-wash-hi);color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}
3368
+ .knobs-ui .knobs-diff-filter>button{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:999px;color:var(--_k-fg-3);cursor:default;transition:background calc(var(--_k-hover-out) * 1ms),opacity .12s}
3369
+ .knobs-ui .knobs-diff-filter>button:hover{background:var(--_k-fill-hover);transition-duration:calc(var(--_k-hover-in) * 1ms)}
3363
3370
  .knobs-diff-filter>button svg{width:12px;height:12px}
3364
3371
  .knobs-diff-list{position:relative;flex:1;min-height:0}
3365
3372
  .knobs-diff-files .knobs-diff-counts{font-size:var(--_k-text-meta);align-items:center}
@@ -11215,29 +11222,6 @@ var init_compare_strip = __esm(() => {
11215
11222
  DIFF = { w: 32, h: 18 };
11216
11223
  });
11217
11224
 
11218
- // src/ui/player.ts
11219
- function playGlyph(doc) {
11220
- const element = doc.createElement("span");
11221
- element.className = "knobs-player-glyph";
11222
- element.setAttribute("aria-hidden", "true");
11223
- let timer = 0;
11224
- const win = doc.defaultView;
11225
- return {
11226
- element,
11227
- flash(playing) {
11228
- element.innerHTML = playing ? PLAY2 : PAUSE;
11229
- element.setAttribute("data-flash", "");
11230
- win.clearTimeout(timer);
11231
- timer = win.setTimeout(() => element.removeAttribute("data-flash"), FLASH_MS);
11232
- },
11233
- dispose: () => win.clearTimeout(timer)
11234
- };
11235
- }
11236
- var FLASH_MS = 450;
11237
- var init_player = __esm(() => {
11238
- init_timeline();
11239
- });
11240
-
11241
11225
  // src/ui/compare.ts
11242
11226
  function compare(container, options) {
11243
11227
  const doc = container.ownerDocument;
@@ -11356,8 +11340,7 @@ function compare(container, options) {
11356
11340
  grip.setAttribute("aria-hidden", "true");
11357
11341
  handle.append(grip);
11358
11342
  const note = el("div", "knobs-cmp-note");
11359
- const glyph = playGlyph(doc);
11360
- stage.append(canvas, split, seam, labels[0], labels[1], handle, zoomPill, note, glyph.element);
11343
+ stage.append(canvas, split, seam, labels[0], labels[1], handle, zoomPill, note);
11361
11344
  root.append(bar, stage);
11362
11345
  const wantsStrip = options.strip === true;
11363
11346
  let tl = bound;
@@ -11399,7 +11382,6 @@ function compare(container, options) {
11399
11382
  let drag = null;
11400
11383
  const touches = new Map;
11401
11384
  let pinch = null;
11402
- let press = null;
11403
11385
  const zu = new Spring(0, ZOOM.omega, ZOOM.zeta, 0.00001);
11404
11386
  const zv = new Spring(0, ZOOM.omega, ZOOM.zeta, 0.00001);
11405
11387
  const zw = new Spring(1, ZOOM.omega, ZOOM.zeta, 0.00001);
@@ -12228,10 +12210,8 @@ function compare(container, options) {
12228
12210
  root.focus({ preventScroll: true });
12229
12211
  touches.set(e.pointerId, p);
12230
12212
  stage.setPointerCapture(e.pointerId);
12231
- press = touches.size === 1 && mode !== "wipe" && master() ? p : null;
12232
12213
  if (touches.size >= 2) {
12233
12214
  e.preventDefault();
12234
- press = null;
12235
12215
  return startPinch();
12236
12216
  }
12237
12217
  if (mode === "wipe") {
@@ -12259,8 +12239,6 @@ function compare(container, options) {
12259
12239
  const onMove = (e) => {
12260
12240
  const p = local(e);
12261
12241
  pointer = p;
12262
- if (press && Math.hypot(p.x - press.x, p.y - press.y) > 4)
12263
- press = null;
12264
12242
  if (touches.has(e.pointerId))
12265
12243
  touches.set(e.pointerId, p);
12266
12244
  if (pinch && touches.size >= 2) {
@@ -12290,11 +12268,6 @@ function compare(container, options) {
12290
12268
  drawNow();
12291
12269
  };
12292
12270
  const endDrag = (e) => {
12293
- if (press && e.type === "pointerup") {
12294
- api.toggle();
12295
- glyph.flash(isPlaying());
12296
- }
12297
- press = null;
12298
12271
  touches.delete(e.pointerId);
12299
12272
  if (touches.size < 2)
12300
12273
  pinch = null;
@@ -12456,7 +12429,6 @@ function compare(container, options) {
12456
12429
  old.el.replaceWith(s.el);
12457
12430
  old.preview.replaceWith(s.preview);
12458
12431
  sides[index] = s;
12459
- root.toggleAttribute("data-video", sides.some((x) => x.kind === "video"));
12460
12432
  note.textContent = "";
12461
12433
  setLabel(index, labelOf(index));
12462
12434
  if (near)
@@ -12471,7 +12443,6 @@ function compare(container, options) {
12471
12443
  stage.insertBefore(sides[0].preview, labels[0]);
12472
12444
  setLabel(0, labelOf(0));
12473
12445
  setLabel(1, labelOf(1));
12474
- root.toggleAttribute("data-video", sides.some((s) => s.kind === "video"));
12475
12446
  loupeBtn.setAttribute("aria-pressed", String(loupeOn));
12476
12447
  loopBtn.setAttribute("aria-pressed", String(loopNow() !== null));
12477
12448
  const modePill = modeList.length > 1 ? selectionPill(modes, {
@@ -12672,7 +12643,6 @@ function compare(container, options) {
12672
12643
  disposed = true;
12673
12644
  loop.stop();
12674
12645
  modePill?.dispose();
12675
- glyph.dispose();
12676
12646
  win.clearTimeout(releaseTimer);
12677
12647
  ro.disconnect();
12678
12648
  nearIo?.disconnect();
@@ -12713,7 +12683,6 @@ var init_compare = __esm(() => {
12713
12683
  init_icons();
12714
12684
  init_look();
12715
12685
  init_rgba();
12716
- init_player();
12717
12686
  init_selection_pill();
12718
12687
  init_timeline();
12719
12688
  init_timeline_model();
@@ -18244,9 +18213,10 @@ function knownPRs(doc, sections) {
18244
18213
  }
18245
18214
  function findPR(prs, n, repo) {
18246
18215
  const known = prs[n];
18216
+ const href = repo ? `https://github.com/${repo}/pull/${n}` : undefined;
18247
18217
  if (known)
18248
- return known;
18249
- return repo ? { href: `https://github.com/${repo}/pull/${n}` } : null;
18218
+ return known.href || !href ? known : { ...known, href };
18219
+ return href ? { href } : null;
18250
18220
  }
18251
18221
  var isPullRequest = (href) => /\/(?:pull|merge_requests)\/\d+(?:[/?#]|$)/.test(href);
18252
18222
  function withKickers(sections, source, kickers = "pr") {
@@ -19211,7 +19181,7 @@ function mountHistory(host, m, steps) {
19211
19181
  context: () => ({ kind: "history" })
19212
19182
  };
19213
19183
  }
19214
- function mountStack(host, m, steps) {
19184
+ function mountStack(host, m, steps, findPR) {
19215
19185
  const doc = host.ownerDocument;
19216
19186
  const layout = stackLayout(m);
19217
19187
  const root = el2(doc, "div", "knobs-wt-stack");
@@ -19253,7 +19223,10 @@ function mountStack(host, m, steps) {
19253
19223
  const line = el2(doc, "div", "knobs-wt-sline");
19254
19224
  if (it.order)
19255
19225
  line.append(text2(doc, "span", "knobs-wt-sord", it.order));
19256
- line.append(link(doc, "knobs-wt-spr", it.pr, it.href));
19226
+ const known = findPR?.(it.pr.replace(/^#/, "")) ?? null;
19227
+ const pr = link(doc, "knobs-wt-spr", it.pr, it.href ?? known?.href);
19228
+ pr.addEventListener("click", (e) => e.stopPropagation());
19229
+ line.append(pr);
19257
19230
  const target = it.step ? steps?.find(it.step) : null;
19258
19231
  if (target && steps) {
19259
19232
  const title = text2(doc, "button", "knobs-wt-stitle", it.title);
@@ -19288,7 +19261,11 @@ function mountStack(host, m, steps) {
19288
19261
  if (meta.childNodes.length)
19289
19262
  main.append(meta);
19290
19263
  row.append(main);
19291
- if (it.status) {
19264
+ const said = prStatus(it.status);
19265
+ const state = known?.state ?? said.state;
19266
+ if (state)
19267
+ row.append(prStateLabel(doc, state, said.checks, it.status));
19268
+ else if (it.status) {
19292
19269
  const chip = text2(doc, "span", "knobs-wt-chip knobs-wt-sstatus", it.status);
19293
19270
  chip.dataset.tone = it.tone ?? "dim";
19294
19271
  row.append(chip);
@@ -19595,8 +19572,34 @@ var PR_PATHS = {
19595
19572
  pull: "M104,64A32,32,0,1,0,64,95v66a32,32,0,1,0,16,0V95A32.06,32.06,0,0,0,104,64ZM56,64A16,16,0,1,1,72,80,16,16,0,0,1,56,64ZM88,192a16,16,0,1,1-16-16A16,16,0,0,1,88,192Zm120-31V110.63a23.85,23.85,0,0,0-7-17L163.31,56H192a8,8,0,0,0,0-16H144a8,8,0,0,0-8,8V96a8,8,0,0,0,16,0V67.31L189.66,105a8,8,0,0,1,2.34,5.66V161a32,32,0,1,0,16,0Zm-8,47a16,16,0,1,1,16-16A16,16,0,0,1,200,208Z",
19596
19573
  merge: "M208,112a32.05,32.05,0,0,0-30.69,23l-42.21-6a8,8,0,0,1-4.95-2.71L94.43,84.55A32,32,0,1,0,72,87v82a32,32,0,1,0,16,0V101.63l30,35a24,24,0,0,0,14.83,8.14l44,6.28A32,32,0,1,0,208,112ZM64,56A16,16,0,1,1,80,72,16,16,0,0,1,64,56ZM96,200a16,16,0,1,1-16-16A16,16,0,0,1,96,200Zm112-40a16,16,0,1,1,16-16A16,16,0,0,1,208,160Z"
19597
19574
  };
19598
- var prGlyph = (merged) => `<svg class="knobs-icon" viewBox="0 0 256 256" width="12" height="12" fill="currentColor" aria-hidden="true" focusable="false"><path d="${merged ? PR_PATHS.merge : PR_PATHS.pull}"/></svg>`;
19575
+ var DRAFT = "M104,64A32,32,0,1,0,64,95v66a32,32,0,1,0,16,0V95A32.06,32.06,0,0,0,104,64ZM56,64A16,16,0,1,1,72,80,16,16,0,0,1,56,64ZM88,192a16,16,0,1,1-16-16A16,16,0,0,1,88,192ZM232,192a32,32,0,1,1-32-32A32,32,0,0,1,232,192ZM216,192a16,16,0,1,0-16,16A16,16,0,0,0,216,192ZM212,120a12,12,0,1,1-12-12A12,12,0,0,1,212,120ZM212,60a12,12,0,1,1-12-12A12,12,0,0,1,212,60Z";
19576
+ var X = "M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z";
19577
+ var CHECK3 = "M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z";
19578
+ 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>`;
19579
+ var prGlyph = (state) => state === "merged" ? svg12(PR_PATHS.merge) : state === "draft" ? svg12(DRAFT, true) : state === "closed" ? svg12(X) : svg12(PR_PATHS.pull);
19599
19580
  var PR_STATE = { merged: "Merged", open: "Open", draft: "Draft", closed: "Closed" };
19581
+ function prStatus(status) {
19582
+ const words = (status ?? "").trim().toLowerCase();
19583
+ const first = /^[a-z]+/.exec(words)?.[0] ?? "";
19584
+ const state = first in PR_STATE ? first : null;
19585
+ return { state, checks: /\b(green|passing|passed|ci|checks? pass)/.test(words) };
19586
+ }
19587
+ function prStateLabel(doc, state, checks, title) {
19588
+ const s = el2(doc, "span", "knobs-wt-prstate");
19589
+ s.dataset.state = state;
19590
+ s.innerHTML = prGlyph(state);
19591
+ s.append(text2(doc, "span", "", PR_STATE[state]));
19592
+ if (checks) {
19593
+ const c = el2(doc, "span", "knobs-wt-prchecks");
19594
+ c.innerHTML = svg12(CHECK3);
19595
+ c.title = "Checks pass";
19596
+ c.setAttribute("aria-label", "checks pass");
19597
+ s.append(c);
19598
+ }
19599
+ if (title)
19600
+ s.title = title;
19601
+ return s;
19602
+ }
19600
19603
  function prChip(doc, n, pr) {
19601
19604
  const chip = el2(doc, pr.href ? "a" : "span", "knobs-wt-pr");
19602
19605
  if (pr.href) {
@@ -19606,7 +19609,7 @@ function prChip(doc, n, pr) {
19606
19609
  }
19607
19610
  if (pr.state)
19608
19611
  chip.dataset.state = pr.state;
19609
- chip.innerHTML = prGlyph(pr.state === "merged");
19612
+ chip.innerHTML = prGlyph(pr.state);
19610
19613
  chip.append(text2(doc, "span", "", `#${n}`));
19611
19614
  const tip = [`#${n}`, pr.title, pr.state ? PR_STATE[pr.state] : ""].filter(Boolean).join(" · ");
19612
19615
  chip.title = tip;
@@ -20083,12 +20086,21 @@ var walkthroughCss = `
20083
20086
  .knobs-wt-add{color:var(--_k-diff-add)}
20084
20087
  .knobs-wt-del{color:var(--_k-diff-del)}
20085
20088
  .knobs-wt-sstatus{margin-top:calc((var(--kw-line) - 20px) / 2)}
20089
+ /* A PR's state the GitHub way: its glyph and word, open and merged quietly tinted (as PR chips),
20090
+ draft and closed faint; no fill, no dot. A check after it where the checks pass, faint too. One
20091
+ width for every row, so the glyphs and words line up. */
20092
+ .knobs-ui .knobs-wt-prstate{--kw-pr:var(--_k-fg-3);display:inline-flex;align-items:center;gap:4px;min-width:calc(7ch + 32px);height:var(--kw-line);color:var(--kw-pr);
20093
+ font:var(--_k-weight) var(--_k-type-small)/var(--_k-leading-small) var(--_k-font);white-space:nowrap}
20094
+ .knobs-ui .knobs-wt-prstate[data-state="open"]{--kw-pr:color-mix(in oklch,var(--_k-ok) 60%,var(--_k-fg-2))}
20095
+ .knobs-ui .knobs-wt-prstate[data-state="merged"]{--kw-pr:color-mix(in oklch,var(--_k-series-5) 60%,var(--_k-fg-2))}
20096
+ .knobs-wt-prstate svg{flex:none}
20097
+ .knobs-ui .knobs-wt-prchecks{display:inline-flex;margin-left:2px;color:var(--_k-fg-3)}
20086
20098
  .knobs-wt-stack[data-flat] .knobs-wt-srow{grid-template-columns:minmax(0,1fr) auto}
20087
20099
  .knobs-wt-stack[data-flat] .knobs-wt-srow+.knobs-wt-srow{box-shadow:inset 0 1px 0 var(--_k-border)}
20088
20100
  @container knobs-wt-slot (max-width:520px){
20089
20101
  .knobs-wt-srow:not([data-trunk]){grid-template-columns:auto minmax(0,1fr)}
20090
20102
  .knobs-wt-stack[data-flat] .knobs-wt-srow{grid-template-columns:minmax(0,1fr)}
20091
- .knobs-wt-sstatus{grid-column:-2;justify-self:start;margin-top:4px}
20103
+ :is(.knobs-wt-sstatus,.knobs-wt-prstate){grid-column:-2;justify-self:start;margin-top:4px}
20092
20104
  }
20093
20105
 
20094
20106
  /* A table: a real one, its head stuck while it scrolls, both ways; edges fade where there's more
@@ -20734,7 +20746,7 @@ function mediaSlot(container, options) {
20734
20746
  case "history":
20735
20747
  return done(mountHistory(host, m, options.steps));
20736
20748
  case "stack":
20737
- return done(mountStack(host, m, options.steps));
20749
+ return done(mountStack(host, m, options.steps, options.findPR));
20738
20750
  case "table":
20739
20751
  return done(mountTable(host, m, options.steps));
20740
20752
  case "mount": {
@@ -20788,7 +20800,7 @@ function mediaSlot(container, options) {
20788
20800
  }
20789
20801
  };
20790
20802
  }
20791
- var CHECK3 = icon("check", { small: true });
20803
+ var CHECK4 = icon("check", { small: true });
20792
20804
 
20793
20805
  // src/ui/walkthrough.ts
20794
20806
  var el3 = (doc, tag, cls) => {
@@ -20875,7 +20887,7 @@ function tasteTest(container, options) {
20875
20887
  pick.append(key);
20876
20888
  }
20877
20889
  const check = el3(doc, "span", "knobs-wt-check");
20878
- check.innerHTML = CHECK3;
20890
+ check.innerHTML = CHECK4;
20879
20891
  pick.append(check);
20880
20892
  pick.addEventListener("click", () => {
20881
20893
  const next = picked === o.id ? null : o.id;
@@ -22036,7 +22048,7 @@ function walkthrough(container, options) {
22036
22048
  media = el3(doc, "div", "knobs-wt-media");
22037
22049
  media.style.setProperty("--kw-n", String(Math.min(step.media.length, step.columns)));
22038
22050
  for (const m of step.media)
22039
- slots.push(mediaSlot(media, { ...look, media: m, onEvent, steps: stepLinks }));
22051
+ slots.push(mediaSlot(media, { ...look, media: m, onEvent, steps: stepLinks, findPR: findPRIn }));
22040
22052
  card.append(media);
22041
22053
  }
22042
22054
  let taste = null;