mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-10 01:09:43 +02:00
feat(tabs): draw lineage lines as one tree for the selected tab's family
Lineage lines used to draw one glowing dashed bezier per parent/child pair, hanging below the tab strip, for every family at once. With a parent on row 3 of a wrapped strip and ten children below it, the curves crossed every lower row's tab names and ran through the terminal text. - One rounded orthogonal tree per spawning tab: every route starts at the parent, so siblings share a trunk. 1.5px, solid, 1px dark outline; only a working child's branch is dashed. - Routes run only through the gaps between tab rows, joined by a spine left of every row (computeLineageTree/computeLineageRows in constants.js), so they never cross a tab or reach the terminal. The vertical rail gets the same tree on its existing left track. - Only the selected tab's family is drawn (what it spawned, plus its parent and siblings). Selection redraws, and the strip's size transitionend redraws once more, since the active tab widens for ~150ms after the selection redraw. - The routing room is reserved by .session-tabs.lineage-tree, keyed on whether any lineage exists, never on the selection, so a tab switch never resizes the header or the PTY. - Colours stay per spawning tab, now claimed in strip order for every family so selection order never decides who gets the skin blue. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+68
-62
@@ -596,6 +596,22 @@ body {
|
||||
max-height: var(--tab-strip-max-height, 40vh);
|
||||
}
|
||||
|
||||
/* Lineage routing room (`_syncLineageGutter()` in session-lineage.js, geometry in
|
||||
computeLineageTree, constants.js). Present only while some tab has spawned
|
||||
another, and never toggled by the selection, so switching tabs cannot resize
|
||||
the header (and with it the terminal). The bottom padding is the last row's
|
||||
gap; a wrapped strip also widens the row gap the routes run in, and opens the
|
||||
spine channel on the left that joins one row's gap to another's. */
|
||||
.session-tabs.lineage-tree {
|
||||
padding-bottom: 14px;
|
||||
}
|
||||
|
||||
.session-tabs.lineage-tree.tabs-two-rows,
|
||||
.session-tabs.lineage-tree.tabs-auto-wrap {
|
||||
row-gap: 12px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.session-tabs::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 0;
|
||||
@@ -1681,9 +1697,10 @@ html[data-line-anim="packet"] .connection-line.line-enter {
|
||||
|
||||
⚠ The 100% frame deliberately omits `opacity`, which makes the browser take
|
||||
the endpoint from the element's own computed value: a subagent line rests at
|
||||
0.9, a lineage line at 0.72, and a WORKING lineage line at 0.95. Pinning 0.9
|
||||
here - as `line-enter-fade` above still does - lands every lineage line on the
|
||||
wrong opacity and snaps it when the class comes off. */
|
||||
0.9, a lineage line at 1 (its `.lineage-family` group carries the
|
||||
translucency), a proxied one at 0.5. Pinning 0.9 here - as `line-enter-fade`
|
||||
above still does - lands every lineage line on the wrong opacity and snaps it
|
||||
when the class comes off. */
|
||||
html[data-line-anim="blur"] .connection-line.line-enter {
|
||||
animation-name: line-enter-blur;
|
||||
animation-duration: calc(380ms * var(--anim-enter-scale, 1));
|
||||
@@ -10665,112 +10682,101 @@ kbd {
|
||||
}
|
||||
|
||||
/* ===== Session lineage lines (tab → tab it spawned, session-lineage.js) =====
|
||||
Deliberately quieter and thinner than the subagent lines above so the two
|
||||
layers read as different things in the same SVG.
|
||||
One quiet orthogonal tree per spawning tab, drawn only for the SELECTED tab's
|
||||
family and routed through the gaps between tab rows (computeLineageTree in
|
||||
constants.js). The routing room itself is reserved on the strip by
|
||||
`.session-tabs.lineage-tree` near the top of this file.
|
||||
|
||||
⚠ QUIET ON PURPOSE (owner call 2026-10-06, "B2"): 1.5px, solid, rounded
|
||||
corners and a 1px dark outline in place of the old double glow. The old lines
|
||||
were glowing dashed beziers that hung into the terminal, and with ten children
|
||||
across a wrapped strip they read as a tangle. The lines no longer cross text,
|
||||
so they no longer need a glow to survive it. Dashes now MEAN something: only a
|
||||
working child's route is dashed.
|
||||
|
||||
Colour: every rule reads --lineage-color, which session-lineage.js sets INLINE
|
||||
per line from the CodemanLineage.COLORS palette (per child, first-seen order,
|
||||
owner call 2026-08-15: several connected tabs must get several colours). The
|
||||
FIRST line gets no override, so it falls through to --session-blue, which EVERY
|
||||
skin block already defines and tunes for its own background; a lone arc therefore
|
||||
still renders the skin-aware blue that shipped in 1.18.2. Do not add a per-skin
|
||||
`.lineage-line` override inside the html:not([data-skin="og"]) block: a bare
|
||||
class rule in there resolves to (0,2,1) and would outrank this one from a
|
||||
surprising place.
|
||||
per path and dot from the CodemanLineage.COLORS palette (per SPAWNING tab,
|
||||
first-seen order). The first spawning tab gets no override and falls through to
|
||||
--session-blue, which EVERY skin block already defines and tunes for its own
|
||||
background. Do not add a per-skin `.lineage-line` override inside the
|
||||
html:not([data-skin="og"]) block: a bare class rule in there resolves to
|
||||
(0,2,1) and would outrank this one from a surprising place.
|
||||
|
||||
Opacity lives on the family GROUP (`.lineage-family`), not on the paths: a
|
||||
parent's routes overlap exactly along the trunk, and translucent paths would
|
||||
stack there into a brighter line than the branches. */
|
||||
.lineage-family {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
⚠ BLUE, NOT THE VIOLET THIS SHIPPED WITH (owner call, 2026-08-14: "make these
|
||||
lines in blue that they are better visible"). Violet sits close to the terminal's
|
||||
own dim foreground and lost contrast the moment it crossed text. Hue therefore no
|
||||
longer separates this layer from the subagent lines, so the separation rests
|
||||
entirely on SHAPE (this one hangs under the strip and never reaches a window),
|
||||
weight and dash: keep those differences intact. Per skin the two are not even the
|
||||
same blue, since --session-blue is tuned per palette while the subagent rule
|
||||
hardcodes #3b82f6. */
|
||||
/* ⚠ QUIETER THAN THE SUBAGENT LINES, NOT INVISIBLE. The first cut ran 2px at 0.55
|
||||
with a single 5px glow, which reads on a design mock and disappears on a real
|
||||
1080p desktop: a faint thread over terminal text, exactly what it is drawn on
|
||||
top of. The weight stays UNDER the subagent lines' 3px so the two layers still
|
||||
separate, and the second, wider glow is what buys the contrast instead: it lifts
|
||||
the line off the terminal without thickening it. Dashes scale with the stroke
|
||||
(4 4 on a 2.5px line reads as a dotted smudge), and `lineage-flow` marches by
|
||||
exactly two dash cycles, so it has to move with them. */
|
||||
.connection-line.lineage-line {
|
||||
stroke: var(--lineage-color, var(--session-blue, #2b8fd9));
|
||||
stroke-width: 2.5;
|
||||
stroke-dasharray: 5 5;
|
||||
stroke-width: 1.5;
|
||||
stroke-dasharray: none;
|
||||
stroke-linecap: round;
|
||||
opacity: 0.72;
|
||||
--line-glow: drop-shadow(0 0 2px rgba(0, 0, 0, 0.7))
|
||||
drop-shadow(0 0 5px var(--lineage-color, var(--session-blue, #2b8fd9)))
|
||||
drop-shadow(0 0 11px var(--lineage-color, var(--session-blue, #2b8fd9)));
|
||||
stroke-linejoin: round;
|
||||
opacity: 1;
|
||||
--line-glow: drop-shadow(0 0 1px rgba(0, 0, 0, 0.9));
|
||||
filter: var(--line-glow);
|
||||
}
|
||||
|
||||
/* ⚠ OUTSIDE the reduced-motion block below on purpose. A working child is the case
|
||||
the line exists to signal, and pairing the brightness with the marching dashes
|
||||
left every worker's arc at the resting 0.72 for anyone who turns motion off. */
|
||||
/* A working child's route is dashed even with motion turned off; the march
|
||||
below is the extra. Routes start at the PARENT, so dashes on the shared trunk
|
||||
stay in phase between working siblings. */
|
||||
.connection-line.lineage-line--working {
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
.connection-line.lineage-line:hover {
|
||||
opacity: 1;
|
||||
stroke-width: 3;
|
||||
stroke-dasharray: 4 3;
|
||||
}
|
||||
|
||||
.lineage-line-dot {
|
||||
fill: var(--lineage-color, var(--session-blue, #2b8fd9));
|
||||
opacity: 0.85;
|
||||
filter: drop-shadow(0 0 4px var(--lineage-color, var(--session-blue, #2b8fd9)))
|
||||
drop-shadow(0 0 9px var(--lineage-color, var(--session-blue, #2b8fd9)));
|
||||
filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.9));
|
||||
}
|
||||
|
||||
/* An arc re-anchored to a collapsed group's header (its row is hidden): quieter
|
||||
/* A line re-anchored to a collapsed group's header (its row is hidden): quieter
|
||||
and dotted, so it reads as "somewhere in there" rather than a real row. */
|
||||
.connection-line.lineage-line--proxied {
|
||||
stroke-dasharray: 2 7;
|
||||
opacity: 0.46;
|
||||
stroke-dasharray: 1 5;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.connection-line.lineage-line--proxied.lineage-line--working {
|
||||
opacity: 0.62;
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
.lineage-line-dot.lineage-line-dot--proxied {
|
||||
opacity: 0.46;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* The child end marches while that worker is actually working, so the line
|
||||
itself carries the signal. Motion is opt-out-able at the OS level. */
|
||||
/* The working route marches from parent to child, so the line itself carries
|
||||
the signal. Motion is opt-out-able at the OS level. */
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.connection-line.lineage-line--working {
|
||||
animation: lineage-flow 1.1s linear infinite;
|
||||
}
|
||||
|
||||
.lineage-line-dot--working {
|
||||
opacity: 1;
|
||||
animation: lineage-dot-pulse 1.4s ease-in-out infinite;
|
||||
}
|
||||
}
|
||||
|
||||
/* Two full dash cycles, so the march loops seamlessly. Tied to `stroke-dasharray`
|
||||
above: at `5 5` the cycle is 10px, so this is -20 rather than the -16 that
|
||||
matched the old `4 4`. Leaving them out of step makes the dashes jump once per
|
||||
iteration. */
|
||||
/* Two full dash cycles, so the march loops seamlessly. Tied to the working
|
||||
`stroke-dasharray` above: at `4 3` the cycle is 7px, so this is -14. Leaving
|
||||
them out of step makes the dashes jump once per iteration. */
|
||||
@keyframes lineage-flow {
|
||||
to {
|
||||
stroke-dashoffset: -20;
|
||||
stroke-dashoffset: -14;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tied to the resting r="2.5" session-lineage.js sets on the dot. */
|
||||
@keyframes lineage-dot-pulse {
|
||||
0%, 100% {
|
||||
opacity: 0.75;
|
||||
r: 3.5;
|
||||
r: 2.5;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
r: 4.5;
|
||||
r: 3.3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user