@jabworks/condux 0.13.3 → 0.14.1
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jabworks/condux",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.1",
|
|
4
4
|
"description": "OpenCode plugin for the condux workflow toolkit — bundles the condux skills and injects the specialist agents (coder, explorer, planner, researcher) plus an opt-in plan-review listener.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -372,16 +372,21 @@
|
|
|
372
372
|
/* A keyboard layer nobody can discover is a keyboard layer nobody uses. kit.js
|
|
373
373
|
places this next to every theme group, so the ? overlay has a visible way in
|
|
374
374
|
for anyone who was never told the shortcut exists. */
|
|
375
|
-
/* kit.js wraps the theme group and the help button in this. The
|
|
376
|
-
|
|
377
|
-
|
|
375
|
+
/* kit.js wraps the theme group and the help button in this. The pairing is
|
|
376
|
+
content-sized, so ? sits against the group no matter what the host is: in a
|
|
377
|
+
flex row the wrapper shrink-wraps anyway, and in block flow (a sidebar) the
|
|
378
|
+
wrapper fills the width but the group no longer stretches across it. Growing
|
|
379
|
+
the group is opt-in — a host that wants it to fill the row says so itself,
|
|
380
|
+
the way the style guide's rail does with `.kit-controls > .themebar`. This
|
|
381
|
+
was the default until 2026-08-25, and the one surface hosting the group in
|
|
382
|
+
block flow (plan-review's nav) got a ? at the far edge (docket #52). */
|
|
378
383
|
.kit-controls {
|
|
379
384
|
display: flex;
|
|
380
385
|
align-items: center;
|
|
381
386
|
}
|
|
382
387
|
|
|
383
388
|
.kit-controls > .kit-theme {
|
|
384
|
-
flex:
|
|
389
|
+
flex: none;
|
|
385
390
|
}
|
|
386
391
|
|
|
387
392
|
.kit-helpbtn {
|
|
@@ -476,12 +481,21 @@
|
|
|
476
481
|
the smaller 0.25rem step now say so explicitly via --radius, which
|
|
477
482
|
already carries that value and is already this file's convention
|
|
478
483
|
(.nav .tabs, .popover, .chip, code, pre, …). */
|
|
484
|
+
/* Docket #48 / Q16 — three theme states, not two. These tokens are
|
|
485
|
+
dark-first like the core, so their light values are an override. Written
|
|
486
|
+
as a bare media query, clicking Dark on a light system flipped every core
|
|
487
|
+
token and left the highlight cream, because the media query still
|
|
488
|
+
matched. Written the way scripts/tokens/core.css:84-85,130 writes it:
|
|
489
|
+
the system-light block excludes an explicit dark stamp, and an explicit
|
|
490
|
+
light block beats a dark OS. */
|
|
479
491
|
:root {
|
|
480
492
|
--hl: rgba(255, 202, 22, 0.30); --hl-active: rgba(255, 202, 22, 0.55);
|
|
481
493
|
}
|
|
482
494
|
@media (prefers-color-scheme: light) {
|
|
483
|
-
:root { --hl: rgba(234, 179, 8, 0.42); --hl-active: rgba(202, 138, 4, 0.62); }
|
|
495
|
+
:root:not([data-theme="dark"]) { --hl: rgba(234, 179, 8, 0.42); --hl-active: rgba(202, 138, 4, 0.62); }
|
|
484
496
|
}
|
|
497
|
+
:root[data-theme="light"] { --hl: rgba(234, 179, 8, 0.42); --hl-active: rgba(202, 138, 4, 0.62); }
|
|
498
|
+
:root[data-theme="dark"] { --hl: rgba(255, 202, 22, 0.30); --hl-active: rgba(255, 202, 22, 0.55); }
|
|
485
499
|
* { box-sizing: border-box; }
|
|
486
500
|
html, body { height: 100%; }
|
|
487
501
|
/* .kit-error sets display:flex unconditionally; an author rule beats the
|
|
@@ -493,9 +507,16 @@
|
|
|
493
507
|
one legitimately 2-D layout in this file; everything else here is a
|
|
494
508
|
one-dimensional row or column and stays flex. */
|
|
495
509
|
body { margin: 0; background: var(--background); color: var(--foreground); font-family: var(--sans); font-size: var(--text-sm); line-height: var(--leading-normal); font-variant-numeric: tabular-nums; display: grid; grid-template-rows: 1fr auto; height: 100dvh; }
|
|
496
|
-
|
|
510
|
+
/* D10 — the document takes the width the two lists were spending. Every
|
|
511
|
+
column rule is scoped ABOVE the stacking breakpoint on purpose: an
|
|
512
|
+
unscoped `grid-template-columns` outranks the 860px and 1100px rules by
|
|
513
|
+
source order, and the three-column grid survives into a phone viewport.
|
|
514
|
+
Measured at 640px while building D10: `.main` collapsed to 88px and the
|
|
515
|
+
340px popover ran off the right edge of the screen. */
|
|
516
|
+
.app { display: grid; grid-template-columns: 236px minmax(0, 1fr) 330px; min-height: 0; }
|
|
517
|
+
.app > * { min-width: 0; } /* Q14 — a bare 1fr column floors at min-content */
|
|
497
518
|
@media (max-width: 1100px) {
|
|
498
|
-
.app { grid-template-columns: 208px minmax(0, 1fr)
|
|
519
|
+
.app { grid-template-columns: 208px minmax(0, 1fr) 300px; }
|
|
499
520
|
}
|
|
500
521
|
@media (max-width: 860px) {
|
|
501
522
|
.app { grid-template-columns: 1fr; grid-template-rows: auto 1fr auto; max-height: none; }
|
|
@@ -537,9 +558,17 @@
|
|
|
537
558
|
.badge-warning { background: var(--warning-muted); color: var(--warning); border-color: var(--warning-border); }
|
|
538
559
|
.badge-soft { background: var(--primary-muted); color: var(--primary-text); }
|
|
539
560
|
|
|
540
|
-
/* Left: navigation — Contents (docs + headings) | Files (paths touched)
|
|
541
|
-
|
|
542
|
-
|
|
561
|
+
/* Left: navigation — Contents (docs + headings) | Files (paths touched).
|
|
562
|
+
D10 — a table of contents rather than a menu: no card ground, no active
|
|
563
|
+
pill, depth as padding. Note `.nav a` is display:flex, so a negative
|
|
564
|
+
text-indent does not produce a hanging indent here — it shifts the flex
|
|
565
|
+
container's first line and clips the label against the row. */
|
|
566
|
+
.nav { border-right: 1px solid var(--border); background: var(--background); overflow-y: auto; padding: 22px 16px 40px; }
|
|
567
|
+
/* Space the whole theme+? pairing, not the group inside it. .kit-controls
|
|
568
|
+
centres its children on the cross axis and flex centres the MARGIN box,
|
|
569
|
+
so a bottom margin here would ride the group up ~6px while ? stayed put
|
|
570
|
+
(docket #52). */
|
|
571
|
+
.nav .kit-controls { margin: 0 0 12px; }
|
|
543
572
|
.nav .tabs { display: flex; margin: 0 0 12px; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
|
|
544
573
|
.nav .tab { flex: 1; font: 500 var(--text-2xs) var(--sans); letter-spacing: .08em; text-transform: uppercase; padding: 6px 4px;
|
|
545
574
|
background: transparent; border: none; border-right: 1px solid var(--border); color: var(--subtle); cursor: pointer; }
|
|
@@ -547,16 +576,16 @@
|
|
|
547
576
|
.nav .tab:hover { color: var(--foreground); }
|
|
548
577
|
.nav .tab.active { background: var(--accent); color: var(--foreground); }
|
|
549
578
|
.nav a.doc { font-weight: 600; color: var(--foreground); }
|
|
550
|
-
.nav a.doc.active {
|
|
579
|
+
.nav a.doc.active { color: var(--foreground); background: transparent; }
|
|
551
580
|
.nav a.file .t { font-family: var(--mono); font-size: var(--text-xs); }
|
|
552
581
|
.nav .none { color: var(--subtle); font-size: var(--text-xs); padding: 4px 6px; }
|
|
553
582
|
code.mention-flash { outline: 2px solid var(--warning); outline-offset: 1px; }
|
|
554
583
|
.nav .label, .chat .label { font-size: var(--text-2xs); letter-spacing: 0.08em; text-transform: uppercase; color: var(--subtle); margin: 0 0 12px; padding: 0 6px; }
|
|
555
|
-
.nav a { display: flex; align-items: center; gap: 6px; color: var(--muted-foreground); text-decoration: none; padding:
|
|
584
|
+
.nav a { display: flex; align-items: center; gap: 6px; color: var(--muted-foreground); text-decoration: none; padding: 3px 2px; border-radius: 0; border-left: none; }
|
|
556
585
|
.nav a span.t { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1; }
|
|
557
|
-
.nav a:hover {
|
|
558
|
-
.nav a.active { color: var(--foreground);
|
|
559
|
-
.nav a.lvl2 { padding-left:
|
|
586
|
+
.nav a:hover { color: var(--foreground); }
|
|
587
|
+
.nav a.active { color: var(--foreground); font-weight: 600; background: transparent; }
|
|
588
|
+
.nav a.lvl2 { padding-left: 14px; } .nav a.lvl3 { padding-left: 26px; font-size: var(--text-xs); color: var(--subtle); }
|
|
560
589
|
|
|
561
590
|
/* Directory mode: doc-site tree — folder rows fold, filter narrows, the
|
|
562
591
|
active doc alone carries its headings. All of it DIRMODE-gated. */
|
|
@@ -566,14 +595,14 @@
|
|
|
566
595
|
.nav .tree-filter input:focus { border-color: var(--ring); }
|
|
567
596
|
.nav a.folder { font-weight: 600; color: var(--muted-foreground); }
|
|
568
597
|
.nav a.folder .arrow { flex: none; width: 12px; color: var(--subtle); font-size: var(--text-2xs); }
|
|
569
|
-
.crumbs { display: none; max-width:
|
|
598
|
+
.crumbs { display: none; max-width: 720px; margin: 0 auto 18px; font: var(--text-xs) var(--mono); color: var(--subtle);
|
|
570
599
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
571
600
|
.crumbs .seg { color: var(--muted-foreground); }
|
|
572
601
|
.crumbs a.seg { color: var(--muted-foreground); text-decoration: none; cursor: pointer; }
|
|
573
602
|
.crumbs a.seg:hover { color: var(--foreground); text-decoration: underline; }
|
|
574
603
|
.crumbs .sep { margin: 0 6px; color: var(--subtle); }
|
|
575
604
|
.crumbs .cur { color: var(--foreground); }
|
|
576
|
-
.pnav { display: none; max-width:
|
|
605
|
+
.pnav { display: none; max-width: 720px; margin: 32px auto 0; padding-top: 14px; border-top: 1px solid var(--border);
|
|
577
606
|
justify-content: space-between; gap: 12px; }
|
|
578
607
|
.pnav a { display: inline-flex; align-items: center; gap: 6px; max-width: 48%; color: var(--muted-foreground);
|
|
579
608
|
text-decoration: none; font-size: var(--text-sm); padding: 6px 10px; border: 1px solid var(--border); border-radius: var(--radius);
|
|
@@ -581,23 +610,23 @@
|
|
|
581
610
|
.pnav a:hover { background: var(--accent); color: var(--foreground); }
|
|
582
611
|
.pnav .spacer { flex: 1; }
|
|
583
612
|
|
|
584
|
-
/* Center: plan
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
.main .hint { color: var(--subtle); margin: 0 auto 24px; font-size: var(--text-xs); max-width:
|
|
591
|
-
.doc { max-width:
|
|
613
|
+
/* Center: plan. `position: relative` and `overflow-y: auto` are load-bearing
|
|
614
|
+
and must both stay: #popover is absolutely positioned against this box,
|
|
615
|
+
and selDoc stores coordinates relative to it plus its scrollTop. Moving
|
|
616
|
+
the scroll to body, or wrapping #plan in its own scroller, silently
|
|
617
|
+
breaks every annotation anchor. */
|
|
618
|
+
.main { overflow-y: auto; padding: 40px 44px 140px; position: relative; }
|
|
619
|
+
.main .hint { color: var(--subtle); margin: 0 auto 24px; font-size: var(--text-xs); max-width: 720px; }
|
|
620
|
+
.doc { max-width: 720px; margin: 0 auto; line-height: var(--leading-relaxed); counter-reset: hl; }
|
|
592
621
|
|
|
593
622
|
/* Revision diff — on a revised plan, show what changed instead of a silent swap. */
|
|
594
|
-
.revbar { display: none; align-items: center; gap: 10px; max-width:
|
|
623
|
+
.revbar { display: none; align-items: center; gap: 10px; max-width: 720px; margin: 0 auto 20px;
|
|
595
624
|
padding: 9px 12px; border: 1px solid var(--warning-border); background: var(--warning-muted); border-radius: var(--radius); }
|
|
596
625
|
.revbar.show { display: flex; }
|
|
597
626
|
.revbar .rb-text { color: var(--warning); font: 600 var(--text-xs) var(--sans); }
|
|
598
627
|
.revbar .rb-stat { color: var(--muted-foreground); font-size: var(--text-xs); }
|
|
599
628
|
.revbar .rb-actions { margin-left: auto; display: flex; gap: 6px; }
|
|
600
|
-
.diffwrap { display: none; max-width:
|
|
629
|
+
.diffwrap { display: none; max-width: 720px; margin: 0 auto; }
|
|
601
630
|
.diffwrap.show { display: block; }
|
|
602
631
|
.diffview { background: var(--muted); border: 1px solid var(--border); border-radius: var(--radius);
|
|
603
632
|
padding: 8px 0; overflow-x: auto; font: var(--text-xs)/var(--leading-normal) var(--mono); }
|
|
@@ -609,11 +638,24 @@
|
|
|
609
638
|
.diffline.del { background: var(--destructive-muted); color: var(--destructive); }
|
|
610
639
|
.diffline.del .g { color: var(--destructive); }
|
|
611
640
|
.diffline.fold { justify-content: center; color: var(--subtle); font-style: italic; padding: 3px 12px; }
|
|
641
|
+
/* D10 — a typographic spine. Hierarchy is space and rule, not boxes: h1
|
|
642
|
+
closes with a rule, h2 opens a section against a hairline, h3 hangs off
|
|
643
|
+
a short dash. This is what makes a long plan skimmable without folding
|
|
644
|
+
it — folds are ruled out on this surface (live Ranges and mark.hl nodes
|
|
645
|
+
have no client rects inside a collapsed <details>). */
|
|
612
646
|
h1, h2, h3, h4, h5, h6 { line-height: var(--leading-snug); margin: 1.5em 0 0.6em; scroll-margin-top: 24px; }
|
|
613
|
-
h1 { font-size: var(--text-
|
|
647
|
+
h1 { font-size: var(--text-2xl); letter-spacing: -0.015em; margin: 0 0 0.2em; padding-bottom: 0.5em; border-bottom: 1px solid var(--border); }
|
|
648
|
+
h2 { font-size: var(--text-lg); letter-spacing: -0.01em; margin: 2.4em 0 0.7em; padding-top: 0.9em; border-top: 1px solid var(--border); }
|
|
649
|
+
h3 { font-size: var(--text-base); color: var(--foreground); margin: 1.9em 0 0.5em; }
|
|
650
|
+
h3::before { content: ''; display: inline-block; width: 14px; height: 1px; vertical-align: middle; margin-right: 10px; background: var(--subtle); }
|
|
614
651
|
h4 { font-size: var(--text-sm); } h5, h6 { font-size: var(--text-xs); color: var(--muted-foreground); }
|
|
652
|
+
.doc > *:first-child, .docpane > *:first-child { margin-top: 0; }
|
|
615
653
|
p { margin: 0.6em 0; }
|
|
616
654
|
ul, ol { margin: 0.6em 0; padding-left: 22px; } li { margin: 0.25em 0; }
|
|
655
|
+
/* Q15 — capping measure on <p> alone misses the worst line, and a plan is
|
|
656
|
+
mostly list items. Table cells keep their column. */
|
|
657
|
+
.doc p, .doc li { max-width: 68ch; }
|
|
658
|
+
.doc td p, .doc td li, .doc th p { max-width: none; }
|
|
617
659
|
li.task { list-style: none; margin-left: -18px; }
|
|
618
660
|
del { color: var(--subtle); }
|
|
619
661
|
a { color: var(--info); }
|
|
@@ -622,12 +664,32 @@
|
|
|
622
664
|
pre code { background: none; border: none; padding: 0; }
|
|
623
665
|
blockquote { margin: 0.6em 0; padding: 2px 14px; color: var(--muted-foreground); border-left: 3px solid var(--primary); }
|
|
624
666
|
hr { border: none; border-top: 1px solid var(--border); margin: 1.6em 0; }
|
|
667
|
+
/* D10 — h2 now opens a section against its own rule, which makes a
|
|
668
|
+
preceding `---` a second divider saying the same thing. draft-plan's
|
|
669
|
+
canonical template puts `---` immediately before every task card, so
|
|
670
|
+
this is the shape of nearly every plan this surface renders: measured
|
|
671
|
+
9 doubled rules in one 9-task plan. Each block is wrapped in its own
|
|
672
|
+
.blk, so `hr + h2` cannot match — the relative selector looks forward
|
|
673
|
+
to the next block instead. The h2's rule is the spine and stays; the
|
|
674
|
+
redundant separator goes. */
|
|
675
|
+
.blk:has(> hr):has(+ .blk h2) { display: none; }
|
|
625
676
|
table { border-collapse: collapse; margin: 0.9em 0; width: 100%; display: block; overflow-x: auto; font-size: var(--text-sm); }
|
|
626
677
|
th, td { border: 1px solid var(--border); padding: 6px 10px; text-align: left; vertical-align: top; }
|
|
627
678
|
th { background: var(--muted); font-weight: 600; }
|
|
628
679
|
tbody tr:nth-child(even) { background: color-mix(in oklch, var(--muted) 40%, transparent); }
|
|
629
680
|
.blk { border-radius: 4px; padding: 1px 8px; margin: 0 -8px; }
|
|
630
|
-
|
|
681
|
+
/* D10 — numbered marginalia. The counter increments per highlight in
|
|
682
|
+
document order and the review column repeats the same ordinal in its
|
|
683
|
+
gutter, so the two columns read as one artifact. paint() orders the
|
|
684
|
+
thread by document position to keep the two numberings in agreement —
|
|
685
|
+
without that they diverge the moment a note is added above an earlier
|
|
686
|
+
one. The counter is also the only trace of an annotation on paper, since
|
|
687
|
+
the review column is print-suppressed. */
|
|
688
|
+
mark.hl { background: var(--hl); color: inherit; border-radius: 2px; cursor: pointer; padding: 0.1em 1px;
|
|
689
|
+
box-decoration-break: clone; -webkit-box-decoration-break: clone; counter-increment: hl; }
|
|
690
|
+
mark.hl::after { content: counter(hl); font: 600 var(--text-2xs) var(--mono); color: var(--muted-foreground);
|
|
691
|
+
vertical-align: super; margin-left: 2px; line-height: 0; }
|
|
692
|
+
mark.hl:hover { background: var(--hl-active); }
|
|
631
693
|
mark.hl.active { background: var(--hl-active); box-shadow: 0 0 0 1px var(--warning-border); }
|
|
632
694
|
|
|
633
695
|
/* Floating "comment" button (Button surface) + comment Popover */
|
|
@@ -664,20 +726,51 @@
|
|
|
664
726
|
.popover .ta { min-height: 132px; border: none; border-radius: 0; padding: 8px 10px; } .popover .ta:focus-visible { outline: none; box-shadow: none; }
|
|
665
727
|
.composer .ta { min-height: 52px; }
|
|
666
728
|
|
|
667
|
-
/* Right: annotations
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
729
|
+
/* Right: annotations as marginalia + composer + decisions.
|
|
730
|
+
D10 — the review column recedes so the document can lead. Notes lose
|
|
731
|
+
their card chrome and are separated by rule alone, each keyed by the
|
|
732
|
+
same ordinal its highlight carries in the document. */
|
|
733
|
+
.chat { border-left: 1px solid var(--border); background: var(--background); display: flex; flex-direction: column; }
|
|
734
|
+
.chat .label { padding: 22px 18px 0; margin-bottom: 4px; display: flex; align-items: center; gap: 8px;
|
|
735
|
+
font: 600 var(--text-2xs) var(--sans); letter-spacing: .09em; text-transform: uppercase; color: var(--subtle); }
|
|
736
|
+
.thread { flex: 1; overflow-y: auto; padding: 10px 18px; display: flex; flex-direction: column; gap: 0; counter-reset: note; }
|
|
671
737
|
.thread .empty { color: var(--subtle); padding: 8px 4px; }
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
.msg
|
|
675
|
-
.msg
|
|
738
|
+
.msg { position: relative; background: transparent; border: none; border-top: 1px solid var(--border);
|
|
739
|
+
border-left: 2px solid transparent; border-radius: 0; padding: 14px 0 14px 26px; box-shadow: none; cursor: default; }
|
|
740
|
+
.msg:first-child { border-top: none; }
|
|
741
|
+
.msg::before { counter-increment: note; content: counter(note); position: absolute; left: 8px; top: 15px;
|
|
742
|
+
font: 600 var(--text-2xs) var(--mono); color: var(--subtle); }
|
|
743
|
+
/* A note on another document takes no ordinal — see paint(). The marker
|
|
744
|
+
says "annotated, elsewhere" without claiming a position in a sequence
|
|
745
|
+
the reader cannot see. */
|
|
746
|
+
.msg[data-other-doc]::before { counter-increment: none; content: '·'; }
|
|
747
|
+
.msg.note { cursor: pointer; }
|
|
748
|
+
.msg.note.active { background: var(--accent); }
|
|
676
749
|
.msg .meta { display: flex; justify-content: space-between; gap: 8px; margin-bottom: 4px; align-items: center; }
|
|
677
|
-
.msg .who { color: var(--muted-foreground); font
|
|
750
|
+
.msg .who { color: var(--muted-foreground); font: 600 var(--text-2xs) var(--sans); text-transform: uppercase; letter-spacing: .07em; }
|
|
678
751
|
.msg .del { cursor: pointer; border: none; background: none; color: var(--subtle); font-size: var(--text-2xs); padding: 0 2px; } .msg .del:hover { color: var(--destructive); }
|
|
679
|
-
.msg .quote { color: var(--muted-foreground); font-size: var(--text-2xs); border-left: 2px solid var(--
|
|
680
|
-
.msg .body { white-space: pre-wrap; word-break: break-word; color: var(--
|
|
752
|
+
.msg .quote { color: var(--muted-foreground); font-size: var(--text-2xs); font-style: italic; border-left: 2px solid var(--hl-active); padding: 1px 9px; margin-bottom: 6px; max-height: 42px; overflow: hidden; }
|
|
753
|
+
.msg .body { white-space: pre-wrap; word-break: break-word; color: var(--foreground); }
|
|
754
|
+
|
|
755
|
+
/* Category as colour, carried by the two marks already in the gutter — the
|
|
756
|
+
ordinal and the label — rather than by a card edge. Lets you count the
|
|
757
|
+
issues in a full thread without reading it. `data-cat` is set in paint();
|
|
758
|
+
the category is otherwise only written as TEXT into .who, and CSS cannot
|
|
759
|
+
select on text content. */
|
|
760
|
+
/* The five categories are exactly the popover's chips — Comment, Issue,
|
|
761
|
+
Question, Suggestion, Nitpick — plus "Note", which is what a note gets
|
|
762
|
+
when no chip was clicked (openToolbar clears pendingCat on every open,
|
|
763
|
+
so the markup's initial `active` chip is not a default). Comment and
|
|
764
|
+
Note are deliberately quiet: they are the common case, and colouring
|
|
765
|
+
them would leave nothing for the categories that carry a decision. */
|
|
766
|
+
.msg[data-cat="Issue"] { border-left-color: var(--destructive); }
|
|
767
|
+
.msg[data-cat="Issue"]::before, .msg[data-cat="Issue"] .who { color: var(--destructive); }
|
|
768
|
+
.msg[data-cat="Question"] { border-left-color: var(--warning); }
|
|
769
|
+
.msg[data-cat="Question"]::before, .msg[data-cat="Question"] .who { color: var(--warning); }
|
|
770
|
+
.msg[data-cat="Suggestion"] { border-left-color: var(--primary); }
|
|
771
|
+
.msg[data-cat="Suggestion"]::before, .msg[data-cat="Suggestion"] .who { color: var(--primary-text); }
|
|
772
|
+
.msg[data-cat="Nitpick"] { border-left-color: var(--subtle); }
|
|
773
|
+
.msg[data-cat="Nitpick"]::before, .msg[data-cat="Nitpick"] .who { color: var(--subtle); }
|
|
681
774
|
|
|
682
775
|
/* Terminus: Separator + composer */
|
|
683
776
|
.composer { border-top: 1px solid var(--border); padding: 12px 14px; }
|
|
@@ -709,6 +802,22 @@
|
|
|
709
802
|
.sent .again { margin-top: 10px; }
|
|
710
803
|
.dot { width: 7px; height: 7px; border-radius: 9999px; background: currentColor; animation: pulse 1.3s ease-in-out infinite; }
|
|
711
804
|
@keyframes pulse { 0%, 100% { opacity: .3; } 50% { opacity: 1; } }
|
|
805
|
+
|
|
806
|
+
/* Docket #50 — the shell has to COLLAPSE for print, not just hide its
|
|
807
|
+
chrome. The kit's print block sets [data-kit-chrome] { display: none },
|
|
808
|
+
which hides .nav and .chat but leaves their grid TRACKS on .app, so
|
|
809
|
+
.main auto-placed into the first (248px) track and the plan printed in a
|
|
810
|
+
~151px column. .main is also the scroll container, so printed output was
|
|
811
|
+
clipped to the one screenful scrolled into view — the rest of the plan
|
|
812
|
+
did not print at all.
|
|
813
|
+
The measure cap stays: prose set to the full page width is no more
|
|
814
|
+
readable on paper than on screen. Only the centring margin goes, so the
|
|
815
|
+
column sits against the page's own margin. */
|
|
816
|
+
@media print {
|
|
817
|
+
.app { display: block; }
|
|
818
|
+
.main { overflow: visible; padding: 0; height: auto; }
|
|
819
|
+
.doc, .crumbs { margin: 0; }
|
|
820
|
+
}
|
|
712
821
|
</style>
|
|
713
822
|
</head>
|
|
714
823
|
<body>
|
|
@@ -1473,12 +1582,47 @@
|
|
|
1473
1582
|
function sendChat(){ const ta=document.getElementById('input'); const text=ta.value.trim(); if(!text)return;
|
|
1474
1583
|
annotations.push({id:nextId++,kind:'chat',quote:'',text:text,section:''}); ta.value=''; paint(); }
|
|
1475
1584
|
|
|
1585
|
+
// The thread renders in DOCUMENT order, not insertion order, because each
|
|
1586
|
+
// highlight carries a CSS counter numbered by document position and the
|
|
1587
|
+
// gutter repeats that ordinal. Left in insertion order the two numberings
|
|
1588
|
+
// disagree the moment a note is added above an earlier one.
|
|
1589
|
+
//
|
|
1590
|
+
// Sort a COPY: `annotations` is the identity store that saveNote pushes to
|
|
1591
|
+
// and the delete handler indexes into with indexOf, so reordering it in
|
|
1592
|
+
// place breaks both. And never reorder the rendered DOM instead — chat
|
|
1593
|
+
// messages have no document position and would be shuffled to the top.
|
|
1594
|
+
function threadOrder(){
|
|
1595
|
+
// Scope the mark lookup to the ACTIVE pane. In DIRMODE every doc's pane
|
|
1596
|
+
// stays in the DOM hidden, so a document-wide query happily matches a
|
|
1597
|
+
// mark in another document's pane and sorts against it.
|
|
1598
|
+
const pane=docState[activeDoc]&&docState[activeDoc].pane;
|
|
1599
|
+
const markOf=function(a){ return pane?pane.querySelector('mark.hl[data-aid="'+a.id+'"]'):null; };
|
|
1600
|
+
// Array.prototype.sort is stable, so a 0 comparison keeps chat messages
|
|
1601
|
+
// — and any note whose mark is missing — in their existing order.
|
|
1602
|
+
return annotations.slice().sort(function(x,y){
|
|
1603
|
+
if(x.kind!=='note'||y.kind!=='note') return 0;
|
|
1604
|
+
const mx=markOf(x), my=markOf(y);
|
|
1605
|
+
if(!mx||!my) return 0;
|
|
1606
|
+
return (mx.compareDocumentPosition(my)&Node.DOCUMENT_POSITION_FOLLOWING)?-1:1;
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1476
1609
|
function paint(){
|
|
1477
1610
|
const box=document.getElementById('thread'); box.innerHTML='';
|
|
1478
1611
|
document.getElementById('count').textContent=annotations.length?'('+annotations.length+')':'';
|
|
1479
1612
|
if(!annotations.length){ box.innerHTML='<div class="empty">No notes yet. Select text to comment, or send a message.</div>'; updateBadges(); updateSummary(); return; }
|
|
1480
|
-
|
|
1613
|
+
threadOrder().forEach(function(a){
|
|
1481
1614
|
const m=document.createElement('div'); m.className='msg'+(a.kind==='note'?' note':''); if(a.kind==='note')m.dataset.aid=String(a.id);
|
|
1615
|
+
// The category is otherwise written only as TEXT into .who, and CSS
|
|
1616
|
+
// cannot select on text content — this is what the colour rules key on.
|
|
1617
|
+
m.dataset.cat=(a.kind==='note'?(a.cat||'Note'):'message');
|
|
1618
|
+
// DIRMODE: the thread lists notes from EVERY document, but the in-document
|
|
1619
|
+
// ordinal counts only the visible pane — marks inside a display:none pane
|
|
1620
|
+
// do not increment a CSS counter. Numbering every row would therefore
|
|
1621
|
+
// label a note "3" in the gutter whose highlight reads "1" in the
|
|
1622
|
+
// document, which is exactly the divergence the numbering exists to
|
|
1623
|
+
// remove. Rows belonging to another document are flagged so they take no
|
|
1624
|
+
// ordinal, leaving both sequences running 1..m over the same set.
|
|
1625
|
+
if(a.kind==='note'&&a.doc&&a.doc!==activeDoc) m.dataset.otherDoc='1';
|
|
1482
1626
|
const meta=document.createElement('div'); meta.className='meta';
|
|
1483
1627
|
const who=document.createElement('span'); who.className='who'; who.textContent=a.kind==='note'?(a.cat||'Note'):'message';
|
|
1484
1628
|
const del=document.createElement('button'); del.className='del'; del.textContent='×';
|
|
@@ -1552,7 +1696,12 @@
|
|
|
1552
1696
|
if(submitting) return; submitting=true;
|
|
1553
1697
|
document.querySelectorAll('.dbar button').forEach(function(b){b.disabled=true;});
|
|
1554
1698
|
try{
|
|
1555
|
-
|
|
1699
|
+
// Send the thread in the SAME order the reviewer saw it. The rendered
|
|
1700
|
+
// thread is ordered by document position and each note is numbered in
|
|
1701
|
+
// the gutter; posting the raw insertion-order array would hand the
|
|
1702
|
+
// agent a different numbering than the one on screen, so "note 1" in a
|
|
1703
|
+
// conversation would mean two different notes.
|
|
1704
|
+
const r=await fetch('/api/feedback',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({decision:decision,thread:threadOrder()})});
|
|
1556
1705
|
if(!r.ok) throw new Error('status '+r.status);
|
|
1557
1706
|
hideApiError();
|
|
1558
1707
|
decisionSent=true; // a terminal decision makes the server exit —
|