fix(tasks-ui): restore the scoped preflight subset and return the page to stock shadcn

Real-device report: the Tasks page "looks like it has no CSS at all, with
blurry borders". Two hypotheses were falsified before the real cause surfaced:
the sheet IS delivered (the chunk carries both the CSS and its injector, and
the bundle route serves it) and the cascade is NOT the problem (with the real
host stylesheet plus the real compiled sheet, a controlled sample keeps our
padding, radius, border, background and font; host CSS-module selectors only
match host nodes, and its global element rules are element-level specificity).

The cause is what we deliberately excluded: PREFLIGHT is also what normalizes
form controls. Measured on a plugin icon button that sets only an icon size,
the UA chrome survived as padding 1px 6px, border 2px outset, background
ButtonFace (#efefef) and a 13.33px system font - gray beveled buttons with a
soft double border, i.e. "no CSS".

- ui/theme.css: a .dsw-tasks-scoped preflight subset (zero margins/padding,
  border width+style, background, appearance none, inherit color/spacing,
  cursor rules) inside @layer base, written with :where() so it stays at
  (0,1,0) and utilities in the later layer still win; plus an explicit
  "@layer theme, base, components, utilities;" statement - layer order follows
  first appearance, and without it the utilities import registered utilities
  BEFORE base, so the reset beat rounded-md (measured: square buttons). The
  built chunk's order was re-checked: properties, theme, base, components,
  utilities.
- Stock shadcn restyle: static cards carry stock shadow-xs/shadow-sm again
  (floating layers keep shadow-lg), body/meta text is text-sm/text-xs instead
  of 11-13px, status uses stock Badge variants, controls use stock Button
  variants and sizes, row actions appear on hover/focus while staying keyboard
  reachable, node cards are 176x68 with shadow-xs.
- bandColsFor budgets columns at the readability floor (width / 0.78) instead
  of 1:1, so a 360px panel keeps two columns; FIT_MIN_SCALE moved into the
  layout module as GRAPH_FIT_MIN_SCALE so the fit and the column budget share
  one constant.
- Guards: tests/ui-foundation.spec.ts pins the scoped reset declarations and
  the layer-order statement; tests/ui-shadows.spec.ts now allows shadow-xs and
  shadow-sm on static panels and bans md and above; the theme guard's inert
  paint rule forced the reset to spell border-width/border-style instead of the
  "border: 0 solid" shorthand.

Verified: typecheck and lint clean; 136 files / 1505 tests; build with the
core/chunk bundle guards; mount e2e 7/7 (mount 444ms, longtasks 2 of 8); the
visual mirror page (real host stylesheet + real components) re-shot at 360 and
720 for graph, tree and the 77-node stress fixture.
This commit is contained in:
Menghuan1918
2026-09-27 22:18:19 +08:00
parent e94da2d4e4
commit d1a9d520ae
14 changed files with 318 additions and 193 deletions
+16 -4
View File
@@ -15,13 +15,20 @@ import { tasksEdges } from './tasks-model.ts'
/** Geometry constants of the canvas (px, pre-scale). Metrics follow the
* approved Variant-D mockup, whose design target is the NARROW native right
* sidebar (~360px): a 132px card fits two per row plus the gutter. */
export const GRAPH_NODE_W = 150
export const GRAPH_NODE_W = 176
/** Two title lines plus the mono meta line: the card clamps long agent names
* to two lines instead of ellipsizing them to a couple of characters, so the
* reserved height covers that (see NODE_TITLE in TasksGraph.tsx). */
export const GRAPH_NODE_H = 60
export const GRAPH_NODE_H = 68
/** Extra height of an agent node carrying a live line. */
export const GRAPH_LIVE_H = 14
/**
* The readability floor the fit honours: the canvas may shrink to this scale
* before the layout starts wrapping (TasksGraph's fit and {@link bandColsFor}'s
* column budget must agree, so the constant lives here, next to the geometry).
*/
export const GRAPH_FIT_MIN_SCALE = 0.78
export const GRAPH_GAP_X = 28
export const GRAPH_GAP_Y = 53
export const GRAPH_PAD = 16
@@ -161,8 +168,13 @@ export function layoutTasksGraph(nodes: readonly TasksNode[], options: LayoutOpt
* canvas keeps its readability floor instead of shrinking: at 360px two cards
* per row, at 720px four.
*/
export function bandColsFor(containerWidth: number): number {
const usable = containerWidth - GRAPH_PAD * 2
export function bandColsFor(containerWidth: number, minScale = GRAPH_FIT_MIN_SCALE): number {
// Columns are budgeted at the readability floor, not at 1:1: the fit may
// scale the canvas DOWN to `minScale` and still be readable, so a narrow
// panel keeps two columns (scaled ~0.86 at 360px) instead of degenerating
// into a one-card-per-row ladder. `layoutTasksGraphForWidth` re-checks the
// real width against the same budget, so a too-wide pick is still narrowed.
const usable = (containerWidth <= 0 ? 360 : containerWidth) / minScale - GRAPH_PAD * 2
const cols = Math.floor((usable + GRAPH_GAP_X) / (GRAPH_NODE_W + GRAPH_GAP_X))
return Math.min(4, Math.max(1, cols))
}