@kitlangton/knobs 0.4.0-next.2 → 0.4.0-next.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -12
- package/dist/dev/{chunk-8mabjeyk.js → chunk-0w1xk9jc.js} +162 -1
- package/dist/dev/{chunk-1qzffbpa.js → chunk-76frnh2j.js} +78 -34
- package/dist/dev/{chunk-ayb292zj.js → chunk-rem3qrhz.js} +2 -2
- package/dist/dev/index.js +3 -3
- package/dist/panel.js +240 -34
- package/dist/themes.js +160 -0
- package/dist/types/tokens/schema.d.ts +180 -0
- package/dist/types/ui/compare-css.d.ts +1 -1
- package/dist/types/ui/compare-gpu.d.ts +0 -5
- package/dist/types/ui/compare-model.d.ts +31 -2
- package/dist/types/ui/compare.d.ts +17 -4
- package/dist/types/ui/history-css.d.ts +1 -0
- package/dist/types/ui/history-model.d.ts +236 -0
- package/dist/types/ui/history.d.ts +33 -0
- package/dist/types/ui/icons.d.ts +40 -0
- package/dist/types/ui/index.d.ts +4 -2
- package/dist/types/ui/selection-pill-css.d.ts +1 -0
- package/dist/types/ui/selection-pill.d.ts +103 -0
- package/dist/types/ui/timeline-model.d.ts +23 -0
- package/dist/types/ui/timeline.d.ts +11 -1
- package/dist/types/ui/tree.d.ts +3 -2
- package/dist/types/ui/walkthrough-blocks.d.ts +20 -1
- package/dist/types/ui/walkthrough-model.d.ts +59 -1
- package/dist/types/ui/walkthrough.d.ts +10 -2
- package/dist/ui-react.js +2027 -952
- package/dist/ui-solid.js +2027 -952
- package/dist/ui.js +2047 -952
- package/package.json +1 -1
|
@@ -409,6 +409,24 @@ export declare const TOKENS: readonly [{
|
|
|
409
409
|
readonly css: "var(--k-warn)";
|
|
410
410
|
};
|
|
411
411
|
readonly doc: "Modified lines.";
|
|
412
|
+
}, {
|
|
413
|
+
readonly name: "before";
|
|
414
|
+
readonly tier: "semantic";
|
|
415
|
+
readonly type: "color";
|
|
416
|
+
readonly group: "Data";
|
|
417
|
+
readonly source: {
|
|
418
|
+
readonly css: "var(--k-fg-3)";
|
|
419
|
+
};
|
|
420
|
+
readonly doc: "Before in a comparison, and only Before: its label and side of the seam. Neutral unless a theme gives it a colour.";
|
|
421
|
+
}, {
|
|
422
|
+
readonly name: "after";
|
|
423
|
+
readonly tier: "semantic";
|
|
424
|
+
readonly type: "color";
|
|
425
|
+
readonly group: "Data";
|
|
426
|
+
readonly source: {
|
|
427
|
+
readonly css: "var(--k-fg)";
|
|
428
|
+
};
|
|
429
|
+
readonly doc: "After in a comparison, and only After.";
|
|
412
430
|
}, {
|
|
413
431
|
readonly name: "series-1";
|
|
414
432
|
readonly tier: "semantic";
|
|
@@ -510,6 +528,168 @@ export declare const TOKENS: readonly [{
|
|
|
510
528
|
readonly value: "288px";
|
|
511
529
|
};
|
|
512
530
|
readonly doc: "Panel width.";
|
|
531
|
+
}, {
|
|
532
|
+
readonly name: "space-inside";
|
|
533
|
+
readonly tier: "semantic";
|
|
534
|
+
readonly type: "length";
|
|
535
|
+
readonly group: "Space";
|
|
536
|
+
readonly source: {
|
|
537
|
+
readonly css: "var(--k-unit)";
|
|
538
|
+
};
|
|
539
|
+
readonly doc: "Parts of one thing: an icon and its label, a meta line and its title.";
|
|
540
|
+
}, {
|
|
541
|
+
readonly name: "space-sibling";
|
|
542
|
+
readonly tier: "semantic";
|
|
543
|
+
readonly type: "length";
|
|
544
|
+
readonly group: "Space";
|
|
545
|
+
readonly source: {
|
|
546
|
+
readonly css: "calc(var(--k-unit) * 2)";
|
|
547
|
+
};
|
|
548
|
+
readonly doc: "Siblings in a group: buttons in a row, a list item's lines.";
|
|
549
|
+
}, {
|
|
550
|
+
readonly name: "space-title";
|
|
551
|
+
readonly tier: "semantic";
|
|
552
|
+
readonly type: "length";
|
|
553
|
+
readonly group: "Space";
|
|
554
|
+
readonly source: {
|
|
555
|
+
readonly css: "calc(var(--k-unit) * 3)";
|
|
556
|
+
};
|
|
557
|
+
readonly doc: "A heading and what it heads.";
|
|
558
|
+
}, {
|
|
559
|
+
readonly name: "space-group";
|
|
560
|
+
readonly tier: "semantic";
|
|
561
|
+
readonly type: "length";
|
|
562
|
+
readonly group: "Space";
|
|
563
|
+
readonly source: {
|
|
564
|
+
readonly css: "calc(var(--k-unit) * 12)";
|
|
565
|
+
};
|
|
566
|
+
readonly doc: "Groups inside a section (prose, video, diff), stacked media, feed items (a hairline in the middle).";
|
|
567
|
+
}, {
|
|
568
|
+
readonly name: "space-section";
|
|
569
|
+
readonly tier: "semantic";
|
|
570
|
+
readonly type: "length";
|
|
571
|
+
readonly group: "Space";
|
|
572
|
+
readonly source: {
|
|
573
|
+
readonly css: "calc(var(--k-unit) * 24)";
|
|
574
|
+
};
|
|
575
|
+
readonly doc: "Sections of a page (the intro, the updates, each PR), a hairline in the middle.";
|
|
576
|
+
}, {
|
|
577
|
+
readonly name: "space-column";
|
|
578
|
+
readonly tier: "semantic";
|
|
579
|
+
readonly type: "length";
|
|
580
|
+
readonly group: "Space";
|
|
581
|
+
readonly source: {
|
|
582
|
+
readonly css: "calc(var(--k-unit) * 6)";
|
|
583
|
+
};
|
|
584
|
+
readonly doc: "Columns: a contents rail and the page, media side by side.";
|
|
585
|
+
}, {
|
|
586
|
+
readonly name: "pad-page";
|
|
587
|
+
readonly tier: "semantic";
|
|
588
|
+
readonly type: "length";
|
|
589
|
+
readonly group: "Space";
|
|
590
|
+
readonly source: {
|
|
591
|
+
readonly css: "calc(var(--k-unit) * 6)";
|
|
592
|
+
};
|
|
593
|
+
readonly doc: "A page's edge; 16px under 640px wide.";
|
|
594
|
+
}, {
|
|
595
|
+
readonly name: "pad-card";
|
|
596
|
+
readonly tier: "semantic";
|
|
597
|
+
readonly type: "length";
|
|
598
|
+
readonly group: "Space";
|
|
599
|
+
readonly source: {
|
|
600
|
+
readonly css: "calc(var(--k-unit) * 3)";
|
|
601
|
+
};
|
|
602
|
+
readonly doc: "Inside a card or popover.";
|
|
603
|
+
}, {
|
|
604
|
+
readonly name: "type-heading";
|
|
605
|
+
readonly tier: "semantic";
|
|
606
|
+
readonly type: "length";
|
|
607
|
+
readonly group: "Type";
|
|
608
|
+
readonly source: {
|
|
609
|
+
readonly value: "20px";
|
|
610
|
+
};
|
|
611
|
+
readonly doc: "Section headings (a PR's title, under its kicker), weight 500.";
|
|
612
|
+
}, {
|
|
613
|
+
readonly name: "leading-heading";
|
|
614
|
+
readonly tier: "semantic";
|
|
615
|
+
readonly type: "length";
|
|
616
|
+
readonly group: "Type";
|
|
617
|
+
readonly source: {
|
|
618
|
+
readonly value: "28px";
|
|
619
|
+
};
|
|
620
|
+
readonly doc: "Line height for type-heading.";
|
|
621
|
+
}, {
|
|
622
|
+
readonly name: "type-title";
|
|
623
|
+
readonly tier: "semantic";
|
|
624
|
+
readonly type: "length";
|
|
625
|
+
readonly group: "Type";
|
|
626
|
+
readonly source: {
|
|
627
|
+
readonly value: "16px";
|
|
628
|
+
};
|
|
629
|
+
readonly doc: "A page's title, weight 500.";
|
|
630
|
+
}, {
|
|
631
|
+
readonly name: "leading-title";
|
|
632
|
+
readonly tier: "semantic";
|
|
633
|
+
readonly type: "length";
|
|
634
|
+
readonly group: "Type";
|
|
635
|
+
readonly source: {
|
|
636
|
+
readonly value: "24px";
|
|
637
|
+
};
|
|
638
|
+
readonly doc: "Line height for type-title.";
|
|
639
|
+
}, {
|
|
640
|
+
readonly name: "type-body";
|
|
641
|
+
readonly tier: "semantic";
|
|
642
|
+
readonly type: "length";
|
|
643
|
+
readonly group: "Type";
|
|
644
|
+
readonly source: {
|
|
645
|
+
readonly value: "13px";
|
|
646
|
+
};
|
|
647
|
+
readonly doc: "Prose, questions, list rows, step titles; 400, or 500 for emphasis.";
|
|
648
|
+
}, {
|
|
649
|
+
readonly name: "leading-body";
|
|
650
|
+
readonly tier: "semantic";
|
|
651
|
+
readonly type: "length";
|
|
652
|
+
readonly group: "Type";
|
|
653
|
+
readonly source: {
|
|
654
|
+
readonly value: "20px";
|
|
655
|
+
};
|
|
656
|
+
readonly doc: "Line height for type-body.";
|
|
657
|
+
}, {
|
|
658
|
+
readonly name: "type-label";
|
|
659
|
+
readonly tier: "semantic";
|
|
660
|
+
readonly type: "length";
|
|
661
|
+
readonly group: "Type";
|
|
662
|
+
readonly source: {
|
|
663
|
+
readonly value: "12px";
|
|
664
|
+
};
|
|
665
|
+
readonly doc: "Labels over media, weight 500; mono inside body text.";
|
|
666
|
+
}, {
|
|
667
|
+
readonly name: "leading-label";
|
|
668
|
+
readonly tier: "semantic";
|
|
669
|
+
readonly type: "length";
|
|
670
|
+
readonly group: "Type";
|
|
671
|
+
readonly source: {
|
|
672
|
+
readonly value: "16px";
|
|
673
|
+
};
|
|
674
|
+
readonly doc: "Line height for type-label.";
|
|
675
|
+
}, {
|
|
676
|
+
readonly name: "type-small";
|
|
677
|
+
readonly tier: "semantic";
|
|
678
|
+
readonly type: "length";
|
|
679
|
+
readonly group: "Type";
|
|
680
|
+
readonly source: {
|
|
681
|
+
readonly value: "11px";
|
|
682
|
+
};
|
|
683
|
+
readonly doc: "Meta, controls, keys, timestamps; mono inside labels.";
|
|
684
|
+
}, {
|
|
685
|
+
readonly name: "leading-small";
|
|
686
|
+
readonly tier: "semantic";
|
|
687
|
+
readonly type: "length";
|
|
688
|
+
readonly group: "Type";
|
|
689
|
+
readonly source: {
|
|
690
|
+
readonly value: "16px";
|
|
691
|
+
};
|
|
692
|
+
readonly doc: "Line height for type-small.";
|
|
513
693
|
}, {
|
|
514
694
|
readonly name: "font-size";
|
|
515
695
|
readonly tier: "semantic";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const compareCss = "\n:where(.knobs-compare,.knobs-compare *,.knobs-cmp-strip,.knobs-cmp-strip *){box-sizing:border-box}\n.knobs-ui.knobs-compare{--_ku-cmp-diff:var(--k-compare-diff,var(--_k-ok));--_ku-cmp-line:var(--_k-compare-divider);\n --_ku-cmp-stage:var(--_k-compare-stage);\n display:flex;flex-direction:column;gap:6px;width:100%;height:100%;min-height:160px;outline:none;user-select:none;-webkit-user-select:none;\n font:var(--_k-weight) var(--_k-font-size)/1.35 var(--_k-font);color:var(--_k-fg);letter-spacing:normal;text-align:left}\n:where(.knobs-ui) :is(.knobs-cmp-mode,.knobs-cmp-toggle){all:revert;box-sizing:border-box;font:inherit;color:inherit;letter-spacing:inherit;background:transparent;border:0;margin:0;padding:0;outline:none;box-shadow:none}\n.knobs-cmp-bar{flex:none;display:flex;align-items:center;gap:
|
|
1
|
+
export declare const compareCss = "\n:where(.knobs-compare,.knobs-compare *,.knobs-cmp-strip,.knobs-cmp-strip *){box-sizing:border-box}\n.knobs-ui.knobs-compare{--_ku-cmp-diff:var(--k-compare-diff,var(--_k-ok));--_ku-cmp-line:var(--_k-compare-divider);\n --_ku-cmp-stage:var(--_k-compare-stage);\n display:flex;flex-direction:column;gap:6px;width:100%;height:100%;min-height:160px;outline:none;user-select:none;-webkit-user-select:none;\n font:var(--_k-weight) var(--_k-font-size)/1.35 var(--_k-font);color:var(--_k-fg);letter-spacing:normal;text-align:left}\n:where(.knobs-ui) :is(.knobs-cmp-mode,.knobs-cmp-toggle){all:revert;box-sizing:border-box;font:inherit;color:inherit;letter-spacing:inherit;background:transparent;border:0;margin:0;padding:0;outline:none;box-shadow:none}\n.knobs-cmp-bar{flex:none;display:flex;align-items:center;gap:var(--_k-gap);height:var(--_k-control-h)}\n/* The modes: one fill, the selection pill on the one picked; a mode isn't brighter for being\n picked: the pill's bright copy is. */\n.knobs-cmp-modes{position:relative;display:inline-flex;height:var(--_k-control-h);border-radius:var(--_k-radius);background:var(--_k-fill)}\n.knobs-cmp-modes[hidden]{display:none}\n.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;\n transition:color calc(var(--_k-hover-out) * 1ms)}\n.knobs-ui .knobs-cmp-mode:is(:hover,:focus-visible){color:var(--_k-fg)}\n/* Toggles: icon buttons, the accent's wash when on. */\n.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;\n transition:color calc(var(--_k-hover-out) * 1ms),background calc(var(--_k-hover-out) * 1ms)}\n.knobs-ui .knobs-cmp-toggle:is(:hover,:focus-visible){color:var(--_k-fg);background:var(--_ku-wash)}\n.knobs-ui .knobs-cmp-toggle[aria-pressed=\"true\"]{color:var(--_k-accent-fg);background:var(--_k-accent-soft)}\n.knobs-cmp-toggle svg{display:block}\n.knobs-cmp-fill{flex:1}\n.knobs-cmp-toggle[hidden]{display:none}\n/* One surface: on a card (a walkthrough's step, a split's pane, anything .knobs-surface) or marked\n data-nested, the stage drops its fill and the sides sit on that card. */\n:where(.knobs-surface,.knobs-pane:not([data-nested])>.knobs-pane-clip) .knobs-ui.knobs-compare,.knobs-ui.knobs-compare[data-nested]{--_ku-cmp-stage:var(--k-compare-stage,transparent)}\n/* In a page's flow (a container with no height), it takes the height its sides need. */\n.knobs-ui.knobs-compare[data-auto]{height:auto}\n.knobs-compare[data-auto] .knobs-cmp-stage{flex:none;min-height:0}\n.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}\n.knobs-compare[data-frame=\"flat\"] .knobs-cmp-stage{border-radius:0}\n.knobs-compare[data-mode=\"wipe\"] .knobs-cmp-stage{cursor:ew-resize}\n/* Unzoomed, a finger still scrolls the page up and down; zoomed, the stage takes every gesture. */\n.knobs-compare[data-zoomed] .knobs-cmp-stage{cursor:grab;touch-action:none}\n.knobs-compare[data-panning],.knobs-compare[data-panning] *{cursor:grabbing!important}\n/* Wiping, the grip stands in for the cursor. */\n.knobs-compare[data-dragging],.knobs-compare[data-dragging] *{cursor:none!important}\n.knobs-cmp-sizer{position:absolute;left:0;right:0;top:0;height:0;visibility:hidden;pointer-events:none}\n.knobs-cmp-canvas{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none}\n.knobs-cmp-media{position:absolute;left:0;top:0;display:block;max-width:none;margin:0;padding:0;border:0;object-fit:fill;transform-origin:0 0;pointer-events:none;will-change:transform}\n.knobs-compare[data-renderer=\"gpu\"] .knobs-cmp-media{opacity:0}\n/* Glass: what's behind shows through, blurred, on the knobs tokens. */\n.knobs-cmp-label,.knobs-cmp-zoom{background:color-mix(in srgb,var(--_k-bg) 52%,transparent);-webkit-backdrop-filter:blur(14px) saturate(1.6);backdrop-filter:blur(14px) saturate(1.6);\n box-shadow:inset 0 0 0 .5px var(--_k-border),inset 0 1px 0 var(--_k-rim),0 2px 10px rgba(0,0,0,calc(.18 * var(--_k-shadow-k,1)))}\n/* \u2717 Before \u00B7 \u2713 After: Before quieter (fg-3), After in full text. Neutral: no colour of its own. */\n.knobs-cmp-label{position:absolute;left:0;top:0;display:flex;align-items:center;gap:4px;padding:1px 7px 1px 5px;border-radius:999px;font-size:var(--_k-text-meta);line-height:16px;font-weight:var(--_k-weight-label);\n white-space:nowrap;pointer-events:none;will-change:transform}\n.knobs-cmp-label[data-side=\"before\"]{color:var(--_k-fg-3)}\n.knobs-cmp-label[data-side=\"after\"]{color:var(--_k-fg)}\n.knobs-cmp-mark svg{display:block;width:12px;height:12px}\n/* The wipe: a hairline down the line with a raised grip at the pointer's height; a wider strip\n takes the hand and the keys (a slider). It fades with the mode; wiping, line and grip brighten. */\n.knobs-cmp-handle{cursor:ew-resize;position:absolute;left:-12px;top:0;width:24px;height:0;opacity:0;pointer-events:none;outline:none;will-change:transform;transition:opacity .18s cubic-bezier(.4,0,1,1)}\n.knobs-cmp-handle[data-on]{opacity:1;pointer-events:auto;transition:opacity .22s cubic-bezier(.2,.8,.2,1)}\n.knobs-cmp-handle::before{content:\"\";position:absolute;top:0;bottom:0;left:11.5px;width:1px;background:color-mix(in srgb,var(--_ku-cmp-line) 72%,transparent);box-shadow:0 0 0 .5px rgba(0,0,0,.28),0 0 6px rgba(0,0,0,.25)}\n.knobs-compare[data-dragging] .knobs-cmp-handle::before{background:var(--_ku-cmp-line)}\n.knobs-cmp-grip{position:absolute;top:0;left:4px;width:16px;height:28px;border-radius:min(8px,calc(var(--_k-radius) * 999));background:var(--_k-bg-overlay);\n box-shadow:var(--_k-shadow-overlay),inset 0 0 0 1px var(--_k-border),inset 0 1px 0 var(--_k-rim);will-change:transform;transition:box-shadow calc(var(--_k-hover-out) * 1ms)}\n.knobs-cmp-grip::before,.knobs-cmp-grip::after{content:\"\";position:absolute;top:10px;width:1px;height:8px;background:var(--_k-fg-3);transition:background calc(var(--_k-hover-out) * 1ms)}\n.knobs-cmp-grip::before{left:6px}\n.knobs-cmp-grip::after{left:9px}\n.knobs-compare[data-dragging] .knobs-cmp-grip{box-shadow:var(--_k-shadow-overlay),inset 0 0 0 1px var(--_k-border-strong),inset 0 1px 0 var(--_k-rim)}\n.knobs-compare[data-dragging] .knobs-cmp-grip::before,.knobs-compare[data-dragging] .knobs-cmp-grip::after{background:var(--_k-fg)}\n.knobs-cmp-handle:focus-visible .knobs-cmp-grip,.knobs-compare[data-keyboard] .knobs-cmp-handle:focus .knobs-cmp-grip{box-shadow:var(--_k-shadow-overlay),0 0 0 2px var(--_k-accent)}\n/* The split's hairline, down the middle side by side. */\n.knobs-cmp-split{position:absolute;left:0;top:0;width:1px;height:0;margin-left:-.5px;background:color-mix(in srgb,var(--_k-bg) 72%,transparent);pointer-events:none;will-change:transform,opacity}\n/* Zoomed: how far, and a way back, in the stage's corner; it fades in and out, nothing shifts. */\n.knobs-cmp-zoom{position:absolute;right:8px;bottom:8px;display:flex;align-items:center;gap:4px;height:26px;padding:0 3px 0 10px;border-radius:999px;\n color:var(--_k-fg);font-size:var(--_k-text-meta);font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";white-space:nowrap;\n opacity:0;scale:.96;pointer-events:none;transition:opacity .16s cubic-bezier(.4,0,1,1),scale .16s cubic-bezier(.4,0,1,1)}\n.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)}\n: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;\n 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}\n: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)}\n.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}\n.knobs-cmp-note:empty{display:none}\n.knobs-cmp-strip-host{flex:none;height:60px;min-height:0}\n.knobs-cmp-timeline-host{flex:none;min-height:0}\n/* The frame strip: Before over After per column, a difference bar under them, a dot where it peaks. */\n.knobs-ui.knobs-cmp-strip{--_ku-cmp-diff:var(--k-compare-diff,var(--_k-ok));position:relative;width:100%;height:100%;min-height:40px;overflow:hidden;cursor:col-resize;touch-action:none;\n font:var(--_k-weight) var(--_k-font-size)/1.35 var(--_k-font);color:var(--_k-fg)}\n.knobs-cmp-strip>canvas{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none}\n@media(prefers-reduced-motion:reduce){:is(.knobs-compare,.knobs-cmp-strip) :where(*,::before,::after){transition-duration:.12s!important}}\n";
|
|
@@ -3,7 +3,6 @@ export declare const MODE_INDEX: {
|
|
|
3
3
|
readonly side: 0;
|
|
4
4
|
readonly wipe: 1;
|
|
5
5
|
readonly difference: 2;
|
|
6
|
-
readonly flicker: 3;
|
|
7
6
|
};
|
|
8
7
|
export interface CompareDraw {
|
|
9
8
|
/** Device px: where each side's picture lands (zoomed, it's larger than its clip). */
|
|
@@ -22,10 +21,6 @@ export interface CompareDraw {
|
|
|
22
21
|
readonly to: number;
|
|
23
22
|
/** 0 shows `from`, 1 shows `to`. */
|
|
24
23
|
readonly mix: number;
|
|
25
|
-
/** Before in After's place (X held). */
|
|
26
|
-
readonly flick: boolean;
|
|
27
|
-
/** Flicker mode's phase: Before showing. */
|
|
28
|
-
readonly phase: boolean;
|
|
29
24
|
readonly threshold: number;
|
|
30
25
|
readonly gain: number;
|
|
31
26
|
readonly accent: readonly [number, number, number];
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import type { TimeView } from "./timeline-model.js";
|
|
2
|
-
|
|
2
|
+
/** Side by side, a wipe, or (opt in) a difference. */
|
|
3
|
+
export type CompareMode = "side" | "wipe" | "difference";
|
|
4
|
+
/**
|
|
5
|
+
* The modes a viewer offers (`["side", "wipe"]` unless given; `difference` only when asked for),
|
|
6
|
+
* in their order, and the mode it starts in (the first offered when the asked one isn't).
|
|
7
|
+
*/
|
|
8
|
+
export declare function compareModes(modes?: readonly CompareMode[], mode?: CompareMode): {
|
|
9
|
+
readonly modes: readonly CompareMode[];
|
|
10
|
+
readonly mode: CompareMode;
|
|
11
|
+
};
|
|
12
|
+
/** The mode a key picks among those offered: a digit by position, or S, W, D. */
|
|
13
|
+
export declare function modeForKey(key: string, modes: readonly CompareMode[]): CompareMode | null;
|
|
14
|
+
/** A mode's letter key (S, W, D). */
|
|
15
|
+
export declare const modeKey: (mode: CompareMode) => string;
|
|
3
16
|
export type MediaKind = "video" | "image";
|
|
4
17
|
/** A source's kind: as given, else by its extension (a video's, or an image). */
|
|
5
18
|
export declare function mediaKind(src: string, kind?: MediaKind): MediaKind;
|
|
@@ -76,7 +89,7 @@ export interface CompareLayout {
|
|
|
76
89
|
readonly after: Rect;
|
|
77
90
|
readonly split: number;
|
|
78
91
|
};
|
|
79
|
-
/** Overlaid (wipe, difference
|
|
92
|
+
/** Overlaid (wipe, difference): both in the same place. */
|
|
80
93
|
readonly overlay: {
|
|
81
94
|
readonly before: Rect;
|
|
82
95
|
readonly after: Rect;
|
|
@@ -134,6 +147,22 @@ export declare function wipeGrab(x: number, rect: Rect, wipe: number, slop?: num
|
|
|
134
147
|
onLine: boolean;
|
|
135
148
|
offset: number;
|
|
136
149
|
};
|
|
150
|
+
/**
|
|
151
|
+
* Wiping, Before and After ride either side of the seam at `x`, `gap` from it, each fading out as
|
|
152
|
+
* its side runs out of room (`left`/`right`: the wipe's edges; `wb`/`wa`: the labels' widths).
|
|
153
|
+
*/
|
|
154
|
+
export declare function seamLabels(x: number, left: number, right: number, wb: number, wa: number, gap?: number): {
|
|
155
|
+
before: {
|
|
156
|
+
x: number;
|
|
157
|
+
opacity: number;
|
|
158
|
+
};
|
|
159
|
+
after: {
|
|
160
|
+
x: number;
|
|
161
|
+
opacity: number;
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
/** Where the wipe's grip sits for a pointer at `y`: its top, kept `margin` inside the line. */
|
|
165
|
+
export declare function gripTop(y: number, rect: Rect, grip: number, margin?: number): number;
|
|
137
166
|
/** The wipe moved by a key: 1% a press, 10% with Shift. */
|
|
138
167
|
export declare const wipeKey: (wipe: number, dir: -1 | 1, big?: boolean) => number;
|
|
139
168
|
export interface StripColumn {
|
|
@@ -29,8 +29,13 @@ export interface CompareSource {
|
|
|
29
29
|
export interface CompareOptions extends UiLook {
|
|
30
30
|
readonly before: CompareSource;
|
|
31
31
|
readonly after: CompareSource;
|
|
32
|
-
/** `"side"
|
|
32
|
+
/** The first of `modes` (`"side"`, side by side) by default. */
|
|
33
33
|
readonly mode?: CompareMode;
|
|
34
|
+
/**
|
|
35
|
+
* The modes it offers, in the mode bar's order and on `1`–`3`: `["side", "wipe"]` by default.
|
|
36
|
+
* Add `"difference"` to offer a difference. One mode hides the bar.
|
|
37
|
+
*/
|
|
38
|
+
readonly modes?: readonly CompareMode[];
|
|
34
39
|
/** Where the wipe's line sits, 0 (all After) to 1 (all Before); 0.5. */
|
|
35
40
|
readonly wipe?: number;
|
|
36
41
|
/** Frames per second (the bound timeline's, else 30): steps, and where paused videos land. */
|
|
@@ -41,8 +46,8 @@ export interface CompareOptions extends UiLook {
|
|
|
41
46
|
readonly threshold?: number;
|
|
42
47
|
/**
|
|
43
48
|
* The clock: an existing `timeline()` (its time drives both sides; while playing it follows
|
|
44
|
-
* After), or `true` for one of its own under the viewer
|
|
45
|
-
* still work.
|
|
49
|
+
* After), or `true` for a slim one of its own under the viewer (the `"lane"` variant: its
|
|
50
|
+
* markers are chapter ticks). Without one, `seek`, `play` and keys still work.
|
|
46
51
|
*/
|
|
47
52
|
readonly timeline?: Timeline | boolean;
|
|
48
53
|
/** The frame strip over the timeline (off; one timeline is made if there's none). */
|
|
@@ -68,6 +73,8 @@ export interface CompareOptions extends UiLook {
|
|
|
68
73
|
export interface Compare {
|
|
69
74
|
readonly element: HTMLElement;
|
|
70
75
|
readonly mode: CompareMode;
|
|
76
|
+
/** The modes it offers. */
|
|
77
|
+
readonly modes: readonly CompareMode[];
|
|
71
78
|
readonly wipe: number;
|
|
72
79
|
readonly loupe: boolean;
|
|
73
80
|
/** Seconds. */
|
|
@@ -85,7 +92,13 @@ export interface Compare {
|
|
|
85
92
|
readonly loop: CompareLoop | null;
|
|
86
93
|
/** New sources, mode, wipe, loupe, threshold or fps. A new mode glides in. */
|
|
87
94
|
update(next: Partial<Pick<CompareOptions, "before" | "after" | "mode" | "wipe" | "loupe" | "threshold" | "fps">>): void;
|
|
88
|
-
|
|
95
|
+
/**
|
|
96
|
+
* `glide`: a jump the hand didn't make (a chapter's click): the timeline's playhead catches up
|
|
97
|
+
* from where it is while the videos land at once.
|
|
98
|
+
*/
|
|
99
|
+
seek(time: number, options?: {
|
|
100
|
+
readonly glide?: boolean;
|
|
101
|
+
}): void;
|
|
89
102
|
/** Moves by `seconds`, the timeline's playhead gliding there (playing carries on). */
|
|
90
103
|
seekBy(seconds: number): void;
|
|
91
104
|
play(): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const historyCss = "\n:where(.knobs-history,.knobs-history *){box-sizing:border-box}\n.knobs-ui.knobs-history{--hy-gutter:128px;--hy-lane:28px;--hy-bar:18px;--hy-axis:20px;--hy-bandrow:0px;--hy-c:var(--_k-fg-3);\n --hy-fade:linear-gradient(to right,#000 calc(100% - 1.5em),transparent);\n position:relative;display:flex;flex-direction:column;width:100%;min-width:0;outline:none;color:var(--_k-fg);\n font:var(--_k-weight) var(--_k-type-body)/var(--_k-leading-body) var(--_k-font);font-variant-numeric:tabular-nums;font-feature-settings:\"tnum\";\n text-align:left;letter-spacing:normal;-webkit-font-smoothing:antialiased;user-select:none;-webkit-user-select:none}\n.knobs-history[data-bands]{--hy-bandrow:var(--_k-leading-small)}\n.knobs-history[data-narrow]{--hy-gutter:0px}\n.knobs-history [hidden]{display:none!important}\n:where(.knobs-ui) :is(.knobs-hy-ev,.knobs-hy-span,.knobs-hy-jump){all:revert;box-sizing:border-box;font:inherit;color:inherit;letter-spacing:inherit;\n background:transparent;border:0;border-radius:0;margin:0;padding:0;min-width:0;box-shadow:none;outline:none}\n.knobs-history [data-tone=\"good\"]{--hy-c:var(--_k-ok)}\n.knobs-history [data-tone=\"warn\"]{--hy-c:var(--_k-warn)}\n.knobs-history [data-tone=\"bad\"]{--hy-c:var(--_k-danger)}\n.knobs-history [data-tone=\"info\"]{--hy-c:var(--_k-info)}\n.knobs-history [data-tone=\"dim\"]{--hy-c:var(--_k-fg-3)}\n\n/* \u2014\u2014\u2014 the chart: lane labels beside the plot (above each lane where it's narrow) \u2014\u2014\u2014 */\n.knobs-hy-chart{position:relative;display:grid;grid-template-columns:var(--hy-gutter) minmax(0,1fr)}\n.knobs-hy-axis{position:relative;grid-column:2;grid-row:1;height:calc(var(--hy-bandrow) + var(--hy-axis))}\n:is(.knobs-hy-tick,.knobs-hy-now,.knobs-hy-blabel){position:absolute;white-space:nowrap;font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3)}\n.knobs-hy-tick{bottom:2px;padding-left:4px}\n.knobs-hy-now{bottom:2px;color:var(--_k-accent-fg);font-weight:var(--_k-weight-label)}\n.knobs-hy-blabel{top:0;padding-left:4px;overflow:hidden;color:var(--_k-fg-2)}\n.knobs-hy-label{grid-column:1;align-self:center;min-width:0;margin-right:var(--_k-space-title);overflow:hidden;white-space:nowrap;\n font-size:var(--_k-type-label);line-height:var(--_k-leading-label);color:var(--_k-fg-2)}\n.knobs-history[data-narrow] .knobs-hy-label{grid-column:2;align-self:end;margin:0;font-size:var(--_k-type-small);line-height:var(--_k-leading-small)}\n:is(.knobs-hy-label,.knobs-hy-blabel)[data-overflow]{-webkit-mask-image:var(--hy-fade);mask-image:var(--hy-fade)}\n.knobs-hy-track{position:relative;grid-column:2;height:var(--hy-lane)}\n.knobs-hy-layer{position:absolute;inset:0;grid-column:2;grid-row:1/-1;pointer-events:none}\n\n/* Bands and month lines behind the lanes; today, the selection and the pointer's line over them. */\n.knobs-hy-band{position:absolute;top:0;bottom:0;background:color-mix(in oklch,var(--_k-fg) 3.5%,transparent)}\n: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}\n.knobs-hy-grid{background:var(--_k-border-subtle)}\n.knobs-hy-today{top:calc(var(--hy-bandrow) + var(--hy-axis) - 4px);background:var(--_k-accent)}\n.knobs-hy-hover{background:color-mix(in oklch,var(--_k-fg) 32%,transparent);opacity:0;transition:opacity calc(var(--_k-hover-out) * 1ms)}\n.knobs-hy-hover[data-on]{opacity:1;transition-duration:calc(var(--_k-hover-in) * 1ms)}\n.knobs-hy-sel{background:var(--_k-fg-2);opacity:0}\n.knobs-hy-sel[data-on]{opacity:.6}\n\n/* Spans: a soft bar in their state's tone, inferred ones hatched; the tag at the end. */\n.knobs-hy-span{position:absolute;top:calc((var(--hy-lane) - var(--hy-bar)) / 2);height:var(--hy-bar);border-radius:var(--_k-radius-sm);cursor:pointer;\n background:color-mix(in oklch,var(--hy-c) 24%,transparent);transition:background calc(var(--_k-hover-out) * 1ms),box-shadow calc(var(--_k-hover-out) * 1ms)}\n.knobs-hy-span[data-inferred]{background:repeating-linear-gradient(135deg,color-mix(in oklch,var(--hy-c) 34%,transparent) 0 2px,color-mix(in oklch,var(--hy-c) 9%,transparent) 2px 6px)}\n.knobs-hy-span[data-hover]{background-color:color-mix(in oklch,var(--hy-c) 34%,transparent);transition-duration:calc(var(--_k-hover-in) * 1ms)}\n.knobs-hy-span[data-selected]{box-shadow:inset 0 0 0 1px var(--hy-c)}\n.knobs-hy-span[data-clip-start]{border-top-left-radius:0;border-bottom-left-radius:0;-webkit-mask-image:linear-gradient(to right,transparent,#000 16px);mask-image:linear-gradient(to right,transparent,#000 16px)}\n.knobs-hy-span[data-clip-end]{border-top-right-radius:0;border-bottom-right-radius:0;-webkit-mask-image:linear-gradient(to left,transparent,#000 16px);mask-image:linear-gradient(to left,transparent,#000 16px)}\n.knobs-hy-tag{position:absolute;top:0;right:6px;line-height:var(--hy-bar);font-size:var(--_k-type-small);font-weight:var(--_k-weight-label);white-space:nowrap;pointer-events:none;\n color:color-mix(in oklch,var(--hy-c) 55%,var(--_k-fg))}\n.knobs-hy-tag[data-place=\"after\"]{right:auto;left:calc(100% + 6px)}\n.knobs-hy-tag[data-place=\"none\"]{visibility:hidden}\n\n/* Event marks (their kind named in the tooltip): a dot for a commit, a diamond for a release, a cross for a deletion, a square for a\n channel's switch, a triangle for a finding; inferred ones hollow. */\n.knobs-hy-ev{position:absolute;top:0;width:16px;height:var(--hy-lane);margin-left:-8px;cursor:pointer;--hy-c:var(--_k-fg-2);--hy-s:9px}\n.knobs-hy-ev::before{content:\"\";position:absolute;left:50%;top:50%;width:var(--hy-s);height:var(--hy-s);margin:calc(var(--hy-s) / -2) 0 0 calc(var(--hy-s) / -2);\n border-radius:50%;background:var(--hy-c);transition:scale calc(var(--_k-motion-snappy-ms) * 1ms) var(--_k-motion-snappy)}\n.knobs-hy-ev[data-range]{width:calc(var(--hy-w) + 16px)}\n.knobs-hy-ev[data-range]::before{left:calc(8px - var(--hy-s) / 2);right:calc(8px - var(--hy-s) / 2);width:auto;margin-left:0;border-radius:var(--_k-round)}\n.knobs-hy-ev[data-type=\"release\"]{--hy-s:8px}\n.knobs-hy-ev[data-type=\"release\"]::before{border-radius:1.5px;rotate:45deg}\n.knobs-hy-ev[data-type=\"channel\"]{--hy-s:8px}\n.knobs-hy-ev[data-type=\"channel\"]::before{border-radius:2px}\n.knobs-hy-ev[data-type=\"audit\"]{--hy-s:10px}\n.knobs-hy-ev[data-type=\"audit\"]::before{border-radius:0;clip-path:polygon(50% 4%,100% 92%,0 92%)}\n.knobs-hy-ev[data-type=\"deletion\"]::before{border-radius:0;rotate:45deg;\n background:linear-gradient(var(--hy-c),var(--hy-c)) center/100% 1.75px no-repeat,linear-gradient(var(--hy-c),var(--hy-c)) center/1.75px 100% no-repeat}\n.knobs-hy-ev[data-inferred]:not([data-type=\"audit\"],[data-type=\"deletion\"])::before{background:transparent;box-shadow:inset 0 0 0 1.5px var(--hy-c)}\n.knobs-hy-ev[data-inferred]:is([data-type=\"audit\"],[data-type=\"deletion\"])::before{opacity:.5}\n.knobs-hy-ev[data-hover]::before{scale:1.25}\n.knobs-hy-ev[data-selected]::before{scale:1.3}\n.knobs-hy-ev[data-selected]::after{content:\"\";position:absolute;left:50%;top:50%;width:calc(var(--hy-s) + 9px);height:calc(var(--hy-s) + 9px);\n margin:calc((var(--hy-s) + 9px) / -2) 0 0 calc((var(--hy-s) + 9px) / -2);border-radius:50%;box-shadow:0 0 0 1.5px var(--hy-c)}\n.knobs-hy-ev[data-range][data-selected]::after{left:3px;right:3px;width:auto;margin-left:0;border-radius:var(--_k-round)}\n.knobs-history:focus-visible .knobs-hy-ev[data-selected]::after{box-shadow:0 0 0 2px var(--_k-focus)}\n.knobs-history:focus-visible .knobs-hy-span[data-selected]{box-shadow:inset 0 0 0 2px var(--_k-focus)}\n\n/* \u2014\u2014\u2014 legend: only what the chart can't say itself \u2014\u2014\u2014 */\n.knobs-hy-legend{display:flex;flex-wrap:wrap;gap:var(--_k-space-inside) var(--_k-space-title);margin-top:var(--_k-space-sibling);padding-left:var(--hy-gutter);\n font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3)}\n.knobs-hy-key{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}\n.knobs-hy-swatch{flex:none;width:14px;height:8px;border-radius:2px;background:color-mix(in oklch,var(--hy-c) 34%,transparent)}\n.knobs-hy-swatch[data-inferred]{background:repeating-linear-gradient(135deg,color-mix(in oklch,var(--hy-c) 60%,transparent) 0 1.5px,transparent 1.5px 4px)}\n\n/* \u2014\u2014\u2014 the detail: one area under the chart, its height on a spring \u2014\u2014\u2014 */\n.knobs-hy-detail{position:relative;overflow:hidden;user-select:text;-webkit-user-select:text}\n.knobs-hy-detail[data-moving]{-webkit-mask-image:linear-gradient(to bottom,#000 calc(100% - 16px),transparent);mask-image:linear-gradient(to bottom,#000 calc(100% - 16px),transparent)}\n.knobs-hy-dc{padding:var(--_k-space-title) 0 0 var(--hy-gutter);transition:opacity calc(var(--_k-motion-snappy-ms) * 1ms) var(--_k-motion-snappy)}\n.knobs-hy-dc:is([data-enter],[data-leave]){opacity:0}\n.knobs-hy-dc[data-leave]{position:absolute;left:0;right:0;top:0;transition-duration:calc(var(--_k-motion-snappy-ms) * .8ms)}\n.knobs-hy-dmeta{display:flex;flex-wrap:wrap;gap:0 var(--_k-space-sibling);font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3)}\n.knobs-hy-dmeta>[data-tone]{color:var(--hy-c)}\n.knobs-hy-dtitle{margin-top:var(--_k-space-inside);font-weight:var(--_k-weight-title);color:var(--_k-fg);text-wrap:balance}\n.knobs-hy-dtext{margin:var(--_k-space-inside) 0 0;max-width:68ch;color:var(--_k-fg-2);text-wrap:pretty}\n.knobs-hy-dtext code{font:calc(1em - 1px) var(--_k-mono);padding:1px 4px;border-radius:var(--_k-radius-sm);background:var(--_k-fill)}\n.knobs-hy-dfoot{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:var(--_k-space-sibling)}\n.knobs-hy-dfoot:empty{display:none}\n.knobs-hy-evidence{display:inline-flex;align-items:center;height:20px;padding:0 6px;border-radius:var(--_k-radius-sm);background:var(--_k-fill);color:var(--_k-fg-2);\n font:var(--_k-type-small)/1 var(--_k-mono);text-decoration:none;transition:background calc(var(--_k-hover-out) * 1ms),color calc(var(--_k-hover-out) * 1ms)}\na.knobs-hy-evidence:hover{background:var(--_k-fill-hover);color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}\n.knobs-ui .knobs-hy-jump{display:inline-flex;align-items:center;gap:4px;height:20px;padding:0 6px 0 8px;border-radius:var(--_k-radius-sm);color:var(--_k-fg-2);cursor:pointer;\n font-size:var(--_k-type-small);transition:background calc(var(--_k-hover-out) * 1ms),color calc(var(--_k-hover-out) * 1ms)}\n.knobs-ui .knobs-hy-jump:is(:hover,:focus-visible){background:var(--_k-fill);color:var(--_k-fg);transition-duration:calc(var(--_k-hover-in) * 1ms)}\n.knobs-hy-jump svg{width:12px;height:12px}\n\n/* \u2014\u2014\u2014 the pointer's tooltip: title, date, one line \u2014\u2014\u2014 */\n.knobs-hy-tip{position:absolute;left:0;top:0;max-width:280px;padding:6px 8px;border-radius:var(--_k-radius-md);background:var(--_k-bg-overlay);\n box-shadow:var(--_k-shadow-overlay),0 0 0 1px var(--_k-border);pointer-events:none;opacity:0;transition:opacity calc(var(--_k-hover-out) * 1ms)}\n.knobs-hy-tip[data-on]{opacity:1;transition-duration:calc(var(--_k-hover-in) * 1ms)}\n.knobs-hy-tt{font-size:var(--_k-type-label);line-height:var(--_k-leading-label);font-weight:var(--_k-weight-label);color:var(--_k-fg);text-wrap:balance}\n:is(.knobs-hy-td,.knobs-hy-tl){font-size:var(--_k-type-small);line-height:var(--_k-leading-small);color:var(--_k-fg-3)}\n.knobs-hy-tl{margin-top:2px;color:var(--_k-fg-2);text-wrap:pretty}\n.knobs-hy-tl:empty{display:none}\n\n@media(prefers-reduced-motion:reduce){\n .knobs-history :where(*,::before,::after){transition-property:opacity,background,color,box-shadow!important}\n .knobs-hy-dc{transition-duration:calc(var(--_k-hover-out) * 1ms)!important}\n}\n";
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
export type HistoryTone = "good" | "warn" | "bad" | "info" | "dim";
|
|
2
|
+
export type HistoryProvenance = "verified" | "inferred";
|
|
3
|
+
/** A small tag that backs a claim: a PR, a commit, a log, as a link or not. */
|
|
4
|
+
export interface HistoryEvidence {
|
|
5
|
+
readonly label: string;
|
|
6
|
+
readonly href?: string;
|
|
7
|
+
}
|
|
8
|
+
/** A stretch of a lane in one state: "v1 works", "v2 broken". */
|
|
9
|
+
export interface HistorySpan {
|
|
10
|
+
readonly id?: string;
|
|
11
|
+
/** ISO date (`2026-09-19`), or as a page writes it (`"Sep 19, 2026"`). */
|
|
12
|
+
readonly from: string;
|
|
13
|
+
/** Left out: still so today. */
|
|
14
|
+
readonly to?: string;
|
|
15
|
+
/** A key in `states`, or a word of its own. */
|
|
16
|
+
readonly state: string;
|
|
17
|
+
/** Its tag at the end, in place of the state's (`"31d broken"`). */
|
|
18
|
+
readonly label?: string;
|
|
19
|
+
/** Its detail's title, in place of "lane · state". */
|
|
20
|
+
readonly title?: string;
|
|
21
|
+
readonly detail?: string;
|
|
22
|
+
readonly provenance?: HistoryProvenance;
|
|
23
|
+
readonly source?: string;
|
|
24
|
+
readonly evidence?: readonly HistoryEvidence[];
|
|
25
|
+
/** A step it's about, for `onJump`. */
|
|
26
|
+
readonly step?: string;
|
|
27
|
+
}
|
|
28
|
+
/** A row of the chart: a channel or a track, with its spans. */
|
|
29
|
+
export interface HistoryLane {
|
|
30
|
+
readonly id: string;
|
|
31
|
+
readonly label: string;
|
|
32
|
+
readonly spans?: readonly HistorySpan[];
|
|
33
|
+
}
|
|
34
|
+
/** A dated moment: a commit, a release, a deletion. */
|
|
35
|
+
export interface HistoryEvent {
|
|
36
|
+
readonly id?: string;
|
|
37
|
+
/** ISO, or as written: "Apr 9, 2026", "Oct 14–16, 2026", "May 7–8". */
|
|
38
|
+
readonly date: string;
|
|
39
|
+
/** Where it ran over days: its last day. */
|
|
40
|
+
readonly to?: string;
|
|
41
|
+
/** The lane it sits on (`"code"` by default). */
|
|
42
|
+
readonly lane?: string;
|
|
43
|
+
readonly title: string;
|
|
44
|
+
readonly detail?: string;
|
|
45
|
+
/** Its mark: a commit a dot, a release a diamond, a deletion a cross, an audit a triangle. */
|
|
46
|
+
readonly type?: "commit" | "release" | "deletion" | "channel" | "audit" | (string & {});
|
|
47
|
+
readonly tone?: HistoryTone;
|
|
48
|
+
readonly provenance?: HistoryProvenance;
|
|
49
|
+
readonly source?: string;
|
|
50
|
+
readonly evidence?: readonly HistoryEvidence[];
|
|
51
|
+
readonly step?: string;
|
|
52
|
+
}
|
|
53
|
+
/** A faint labelled stretch behind every lane: a phase. */
|
|
54
|
+
export interface HistoryBand {
|
|
55
|
+
readonly from: string;
|
|
56
|
+
readonly to?: string;
|
|
57
|
+
readonly label: string;
|
|
58
|
+
}
|
|
59
|
+
/** What a span's state means: its name in the legend, its colour and its tag (`{d}` the days). */
|
|
60
|
+
export interface HistoryState {
|
|
61
|
+
readonly label?: string;
|
|
62
|
+
readonly tone?: HistoryTone;
|
|
63
|
+
/** `"{d}d broken"` (the default `"{d}d"`), or words of its own (`"v1"`). */
|
|
64
|
+
readonly tag?: string;
|
|
65
|
+
}
|
|
66
|
+
/** The walkthrough's older shape: events under headings. */
|
|
67
|
+
export interface HistoryGroup {
|
|
68
|
+
readonly heading?: string;
|
|
69
|
+
readonly summary?: string;
|
|
70
|
+
readonly events?: readonly HistoryEvent[];
|
|
71
|
+
readonly rows?: readonly HistoryEvent[];
|
|
72
|
+
}
|
|
73
|
+
/** What `history()` draws: plain JSON. */
|
|
74
|
+
export interface HistoryData {
|
|
75
|
+
readonly axis?: {
|
|
76
|
+
readonly from?: string;
|
|
77
|
+
readonly to?: string;
|
|
78
|
+
readonly today?: string;
|
|
79
|
+
};
|
|
80
|
+
readonly lanes?: readonly HistoryLane[];
|
|
81
|
+
readonly events?: readonly HistoryEvent[];
|
|
82
|
+
readonly bands?: readonly HistoryBand[];
|
|
83
|
+
readonly states?: Readonly<Record<string, HistoryState>>;
|
|
84
|
+
/** The walkthrough's older shape: each group's events go on the code lane under a band. */
|
|
85
|
+
readonly groups?: readonly HistoryGroup[];
|
|
86
|
+
}
|
|
87
|
+
/** Days since 1970-01-01 (UTC): every date in the model. */
|
|
88
|
+
export type Day = number;
|
|
89
|
+
export interface HyItem {
|
|
90
|
+
readonly id: string;
|
|
91
|
+
readonly kind: "event" | "span";
|
|
92
|
+
readonly lane: string;
|
|
93
|
+
readonly from: Day;
|
|
94
|
+
readonly to: Day;
|
|
95
|
+
/** A span still running today. */
|
|
96
|
+
readonly open: boolean;
|
|
97
|
+
readonly title: string;
|
|
98
|
+
readonly detail?: string;
|
|
99
|
+
readonly tone: HistoryTone;
|
|
100
|
+
readonly type?: string;
|
|
101
|
+
readonly inferred: boolean;
|
|
102
|
+
readonly provenance?: HistoryProvenance;
|
|
103
|
+
readonly source?: string;
|
|
104
|
+
readonly evidence: readonly HistoryEvidence[];
|
|
105
|
+
readonly step?: string;
|
|
106
|
+
/** A span's state key. */
|
|
107
|
+
readonly state?: string;
|
|
108
|
+
/** A span's end tags, longest first: its own or the state's, then the days alone. */
|
|
109
|
+
readonly tags: readonly string[];
|
|
110
|
+
}
|
|
111
|
+
export interface HyLane {
|
|
112
|
+
readonly id: string;
|
|
113
|
+
readonly label: string;
|
|
114
|
+
readonly spans: readonly HyItem[];
|
|
115
|
+
readonly events: readonly HyItem[];
|
|
116
|
+
}
|
|
117
|
+
export interface HyBand {
|
|
118
|
+
readonly from: Day;
|
|
119
|
+
readonly to: Day;
|
|
120
|
+
readonly label: string;
|
|
121
|
+
}
|
|
122
|
+
export interface HyState {
|
|
123
|
+
readonly key: string;
|
|
124
|
+
readonly label: string;
|
|
125
|
+
readonly tone: HistoryTone;
|
|
126
|
+
}
|
|
127
|
+
export interface HistoryModel {
|
|
128
|
+
readonly from: Day;
|
|
129
|
+
readonly to: Day;
|
|
130
|
+
readonly today: Day;
|
|
131
|
+
readonly lanes: readonly HyLane[];
|
|
132
|
+
/** Every event, in the order ←/→ walk them. */
|
|
133
|
+
readonly events: readonly HyItem[];
|
|
134
|
+
readonly bands: readonly HyBand[];
|
|
135
|
+
/** The states the spans use, in the order they first appear. */
|
|
136
|
+
readonly states: readonly HyState[];
|
|
137
|
+
/** The event types used, in the order they first appear. */
|
|
138
|
+
readonly types: readonly string[];
|
|
139
|
+
readonly inferred: boolean;
|
|
140
|
+
readonly items: ReadonlyMap<string, HyItem>;
|
|
141
|
+
}
|
|
142
|
+
export declare const dayOf: (y: number, m: number, d: number) => Day;
|
|
143
|
+
export declare const todayDay: (now?: number) => Day;
|
|
144
|
+
/**
|
|
145
|
+
* A date as written, as whole days: ISO (`2026-04-09`, a time after it ignored), or `"Apr 9, 2026"`,
|
|
146
|
+
* `"Oct 14–16, 2026"`, `"Jun 26 – Jul 2, 2026"`; with no year, `year`. Null when it's neither.
|
|
147
|
+
*/
|
|
148
|
+
export declare function parseDay(text: string, year?: number): {
|
|
149
|
+
from: Day;
|
|
150
|
+
to: Day;
|
|
151
|
+
year?: number;
|
|
152
|
+
} | null;
|
|
153
|
+
/** `"Jun 26, 2026"`. */
|
|
154
|
+
export declare function formatDay(day: Day): string;
|
|
155
|
+
/** One day, or a range as short as it reads: `"Oct 14–16, 2026"`, `"Sep 19, 2026 – today"`. */
|
|
156
|
+
export declare function formatRange(from: Day, to: Day, open?: boolean): string;
|
|
157
|
+
/** `"1 day"`, `"31 days"`. */
|
|
158
|
+
export declare const formatDays: (n: number) => string;
|
|
159
|
+
/** A state's colour from its name when `states` doesn't give one. */
|
|
160
|
+
export declare function guessTone(state: string): HistoryTone;
|
|
161
|
+
/** A group heading as a band's label: its number and a trailing "(Apr – May 2026)" go. */
|
|
162
|
+
export declare const bandLabel: (heading: string) => string;
|
|
163
|
+
/**
|
|
164
|
+
* The walkthrough's older shape in the new one: every group's events on the code lane, each group
|
|
165
|
+
* with a heading a faint band from its first event to its last. Dates with no year take the year
|
|
166
|
+
* of the event before (or the first one written).
|
|
167
|
+
*/
|
|
168
|
+
export declare function fromGroups(groups: readonly HistoryGroup[], today: Day): {
|
|
169
|
+
events: HistoryEvent[];
|
|
170
|
+
bands: HistoryBand[];
|
|
171
|
+
};
|
|
172
|
+
/** A day as ISO `2026-04-09`. */
|
|
173
|
+
export declare function iso(day: Day): string;
|
|
174
|
+
/** The document filled in: whole days, lanes with their spans and events, the axis and the order. */
|
|
175
|
+
export declare function normalizeHistory(data: HistoryData, now?: Day): HistoryModel;
|
|
176
|
+
/** The order ←/→ walk: by date, then lane (top first), then as written. */
|
|
177
|
+
export declare function eventOrder(events: readonly {
|
|
178
|
+
item: HyItem;
|
|
179
|
+
index: number;
|
|
180
|
+
laneIndex: number;
|
|
181
|
+
}[]): HyItem[];
|
|
182
|
+
/**
|
|
183
|
+
* The event ←/→ land on from `current`: the next or the one before, stopping at the ends; from
|
|
184
|
+
* nothing (or a span), → the first and ← the last.
|
|
185
|
+
*/
|
|
186
|
+
export declare function stepEvent(order: readonly HyItem[], current: string | null, dir: 1 | -1): HyItem | null;
|
|
187
|
+
/** Where a day sits across `width` (pixels), and the day at an x. */
|
|
188
|
+
export declare function dayScale(from: Day, to: Day, width: number): {
|
|
189
|
+
x: (day: number) => number;
|
|
190
|
+
day: (x: number) => number;
|
|
191
|
+
/** As a fraction of the width, for `left: n%`. */
|
|
192
|
+
f: (day: number) => number;
|
|
193
|
+
};
|
|
194
|
+
export interface MonthTick {
|
|
195
|
+
readonly day: Day;
|
|
196
|
+
/** `"Apr"`, or the year on January. */
|
|
197
|
+
readonly label: string;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* A tick on the first of each month inside the axis, thinned to every 2, 3, 6 or 12 months until
|
|
201
|
+
* neighbours sit at least `minGap` pixels apart. January says its year.
|
|
202
|
+
*/
|
|
203
|
+
export declare function monthTicks(from: Day, to: Day, width: number, minGap?: number): MonthTick[];
|
|
204
|
+
/**
|
|
205
|
+
* Which labels to draw, left to right: each starts at its `x` and is `w` wide, and one is left out
|
|
206
|
+
* where it would touch the one before (within `gap`), a reserved box (today's label), or run past
|
|
207
|
+
* `width`.
|
|
208
|
+
*/
|
|
209
|
+
export declare function fitLabels(labels: readonly {
|
|
210
|
+
x: number;
|
|
211
|
+
w: number;
|
|
212
|
+
}[], width: number, reserved?: readonly {
|
|
213
|
+
x0: number;
|
|
214
|
+
x1: number;
|
|
215
|
+
}[], gap?: number): boolean[];
|
|
216
|
+
/** A span clipped to the axis, and which ends were cut; null when it's outside it. */
|
|
217
|
+
export declare function clipSpan(from: Day, to: Day, axisFrom: Day, axisTo: Day): {
|
|
218
|
+
from: Day;
|
|
219
|
+
to: Day;
|
|
220
|
+
clipStart: boolean;
|
|
221
|
+
clipEnd: boolean;
|
|
222
|
+
} | null;
|
|
223
|
+
/**
|
|
224
|
+
* Where a span's tag goes: the longest of `widths` (pixels, longest first) that fits inside the
|
|
225
|
+
* bar with `pad` either side, else one that fits after it in `roomAfter`; else none.
|
|
226
|
+
*/
|
|
227
|
+
export declare function fitTag(widths: readonly number[], spanWidth: number, roomAfter: number, pad?: number): {
|
|
228
|
+
index: number;
|
|
229
|
+
place: "inside" | "after";
|
|
230
|
+
} | null;
|
|
231
|
+
/** Each band's label room: from its start to its end or the next band's start, if sooner. */
|
|
232
|
+
export declare function bandRoom(bands: readonly HyBand[], x: (day: Day) => number): number[];
|
|
233
|
+
/** The event nearest `day` within `tolerance` days, else null. */
|
|
234
|
+
export declare function nearestEvent(events: readonly HyItem[], day: number, tolerance: number): HyItem | null;
|
|
235
|
+
/** The first sentence of a detail: a tooltip's one line. */
|
|
236
|
+
export declare function firstSentence(text: string | undefined): string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type HistoryData, type HistoryModel } from "./history-model.js";
|
|
2
|
+
import { type UiLook } from "./look.js";
|
|
3
|
+
export type { HistoryBand, HistoryData, HistoryEvent, HistoryEvidence, HistoryGroup, HistoryLane, HistoryProvenance, HistorySpan, HistoryState, HistoryTone, } from "./history-model.js";
|
|
4
|
+
export interface HistoryOptions extends UiLook {
|
|
5
|
+
/** Lanes of spans and events (or the walkthrough's `groups` of events): plain JSON. */
|
|
6
|
+
readonly data: HistoryData;
|
|
7
|
+
/** The selected event or span's id. */
|
|
8
|
+
readonly selected?: string | null;
|
|
9
|
+
/** Clicks and keys moved the selection (a host's `select()` doesn't call it). */
|
|
10
|
+
readonly onSelect?: (id: string | null) => void;
|
|
11
|
+
/** Draws "Go to step" in the detail of anything with a `step`, and Enter on it. */
|
|
12
|
+
readonly onJump?: (step: string) => void;
|
|
13
|
+
/** Its accessible name. */
|
|
14
|
+
readonly label?: string;
|
|
15
|
+
/** Under this width (px) lane labels sit above their lanes instead of beside them (560). */
|
|
16
|
+
readonly narrow?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface History {
|
|
19
|
+
readonly element: HTMLElement;
|
|
20
|
+
/** The data filled in: whole days, lanes, the axis and the order ←/→ walk. */
|
|
21
|
+
readonly model: HistoryModel;
|
|
22
|
+
readonly selected: string | null;
|
|
23
|
+
/** Selects an event or span (`null` clears); doesn't call `onSelect`. */
|
|
24
|
+
select(id: string | null): void;
|
|
25
|
+
set(data: HistoryData): void;
|
|
26
|
+
focus(): void;
|
|
27
|
+
dispose(): void;
|
|
28
|
+
/** The same as `dispose`. */
|
|
29
|
+
destroy(): void;
|
|
30
|
+
}
|
|
31
|
+
/** Adds the history's stylesheet once per document. */
|
|
32
|
+
export declare function injectHistoryStyles(doc: Document): void;
|
|
33
|
+
export declare function history(container: HTMLElement, options: HistoryOptions): History;
|