@leftoff/cli 0.9.18-beta.1 → 0.9.18-beta.11
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/CHANGELOG.md +46 -1
- package/bin/leftoff-statusline.js +9 -0
- package/bin/leftoff.js +787 -557
- package/bin/pty-host-main.js +66 -0
- package/docs/SECURITY.md +1 -1
- package/docs/macos-permissions.md +52 -0
- package/docs/test-plan.md +43 -0
- package/mod/.claude-plugin/plugin.json +1 -1
- package/mod/hooks/core.js +211 -13
- package/mod/hooks/register.js +196 -7
- package/mod/skills/walkthrough/SKILL.md +5 -3
- package/package.json +1 -1
- package/ui/app.css +279 -4
- package/ui/app.js +661 -53
- package/ui/ask.js +5 -3
- package/ui/chip-fade.js +1 -1
- package/ui/cp-fold.js +22 -0
- package/ui/decisions.js +245 -0
- package/ui/fs-side.js +29 -0
- package/ui/hook-fix.js +109 -0
- package/ui/live-status.js +68 -2
- package/ui/live-view.js +36 -5
- package/ui/prs.js +15 -5
- package/ui/pull.js +14 -1
- package/ui/semdiff.js +163 -20
- package/ui/session-catchup.js +18 -2
- package/ui/session-title.js +88 -0
- package/ui/sglyph.js +2 -2
- package/ui/slash-commands.js +89 -0
- package/ui/slash.js +82 -0
- package/ui/summary-examples.js +2 -2
- package/ui/tabs.js +10 -3
- package/ui/term.js +11 -3
- package/ui/testplan.js +162 -0
- package/ui/viewport-fit.js +73 -0
- package/ui/walk-pop.js +3 -0
- package/ui/what-it-did.js +12 -5
package/ui/app.css
CHANGED
|
@@ -2491,10 +2491,25 @@ mark.tx, .co-snip mark, .tx-snip mark, .tx-msg-text mark { background: var(--tx-
|
|
|
2491
2491
|
|
|
2492
2492
|
/* 0.9.8 attachments: a paperclip beside ↑, chips above the words inside the box, thumbnails in your bubble */
|
|
2493
2493
|
.composer { flex-wrap: wrap; }
|
|
2494
|
+
/* "/" commands (ui/slash.js): a list above the composer; options 44px tall */
|
|
2495
|
+
.composer .slash-menu { position: absolute; left: 0; right: 0; bottom: calc(100% + 6px); z-index: 30; max-height: min(320px, 50vh); overflow-y: auto; overscroll-behavior: contain; padding: 4px; border: 1px solid var(--line-strong); border-radius: 14px; background: var(--card); box-shadow: 0 8px 24px rgba(0,0,0,.22); cursor: default; }
|
|
2496
|
+
.slash-h { padding: 6px 10px 2px; font: 600 11.5px/1.4 var(--mono); color: var(--muted); }
|
|
2497
|
+
.slash-opt { display: flex; align-items: baseline; gap: 10px; min-height: 44px; box-sizing: border-box; padding: 11px 10px; border-radius: 10px; cursor: pointer; }
|
|
2498
|
+
.slash-opt[aria-selected="true"] { background: var(--bg2); }
|
|
2499
|
+
@media (hover: hover) { .slash-opt:hover { background: var(--bg2); } }
|
|
2500
|
+
.slash-opt.off { cursor: not-allowed; opacity: .6; }
|
|
2501
|
+
.slash-l { flex: none; font: 500 13.5px/1.5 var(--mono); color: var(--text); }
|
|
2502
|
+
.slash-hint { color: var(--muted); font-weight: 400; }
|
|
2503
|
+
.slash-w { min-width: 0; font-size: 13px; line-height: 1.5; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
2504
|
+
/* a slash command's bubble: the command in mono, then what Claude Code printed */
|
|
2505
|
+
.lv-cmd code { font-size: 13.5px; background: none; border: 0; padding: 0; }
|
|
2506
|
+
.lv-cmd-out { margin: 6px 0 0; padding: 0; font: 12.5px/1.5 var(--mono); color: var(--text2); white-space: pre-wrap; overflow-wrap: anywhere; background: none; border: 0; max-height: 240px; overflow-y: auto; }
|
|
2507
|
+
.lv-cmd-done { font-family: inherit; color: var(--muted); }
|
|
2494
2508
|
.composer .att-chips { flex: 0 0 100%; order: -1; display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 8px 2px 0; margin-left: -6px; }
|
|
2495
2509
|
.composer .att-chips[hidden] { display: none; }
|
|
2496
2510
|
.composer.att-drop { border-color: var(--accent); box-shadow: 0 0 0 3px var(--focus); background: color-mix(in srgb, var(--accent) 7%, var(--card2)); }
|
|
2497
2511
|
.att-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; overflow: hidden; clip: rect(0 0 0 0); }
|
|
2512
|
+
.att-picker { position: fixed; left: 0; bottom: 0; } /* 0.9.18: the paperclip's picker, outside the page so a redraw can't drop what you picked */
|
|
2498
2513
|
.composer-attach { flex: none; width: 44px; height: 44px; margin: 0; padding: 0; border: 0; background: transparent; display: grid; place-items: center; cursor: pointer; color: var(--muted); -webkit-tap-highlight-color: transparent; }
|
|
2499
2514
|
.composer-clip { width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center; transition: background-color .15s, color .15s; }
|
|
2500
2515
|
.composer-clip .ic { width: 19px; height: 19px; }
|
|
@@ -2841,7 +2856,18 @@ mark.tx, .co-snip mark, .tx-snip mark, .tx-msg-text mark { background: var(--tx-
|
|
|
2841
2856
|
.tab:hover { color: var(--text); }
|
|
2842
2857
|
.tab:focus-visible, .tab-x:focus-visible, .tab-all:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
|
|
2843
2858
|
.tab-board { flex: none; border-right: 1px solid var(--line); }
|
|
2844
|
-
.tab-cell { position: relative; flex: 0 1 200px; min-width:
|
|
2859
|
+
.tab-cell { position: relative; flex: 0 1 200px; min-width: 140px; display: flex; align-items: stretch; border-right: 1px solid var(--line); }
|
|
2860
|
+
/* 0.9.18 a busy strip: the tab you're on keeps its full width (only the others shrink), the rest
|
|
2861
|
+
keep 140px and the strip scrolls with a fade at the edge that has more (ui/chip-fade.js);
|
|
2862
|
+
a pinned tab is its pin and its status shape, the name in its tip and label. */
|
|
2863
|
+
.tab-cell.on { flex-shrink: 0; }
|
|
2864
|
+
.tab-cell.pinned { flex: none; min-width: 0; }
|
|
2865
|
+
.tab-cell.pinned .tab-s { padding-right: 12px; }
|
|
2866
|
+
.tab-cell.pinned .tab-t { display: none; }
|
|
2867
|
+
.tab-cell.pinned .tab-pin { margin-right: 0; }
|
|
2868
|
+
#tabStrip .tab-scroll.fade-r { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); }
|
|
2869
|
+
#tabStrip .tab-scroll.fade-l { -webkit-mask-image: linear-gradient(to left, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(to left, #000 calc(100% - 32px), transparent); }
|
|
2870
|
+
#tabStrip .tab-scroll.fade-l.fade-r { -webkit-mask-image: linear-gradient(to right, transparent, #000 32px, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(to right, transparent, #000 32px, #000 calc(100% - 32px), transparent); }
|
|
2845
2871
|
.tab-s { flex: 1; min-width: 0; padding-right: 0; }
|
|
2846
2872
|
.tab.on, .tab-cell.on { color: var(--text); background: var(--card); }
|
|
2847
2873
|
.tab-board.on, .tab-cell.on { box-shadow: inset 0 -2px 0 var(--accent); }
|
|
@@ -3577,6 +3603,11 @@ html.lf-kb .live-box.full { padding-bottom: 8px; }
|
|
|
3577
3603
|
.live-fbar { display: flex; align-items: center; gap: 6px; min-height: 52px; padding-top: env(safe-area-inset-top); border-bottom: 1px solid var(--line); }
|
|
3578
3604
|
.live-box.full > .live-fbar { max-width: none; margin: 0 calc(-1 * var(--lf-pr)) 0 calc(-1 * var(--lf-pl)); width: auto; padding-left: max(4px, env(safe-area-inset-left)); padding-right: max(8px, env(safe-area-inset-right)); background: var(--bg); }
|
|
3579
3605
|
.live-fs-x { margin-left: 0; }
|
|
3606
|
+
/* 2026-11-05 session titles: the name on line one, folder@branch muted under it */
|
|
3607
|
+
.live-fbar .lf-tt { display: flex; flex-direction: column; justify-content: center; flex: 1 1 auto; min-width: 0; gap: 1px; }
|
|
3608
|
+
.live-fbar .lf-where { display: block; min-width: 0; font-size: 12px; line-height: 1.25; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
3609
|
+
.live-fbar .lf-where:empty { display: none; }
|
|
3610
|
+
.s-where { margin-top: 2px; font-size: 12px; line-height: 1.3; color: var(--muted); overflow-wrap: anywhere; }
|
|
3580
3611
|
.live-fbar .lf-title { display: block; flex: 1 1 auto; min-width: 0; margin: 0; font-size: 15px; font-weight: 650; line-height: 1.3; color: var(--text); text-transform: none; letter-spacing: normal; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
3581
3612
|
.lf-pills { flex: none; display: inline-flex; align-items: center; gap: 6px; }
|
|
3582
3613
|
.lf-stop { flex: none; min-height: 36px; }
|
|
@@ -4512,15 +4543,32 @@ html:not(.live-full) .fs-side { display: none; }
|
|
|
4512
4543
|
html.live-full.fs-on .live-box.full { height: calc(var(--lf-h, 100vh) - var(--fs-peek, 56px)); height: calc(var(--lf-h, 100dvh) - 56px - env(safe-area-inset-bottom)); padding-bottom: 6px; }
|
|
4513
4544
|
html.live-full.lf-kb .fs-side { display: none; }
|
|
4514
4545
|
html.live-full.lf-kb.fs-on .live-box.full { height: var(--lf-h, 100dvh); }
|
|
4546
|
+
/* 2026-11-06 typing outside the sheet: it steps aside whether or not a keyboard's height was measured (app.js fitSheetAside) */
|
|
4547
|
+
html.live-full.lf-typing .fs-side { display: none; }
|
|
4548
|
+
html.live-full.lf-typing.fs-on .live-box.full { height: var(--lf-h, 100dvh); }
|
|
4549
|
+
/* the sheet follows a finger (app.js); its peek is the handle, its content scrolls natively */
|
|
4550
|
+
.fs-side.dragging { transition: none; }
|
|
4551
|
+
.fs-peek { touch-action: none; }
|
|
4515
4552
|
}
|
|
4516
4553
|
@media (prefers-reduced-motion: reduce) { .fs-side { transition: none; } }
|
|
4517
4554
|
/* the tab strip: pinned tabs at the left (a pin), each tab's status as a shape AND a word, + New session */
|
|
4518
|
-
.tab-cell.pinned .tab-t { max-width: 10em; }
|
|
4519
4555
|
.tab-pin { display: inline-flex; flex: none; margin-right: 5px; color: var(--text2); }
|
|
4520
4556
|
.tab-pin .ic { width: 12px; height: 12px; }
|
|
4521
4557
|
.lf-tab .tab-pin { margin-right: 2px; }
|
|
4522
4558
|
.tab-st { display: inline-flex; align-items: center; gap: 3px; flex: none; margin-left: 6px; }
|
|
4523
4559
|
.tab-w { font-size: 11.5px; font-weight: 550; color: var(--text2); white-space: nowrap; }
|
|
4560
|
+
/* 0.9.18 the strip: a closable tab's shape sits in the ×'s place; the × on hover or keyboard focus.
|
|
4561
|
+
A pinned tab and a touch screen (no hover) keep the shape beside the title. */
|
|
4562
|
+
.tab-st:empty, .tab-st-touch { display: none; }
|
|
4563
|
+
.tab-x .tab-xsg { position: absolute; left: 50%; top: 50%; margin: -7px 0 0 -7px; }
|
|
4564
|
+
.tab-x.has-st .ic { opacity: 0; }
|
|
4565
|
+
.tab-cell:hover .tab-x.has-st .ic, .tab-x.has-st:focus-visible .ic { opacity: 1; }
|
|
4566
|
+
.tab-cell:hover .tab-x .tab-xsg, .tab-x:focus-visible .tab-xsg { visibility: hidden; }
|
|
4567
|
+
@media (hover: none), (pointer: coarse) {
|
|
4568
|
+
.tab-st-touch:not(:empty) { display: inline-flex; }
|
|
4569
|
+
.tab-x .tab-xsg { display: none; }
|
|
4570
|
+
.tab-x.has-st .ic { opacity: 1; }
|
|
4571
|
+
}
|
|
4524
4572
|
.tab-new { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 44px; color: var(--text2); border-radius: 8px; }
|
|
4525
4573
|
.tab-new .ic { width: 16px; height: 16px; }
|
|
4526
4574
|
@media (hover: hover) { .tab-new:hover { color: var(--text); background: var(--card2); } }
|
|
@@ -5274,6 +5322,18 @@ code .file-ref-off { font-size: inherit; }
|
|
|
5274
5322
|
.wt-sf .wt-snip + .wt-snip { border-top: 1px dashed var(--line); }
|
|
5275
5323
|
.wt-sf .wt-snip-h { margin: 0; padding: 4px 10px; }
|
|
5276
5324
|
@media (pointer: fine) { .wt-sf > summary { min-height: 34px; } .wt-sfa-b { min-height: 32px; } }
|
|
5325
|
+
/* 2026-11-03 a file a step shares with an earlier one: folded, with a quiet way back to where it's open */
|
|
5326
|
+
.wt-also { display: inline-flex; align-items: center; gap: 4px; align-self: stretch; padding: 0 6px; font-size: 12px; color: var(--text2); text-decoration: none; white-space: nowrap; }
|
|
5327
|
+
.wt-also .ic { width: 13px; height: 13px; color: var(--muted); }
|
|
5328
|
+
.wt-also:hover { color: var(--text); text-decoration: underline; text-underline-offset: 2px; }
|
|
5329
|
+
.wt-also:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 6px; }
|
|
5330
|
+
.wt-sf-also > summary .wt-sf-p { color: var(--text2); }
|
|
5331
|
+
/* the lines the step's refs name, when it shows a hunk whole: the rest steps back */
|
|
5332
|
+
.wt-gd-focus .gd-row:not(.wt-focus), .wt-gd-focus .gd-half:not(.wt-focus):not(.gd-empty) { opacity: .55; }
|
|
5333
|
+
.wt-gd-focus .gd-row.wt-focus .gd-n, .wt-gd-focus .gd-half.wt-focus .gd-gut { box-shadow: inset 2px 0 0 var(--muted); }
|
|
5334
|
+
.wt-focus-w { display: inline-flex; align-items: center; gap: 6px; }
|
|
5335
|
+
.wt-focus-g { width: 2px; height: 12px; border-radius: 1px; background: var(--muted); }
|
|
5336
|
+
.wt-snip-none { margin: 0; padding: 10px; font-size: 12.5px; }
|
|
5277
5337
|
/* "changed since": a half-filled circle and the words (the stale bar uses the same glyph) */
|
|
5278
5338
|
.wt-chg { flex: none; display: inline-flex; align-items: center; gap: 5px; height: 20px; padding: 1px 7px 0; box-sizing: border-box; border: 1px dashed var(--line-strong); border-radius: 6px; font-size: 11.5px; font-weight: 600; line-height: 1; color: var(--text2); }
|
|
5279
5339
|
.wt-chg-g { flex: none; width: 9px; height: 9px; border-radius: 50%; border: 1.5px solid currentColor; background: linear-gradient(90deg, currentColor 50%, transparent 50%); box-sizing: border-box; }
|
|
@@ -5287,8 +5347,8 @@ code .file-ref-off { font-size: inherit; }
|
|
|
5287
5347
|
.wt-dmode { display: none; }
|
|
5288
5348
|
@media (min-width: 1000px) { .wt-dmode { display: inline-flex; } }
|
|
5289
5349
|
.wt-r2 { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 8px; margin: 6px 0 0; }
|
|
5290
|
-
.wt-r2:not(:has(.wt-spot)):not(:has(.wt-dmode:not([hidden]))) { display: none; }
|
|
5291
|
-
@media (max-width: 999.98px) { .wt-r2:not(:has(.wt-spot)) { display: none; } }
|
|
5350
|
+
.wt-r2:not(:has(.wt-spot)):not(:has(.wt-dmode:not([hidden]))):not(:has(.fsc-seg)) { display: none; }
|
|
5351
|
+
@media (max-width: 999.98px) { .wt-r2:not(:has(.wt-spot)):not(:has(.fsc-seg)) { display: none; } }
|
|
5292
5352
|
/* the side-by-side drawing */
|
|
5293
5353
|
.gd-split .gd-srow { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); min-width: 0; }
|
|
5294
5354
|
.gd-half { display: grid; grid-template-columns: var(--gd-gw) 2ch minmax(0, 1fr); min-width: 0; }
|
|
@@ -5357,3 +5417,218 @@ code .file-ref-off { font-size: inherit; }
|
|
|
5357
5417
|
.fs-wt-rv { display: inline-flex; align-items: center; gap: 4px; height: 18px; margin-left: 6px; padding: 1px 6px 0; box-sizing: border-box; border: 1px solid var(--line); border-radius: 9px; font-size: 11px; font-weight: 600; line-height: 1; color: var(--text2); white-space: nowrap; vertical-align: middle; }
|
|
5358
5418
|
.fs-wt-rv .ic { width: 11px; height: 11px; color: var(--green); margin-top: -1px; }
|
|
5359
5419
|
.fs-wt-rv-part { border-style: dashed; }
|
|
5420
|
+
|
|
5421
|
+
/* 2026-11-03 This session · Whole folder (walkthrough, its Files view, What it did): a quiet segmented
|
|
5422
|
+
toggle with each side's count; 44px to a finger, 32px to a mouse. Files that aren't this session's carry
|
|
5423
|
+
a small hollow-dashed ring plus the words "not from this session" (shape and words, never color alone). */
|
|
5424
|
+
.fsc-seg { margin-right: auto; flex: none; }
|
|
5425
|
+
.wt-seg.fsc-seg button { height: 44px; gap: 7px; }
|
|
5426
|
+
.fsc-n { font: 500 11.5px/1 var(--mono); font-variant-numeric: tabular-nums; color: var(--muted); padding: 2px 5px 1px; border-radius: 6px; background: var(--bg2); }
|
|
5427
|
+
.wt-seg.fsc-seg button[aria-pressed="true"] .fsc-n { color: var(--text2); background: var(--bg); }
|
|
5428
|
+
@media (pointer: fine) { .wt-seg.fsc-seg button { height: 32px; } }
|
|
5429
|
+
@media (max-width: 600px) { .wt-r2 .fsc-seg { width: 100%; display: flex; } .wt-r2 .fsc-seg button { flex: 1 1 0; } }
|
|
5430
|
+
.nots-g { display: inline-block; flex: none; width: 7px; height: 7px; border: 1.5px dashed var(--muted); border-radius: 50%; box-sizing: border-box; margin: 0 5px 0 0; vertical-align: middle; }
|
|
5431
|
+
.wt-nots { display: inline-flex; align-items: center; flex: none; font-size: 11.5px; color: var(--muted); white-space: nowrap; margin-left: 6px; }
|
|
5432
|
+
.wt-pre { font-size: 11.5px; white-space: nowrap; margin-left: 6px; }
|
|
5433
|
+
.wt-fl-i .nots-g { margin: 0 2px 0 4px; }
|
|
5434
|
+
.wt-fl-i.nots .wt-fm-f, .wt-file.nots .wt-file-p, .wid-item.nots .wid-base { color: var(--text2); }
|
|
5435
|
+
.wid-scope { margin: 4px 0 8px; }
|
|
5436
|
+
.wid-scope .fsc-seg { display: flex; width: 100%; box-sizing: border-box; }
|
|
5437
|
+
.wid-scope .fsc-seg button { flex: 1 1 0; }
|
|
5438
|
+
.wid-path .wt-nots, .wid-path .wt-pre { font-family: var(--sans, inherit); }
|
|
5439
|
+
.wid-scope-n { display: flex; gap: 6px; align-items: flex-start; }
|
|
5440
|
+
.wid-scope-n .ic { width: 13px; height: 13px; flex: none; margin-top: 2px; }
|
|
5441
|
+
|
|
5442
|
+
/* 2026-11-03 "hooks not reporting": a quiet note beside the session's pills (a dashed ring plus words) */
|
|
5443
|
+
.sub .s-hooks { display: inline-flex; align-items: center; gap: 5px; margin-left: 4px; font-size: 12px; color: var(--muted); white-space: nowrap; }
|
|
5444
|
+
.sub .s-hooks-ring { width: 8px; height: 8px; border-radius: 50%; border: 1.5px dashed currentColor; box-sizing: border-box; flex: none; }
|
|
5445
|
+
.fs-head .sub > .s-hooks { height: 22px; vertical-align: middle; }
|
|
5446
|
+
.fs-side.is-scrolled .fs-head .sub > .s-hooks { display: none; }
|
|
5447
|
+
/* 2026-11-03 which hooks were skipped and why: the same note, its words cut short on a narrow header (the whole in its tip) */
|
|
5448
|
+
.sub .s-hooks-skip { max-width: min(420px, 100%); min-width: 0; }
|
|
5449
|
+
.sub .s-hooks-skip .s-hooks-t { overflow: hidden; text-overflow: ellipsis; min-width: 0; }
|
|
5450
|
+
|
|
5451
|
+
/* 2026-11-03 "Let me answer prompts here" (ui/hook-fix.js): the hooks-skipped note's way out. Centered; a bottom sheet on a phone. */
|
|
5452
|
+
.sub .s-hooks-fix { margin-left: 6px; min-height: 44px; padding: 0 10px; border: 1px solid var(--btn-line); border-radius: 8px; background: transparent; color: var(--accent); font: inherit; font-size: 12px; cursor: pointer; white-space: nowrap; }
|
|
5453
|
+
@media (pointer: fine) { .sub .s-hooks-fix { min-height: 32px; padding: 0 9px; } }
|
|
5454
|
+
.sub .s-hooks-fix:hover { background: var(--btn-hover); }
|
|
5455
|
+
.sub .s-hooks-fix:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
5456
|
+
.fs-side.is-scrolled .fs-head .sub > .s-hooks-fix { display: none; }
|
|
5457
|
+
.sub .s-hooks-applied .s-hooks-ring { border-style: dotted; }
|
|
5458
|
+
.hf-back { position: fixed; inset: 0; z-index: 400; background: rgba(0,0,0,.45); display: flex; align-items: center; justify-content: center; padding: 16px; }
|
|
5459
|
+
.hf-modal { width: min(560px, 100%); max-height: calc(100dvh - 32px); overflow: auto; background: var(--card); border: 1px solid var(--line-strong); border-radius: var(--radius); box-shadow: var(--toast-shadow); padding: 16px 18px 14px; color: var(--text2); }
|
|
5460
|
+
.hf-grab { display: none; }
|
|
5461
|
+
.hf-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
|
|
5462
|
+
.hf-head h2 { margin: 0; font-size: 15px; font-weight: 600; color: var(--text); text-transform: none; letter-spacing: normal; }
|
|
5463
|
+
.hf-p { margin: 8px 0; font-size: 13px; line-height: 1.5; }
|
|
5464
|
+
.hf-quiet { color: var(--muted); font-size: 12.5px; }
|
|
5465
|
+
.hf-file code, .hf-p code { font-size: 12px; overflow-wrap: anywhere; }
|
|
5466
|
+
.hf-file { margin: 4px 0; }
|
|
5467
|
+
.hf-code { margin: 6px 0; padding: 10px 12px; background: var(--field); border: 1px solid var(--line); border-radius: 8px; font-size: 12px; line-height: 1.45; overflow: auto; white-space: pre; color: var(--text); user-select: all; }
|
|
5468
|
+
.hf-mark { display: inline-block; width: 9px; height: 9px; margin-right: 7px; vertical-align: 0; box-sizing: border-box; }
|
|
5469
|
+
.hf-mark-ok { border-radius: 50%; background: var(--green); }
|
|
5470
|
+
.hf-mark-admin { border-radius: 2px; border: 1.5px solid var(--yellow); transform: rotate(45deg); }
|
|
5471
|
+
.hf-mark-off { border-radius: 50%; border: 1.5px dashed var(--muted); }
|
|
5472
|
+
.hf-ign { margin-top: 10px; padding-top: 6px; border-top: 1px solid var(--line); }
|
|
5473
|
+
.hf-acts { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
|
|
5474
|
+
.hf-acts .btn, .hf-x { min-height: 44px; min-width: 44px; justify-content: center; }
|
|
5475
|
+
@media (pointer: fine) { .hf-acts .btn, .hf-x { min-height: 32px; min-width: 32px; } }
|
|
5476
|
+
.hf-modal .btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
5477
|
+
@media (max-width: 640px) {
|
|
5478
|
+
.hf-back { align-items: flex-end; padding: 0; }
|
|
5479
|
+
.hf-modal { width: 100%; max-height: 88dvh; border-radius: var(--radius) var(--radius) 0 0; border-bottom: none; padding: 8px 16px calc(14px + env(safe-area-inset-bottom)); }
|
|
5480
|
+
.hf-grab { display: block; width: 36px; height: 4px; border-radius: 2px; background: var(--line-strong); margin: 2px auto 8px; }
|
|
5481
|
+
.hf-acts { justify-content: stretch; }
|
|
5482
|
+
.hf-acts .btn { flex: 1 1 auto; }
|
|
5483
|
+
}
|
|
5484
|
+
|
|
5485
|
+
/* 2026-11-03 decision memory */
|
|
5486
|
+
.dec-list { list-style: none; margin: 0; padding: 0 0 6px; display: grid; gap: 10px; }
|
|
5487
|
+
.dec-row { font-size: 13.5px; line-height: 1.45; color: var(--text); }
|
|
5488
|
+
.dec-id { font: 600 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text2); }
|
|
5489
|
+
.dec-why, .dec-alt, .dec-src { font-size: 12.5px; color: var(--text2); margin-top: 2px; }
|
|
5490
|
+
.dec-src { color: var(--muted, var(--text2)); }
|
|
5491
|
+
.dec-st { margin-left: 6px; font-size: 12px; color: var(--text2); white-space: nowrap; }
|
|
5492
|
+
.dec-rc { display: inline-flex; align-items: center; gap: 2px; font-size: 12px; color: var(--text2); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
5493
|
+
a.dec-rc { min-height: 44px; text-decoration: none; }
|
|
5494
|
+
a.dec-rc:hover .dec-rc-w { text-decoration: underline; }
|
|
5495
|
+
.pchip-rc { margin-left: 6px; }
|
|
5496
|
+
.proj-review-slot:empty { display: none; }
|
|
5497
|
+
.scu-decs { margin-top: 10px; }
|
|
5498
|
+
.scu-dec-h { font-size: 12.5px; font-weight: 600; color: var(--text2); margin: 0 0 4px; }
|
|
5499
|
+
.scu-dec-l { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; font-size: 13.5px; }
|
|
5500
|
+
.scu-dec-l .dec-st { margin: 0 4px 0 0; }
|
|
5501
|
+
.dec-given { margin: 6px 0 2px; font-size: 12.5px; color: var(--text2); }
|
|
5502
|
+
.dec-given-i { display: inline-block; width: 1em; text-align: center; }
|
|
5503
|
+
.dec-missed { color: var(--text); }
|
|
5504
|
+
.dec-given-how { color: var(--text2); }
|
|
5505
|
+
.dec-given code { font-size: 12px; }
|
|
5506
|
+
.dec-open { display: inline-flex; align-items: center; min-height: 44px; font-size: 13px; }
|
|
5507
|
+
/* the panel's list: one quiet line (status · id · source), the decision, why; a hairline between rows */
|
|
5508
|
+
.dec-list { gap: 0; }
|
|
5509
|
+
.dec-list > .dec-row { padding: 10px 0; border-top: 1px solid var(--line); }
|
|
5510
|
+
.dec-list > .dec-row:first-child { border-top: 0; padding-top: 2px; }
|
|
5511
|
+
.dec-row-h { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 2px; font-size: 11.5px; line-height: 1.5; color: var(--text2); }
|
|
5512
|
+
.dec-row-h .dec-st { margin-left: 0; font-size: 11.5px; color: var(--text); }
|
|
5513
|
+
.dec-row-h .dec-id { font: 500 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text2); opacity: .85; }
|
|
5514
|
+
.dec-row-h .dec-src { margin-top: 0; font-size: 11.5px; }
|
|
5515
|
+
.dec-auto { font-size: 11.5px; color: var(--text2); }
|
|
5516
|
+
.dec-dot { color: var(--text2); opacity: .6; }
|
|
5517
|
+
.dec-list .dec-text { font-size: 14px; font-weight: 500; line-height: 1.4; margin-top: 2px; overflow-wrap: anywhere; }
|
|
5518
|
+
.dec-row-rejected .dec-text { text-decoration: line-through; text-decoration-thickness: 1px; color: var(--text2); }
|
|
5519
|
+
.dec-row-superseded .dec-text { color: var(--text2); }
|
|
5520
|
+
.dec-list .dec-why, .dec-list .dec-alt { font-size: 12.5px; line-height: 1.4; }
|
|
5521
|
+
/* compact buttons: a small pill inside a 44px hit area */
|
|
5522
|
+
.dec-acts-panel { gap: 6px; margin-top: 8px; }
|
|
5523
|
+
.dec-acts.dec-acts-panel .btn { position: relative; min-height: 0; height: 28px; padding: 0 10px; font-size: 12.5px; border-radius: 7px; }
|
|
5524
|
+
.dec-acts.dec-acts-panel .btn::after { content: ''; position: absolute; inset: -8px -3px; }
|
|
5525
|
+
|
|
5526
|
+
.dec-page .dec-head h1 { margin: 4px 0 2px; }
|
|
5527
|
+
.dec-tl { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 0; }
|
|
5528
|
+
.dec-item { position: relative; padding: 12px 0 12px 18px; border-left: 2px solid var(--line); margin-left: 6px; }
|
|
5529
|
+
.dec-item::before { content: ''; position: absolute; left: -6px; top: 17px; width: 10px; height: 10px; border-radius: 50%; background: var(--bg); border: 2px solid var(--text2); box-sizing: border-box; }
|
|
5530
|
+
.dec-item.dec-accepted::before { background: var(--text2); }
|
|
5531
|
+
.dec-item.dec-superseded, .dec-item.dec-rejected { opacity: .78; }
|
|
5532
|
+
.dec-item.dec-superseded::before { border-color: var(--line-strong, var(--line)); }
|
|
5533
|
+
.dec-item.dec-rejected .dec-text { text-decoration: line-through; text-decoration-thickness: 1px; }
|
|
5534
|
+
.dec-h { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; font-size: 12.5px; color: var(--text2); }
|
|
5535
|
+
.dec-date { font-variant-numeric: tabular-nums; }
|
|
5536
|
+
.dec-repo { padding: 1px 6px; border: 1px solid var(--line); border-radius: 999px; font-size: 11.5px; }
|
|
5537
|
+
.dec-item .dec-st { margin-left: 0; }
|
|
5538
|
+
.dec-item .dec-text { font-size: 15px; line-height: 1.45; margin-top: 4px; color: var(--text); overflow-wrap: anywhere; }
|
|
5539
|
+
.dec-meta { font-size: 12.5px; color: var(--text2); margin-top: 4px; }
|
|
5540
|
+
.dec-chains { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 4px; font-size: 12.5px; color: var(--text2); }
|
|
5541
|
+
.dec-chip { font: 600 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text); background: transparent; border: 1px solid var(--line); border-radius: 6px; padding: 4px 6px; min-height: 24px; cursor: pointer; position: relative; }
|
|
5542
|
+
.wt-dec-rel { font: 600 11.5px/1 system-ui, sans-serif; margin-left: 6px; padding: 2px 5px; border: 1px solid var(--line); border-radius: 4px; color: var(--text2); white-space: nowrap; }
|
|
5543
|
+
.wt-dec-dep { border-style: solid; border-color: var(--text2); color: var(--text); }
|
|
5544
|
+
.wt-dec-prop { border-style: dashed; }
|
|
5545
|
+
.dec-chip::after { content: ''; position: absolute; inset: -10px -4px; }
|
|
5546
|
+
.dec-chip:hover { border-color: var(--text2); }
|
|
5547
|
+
.dec-sug { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin-top: 8px; padding: 8px 10px; border: 1px dashed var(--line); border-radius: 8px; font-size: 13px; }
|
|
5548
|
+
.dec-acts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
|
|
5549
|
+
.dec-acts .btn, .dec-acts .linkish { min-height: 44px; }
|
|
5550
|
+
.dec-acts-quiet { margin-top: 0; }
|
|
5551
|
+
.dec-ctl { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 4px 0 4px; }
|
|
5552
|
+
.dec-ctl .sask-opt, .dec-ctl .btn, .dec-store-opts .sask-opt { min-height: 44px; }
|
|
5553
|
+
.dec-store-h { font-weight: 600; margin-bottom: 6px; }
|
|
5554
|
+
.dec-store-opts { display: inline-flex; flex-wrap: wrap; gap: 6px; }
|
|
5555
|
+
.dec-store p { margin: 8px 0 0; max-width: 62ch; }
|
|
5556
|
+
.dec-edit { display: grid; gap: 8px; margin-top: 8px; max-width: 640px; }
|
|
5557
|
+
.dec-edit label { display: grid; gap: 4px; font-size: 12.5px; color: var(--text2); }
|
|
5558
|
+
.dec-in { font: inherit; font-size: 15px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 8px; background: var(--bg); color: var(--text); min-height: 44px; box-sizing: border-box; width: 100%; }
|
|
5559
|
+
@media (pointer: coarse) { .dec-in { font-size: 16px; } }
|
|
5560
|
+
.dec-in:focus-visible { outline: 2px solid var(--accent, var(--text2)); outline-offset: 1px; }
|
|
5561
|
+
.dec-flash { background: color-mix(in srgb, var(--text2) 10%, transparent); transition: background .6s; }
|
|
5562
|
+
@media (prefers-reduced-motion: reduce) { .dec-flash { transition: none; } }
|
|
5563
|
+
.dec-proj p { margin: 4px 0 10px; }
|
|
5564
|
+
.dec-empty { margin: 6px 0 10px; } .dec-empty p, .dec-proj .dec-empty p { margin: 0 0 4px; max-width: 64ch; } /* 2026-11-05 where decisions come from, on an empty log */
|
|
5565
|
+
|
|
5566
|
+
.wt-decs { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 2px; }
|
|
5567
|
+
.wt-dec { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; min-height: 32px; padding: 4px 8px; font-weight: 400; }
|
|
5568
|
+
@media (pointer: coarse) { .wt-dec { min-height: 44px; } }
|
|
5569
|
+
.wt-dec .wt-dec-id { font-weight: 600; }
|
|
5570
|
+
.wt-dec .wt-dec-t { font: 12.5px/1.3 var(--sans, system-ui, sans-serif); color: var(--text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 42ch; }
|
|
5571
|
+
.wt-dec-gone { border-style: dashed; }
|
|
5572
|
+
.wt-dec-w { font: 12px/1 var(--sans, system-ui, sans-serif); color: var(--text2); }
|
|
5573
|
+
.dec-chainview { margin-top: 4px; }
|
|
5574
|
+
|
|
5575
|
+
.dv-isdec .dv-dl { display: none !important; }
|
|
5576
|
+
.dec-ctl .sask-opt { border: 1px solid var(--line); border-radius: 8px; padding: 0 12px; background: transparent; color: var(--text2); cursor: pointer; }
|
|
5577
|
+
.dec-ctl .sask-opt[aria-pressed="true"] { color: var(--text); border-color: var(--line-strong, var(--text2)); }
|
|
5578
|
+
@media (min-width: 700px) { .dv-isdec .dv-sheet { max-width: 760px; height: auto; max-height: 80vh; margin-left: auto; margin-right: auto; } }
|
|
5579
|
+
|
|
5580
|
+
/* testplan Part 3: the test plan page (ui/testplan.js) */
|
|
5581
|
+
.tp-page .tp-head h1 { margin: 4px 0 2px; }
|
|
5582
|
+
.tp-budget { display: inline-flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 12px; }
|
|
5583
|
+
.tp-budget .sask-opt, .tp-go .btn, .tp-exp .btn, .tp-acts .btn, .tp-open { min-height: 44px; }
|
|
5584
|
+
.tp-budget .sask-opt { border: 1px solid var(--line); border-radius: 8px; padding: 0 14px; background: transparent; color: var(--text2); cursor: pointer; font: inherit; }
|
|
5585
|
+
.tp-budget .sask-opt[aria-pressed="true"] { color: var(--text); border-color: var(--line-strong, var(--text2)); font-weight: 600; }
|
|
5586
|
+
.tp-prl, .tp-recent, .tp-ncl { list-style: none; margin: 8px 0; padding: 0; display: grid; gap: 6px; font-size: 14px; }
|
|
5587
|
+
.tp-prl a, .tp-recent a { overflow-wrap: anywhere; }
|
|
5588
|
+
.tp-wt, .tp-nowt { font-size: 12px; color: var(--text2); white-space: nowrap; }
|
|
5589
|
+
.tp-go { margin: 12px 0; }
|
|
5590
|
+
.tp-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; margin: 8px 0 4px; }
|
|
5591
|
+
.tp-prog { font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
5592
|
+
.tp-cov { color: var(--text2); font-size: 13px; }
|
|
5593
|
+
.tp-exp { display: inline-flex; flex-wrap: wrap; gap: 8px; margin-left: auto; }
|
|
5594
|
+
.tp-by { font-size: 12.5px; margin: 0 0 8px; }
|
|
5595
|
+
.tp-list { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 12px; }
|
|
5596
|
+
.tp-check { border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px; background: var(--bg); }
|
|
5597
|
+
.tp-check.tp-done { border-style: solid; opacity: .82; }
|
|
5598
|
+
.tp-check.tp-failed { border-width: 2px; border-color: var(--text2); }
|
|
5599
|
+
.tp-ch { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
|
|
5600
|
+
.tp-n { font: 600 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text2); }
|
|
5601
|
+
.tp-title { font-size: 16px; margin: 0; flex: 1 1 260px; overflow-wrap: anywhere; }
|
|
5602
|
+
.tp-risk { font-size: 12px; white-space: nowrap; color: var(--text2); border: 1px solid var(--line); border-radius: 999px; padding: 2px 8px; }
|
|
5603
|
+
.tp-risk-high { color: var(--text); border-color: var(--text2); font-weight: 600; }
|
|
5604
|
+
.tp-min { font-size: 12px; color: var(--text2); font-variant-numeric: tabular-nums; }
|
|
5605
|
+
.tp-where, .tp-expect, .tp-why { margin: 6px 0 0; font-size: 14px; line-height: 1.45; overflow-wrap: anywhere; }
|
|
5606
|
+
.tp-k { display: inline-block; min-width: 96px; font-size: 12px; color: var(--text2); }
|
|
5607
|
+
.tp-steps { margin: 6px 0 0; padding-left: 22px; font-size: 14px; line-height: 1.45; }
|
|
5608
|
+
.tp-cites { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
|
|
5609
|
+
.tp-cite { font: 600 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text); border: 1px solid var(--line); border-radius: 6px; padding: 5px 7px; text-decoration: none; position: relative; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
5610
|
+
a.tp-cite::after { content: ''; position: absolute; inset: -10px -4px; }
|
|
5611
|
+
.tp-acts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; align-items: center; }
|
|
5612
|
+
.tp-tick[aria-pressed="true"] { font-weight: 600; border-color: var(--text); }
|
|
5613
|
+
.tp-clear { min-height: 44px; }
|
|
5614
|
+
.tp-note { display: grid; gap: 8px; margin-top: 10px; max-width: 640px; }
|
|
5615
|
+
.tp-note-l { display: grid; gap: 4px; font-size: 12.5px; color: var(--text2); }
|
|
5616
|
+
.tp-propf { margin-top: 10px; }
|
|
5617
|
+
.tp-prop { display: inline-flex; align-items: center; min-height: 44px; }
|
|
5618
|
+
.tp-nc-w { overflow-wrap: anywhere; }
|
|
5619
|
+
.tp-notes { margin: 12px 0; font-size: 13px; color: var(--text2); }
|
|
5620
|
+
.tp-notes summary { min-height: 44px; display: flex; align-items: center; cursor: pointer; }
|
|
5621
|
+
.tp-del .linkish { min-height: 44px; }
|
|
5622
|
+
.tp-wait { display: flex; align-items: center; gap: 10px; padding: 16px 0; color: var(--text2); }
|
|
5623
|
+
.tp-spin { width: 14px; height: 14px; border: 2px solid var(--line); border-top-color: var(--text2); border-radius: 50%; animation: tp-spin 1s linear infinite; }
|
|
5624
|
+
@keyframes tp-spin { to { transform: rotate(360deg); } }
|
|
5625
|
+
@media (prefers-reduced-motion: reduce) { .tp-spin { animation: none; } }
|
|
5626
|
+
@media (max-width: 600px) { .tp-exp { margin-left: 0; } .tp-k { display: block; min-width: 0; } }
|
|
5627
|
+
/* testplan Part 3: picking merged PRs on the PRs page */
|
|
5628
|
+
.prrow.pickable { display: flex; gap: 8px; align-items: flex-start; }
|
|
5629
|
+
.pr-pick { display: inline-flex; flex-direction: column; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; border: 0; background: transparent; color: var(--text2); cursor: pointer; font: inherit; font-size: 18px; line-height: 1; padding: 0; }
|
|
5630
|
+
.pr-pick-w { font-size: 11px; margin-top: 2px; }
|
|
5631
|
+
.pr-pick[aria-pressed="true"] { color: var(--text); }
|
|
5632
|
+
.prs-picks { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; padding: 8px 12px; margin: 0 0 10px; border: 1px solid var(--line); border-radius: 10px; background: var(--bg); }
|
|
5633
|
+
.prs-picks .btn, .prs-picks .linkish { min-height: 44px; }
|
|
5634
|
+
.wt-dec .wt-dec-id { white-space: nowrap; flex: none; }
|