mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 16:59:43 +02:00
fix(tabs): keep the in-tiles tab marker in the ledger layout
#561 marks the tab of every tiled session with an inset underline (`.session-tab.in-tiles:not(.active) { box-shadow: inset 0 -2px 0 ... }`, 0,3,0). #538's ledger draws each cell's status bar as an inset box-shadow too (`.session-tabs-host > .session-tabs.tabs-ledger > .session-tab`, 0,4,0), so in the ledger the bar replaced the marker and nothing in the strip said which sessions were on the grid. The ledger now restates the marker beside its bar for a tiled, inactive cell; the bar still follows --ledger-bar, so needs-you, waiting and exited cells keep their colours. By state, by case, classic and the side rail paint no cell shadow of their own and already showed it. The test scans every arrangement-scoped rule that paints a tab cell's shadow and requires an .in-tiles variant that keeps both, so a new arrangement cannot drop it again. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21005,6 +21005,13 @@ html[data-session-list='sidebar'][data-sidebar='collapsed'] .session-sidebar .ta
|
||||
--ledger-bar: var(--text-muted);
|
||||
}
|
||||
|
||||
/* A tiled session's cell keeps the tile grid's underline (.in-tiles) beside
|
||||
its status bar: both are inset shadows, and the bar's rule above would
|
||||
otherwise replace the marker. */
|
||||
.session-tabs-host > .session-tabs.tabs-ledger > .session-tab.in-tiles:not(.active) {
|
||||
box-shadow: inset 3px 0 0 var(--ledger-bar), inset 0 -2px 0 color-mix(in srgb, var(--accent, #4a9eff) 60%, transparent);
|
||||
}
|
||||
|
||||
/* The active cell has to win against a grid of look-alike cells. The skin's
|
||||
active rule (1px accent border, no shadow, both !important) is too quiet
|
||||
here, so an inset second pixel doubles the border without resizing the
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import postcss, { type AtRule } from 'postcss';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import {
|
||||
FakeEl,
|
||||
@@ -451,4 +452,46 @@ describe('the tab marker', () => {
|
||||
const css = readFileSync(resolve(import.meta.dirname, '../src/web/public/styles.css'), 'utf8');
|
||||
expect(css).toMatch(/\.session-tab\.in-tiles/);
|
||||
});
|
||||
|
||||
it('every tab arrangement that draws its own cell shadow keeps the marker in it', () => {
|
||||
// The marker is an inset box-shadow, so an arrangement that paints its
|
||||
// cells with a box-shadow of its own (the ledger's status bar, #538)
|
||||
// replaces it unless it restates the marker alongside its own shadow.
|
||||
const css = readFileSync(resolve(import.meta.dirname, '../src/web/public/styles.css'), 'utf8');
|
||||
type Found = { selector: string; shadow: string; media: string };
|
||||
const shadows: Found[] = [];
|
||||
postcss.parse(css).walkRules((rule) => {
|
||||
let shadow = '';
|
||||
rule.walkDecls('box-shadow', (d) => {
|
||||
shadow = d.value;
|
||||
});
|
||||
if (!shadow) return;
|
||||
const media: string[] = [];
|
||||
for (let p = rule.parent; p && p.type !== 'root'; p = p.parent) {
|
||||
if (p.type === 'atrule') media.push((p as AtRule).params);
|
||||
}
|
||||
for (const selector of rule.selectors) shadows.push({ selector, shadow, media: media.join(' ') });
|
||||
});
|
||||
const marker = shadows.find((r) => r.selector === '.session-tab.in-tiles:not(.active)');
|
||||
expect(marker?.shadow).toMatch(/^inset 0 -2px 0 /);
|
||||
// A whole tab cell at rest, scoped to an arrangement: not a pseudo-element,
|
||||
// not a passing state (hover, press, drag) and not the active tab, which
|
||||
// the marker skips anyway.
|
||||
const cells = shadows.filter((r) => {
|
||||
const last = r.selector.split(/\s*[\s>+~]\s*/).pop()!;
|
||||
return (
|
||||
/^\.session-tab(?![\w-])/.test(last) &&
|
||||
r.selector !== last &&
|
||||
!/::|:hover|:active|\.active(?![\w-])|drag-over|in-tiles/.test(last.replace(':not(.active)', ''))
|
||||
);
|
||||
});
|
||||
expect(cells.map((r) => r.selector)).toContain('.session-tabs-host > .session-tabs.tabs-ledger > .session-tab');
|
||||
const missing = cells.filter((cell) => {
|
||||
const tiled = shadows.find(
|
||||
(r) => r.selector === `${cell.selector}.in-tiles:not(.active)` && r.media === cell.media
|
||||
);
|
||||
return !tiled || !tiled.shadow.includes(cell.shadow) || !tiled.shadow.includes(marker!.shadow);
|
||||
});
|
||||
expect(missing.map((r) => r.selector)).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user