feat(browse): make virtual tree rows visually distinct

Folders the cascade declares but disk doesn't carry (working/,
staging/, reviewing/, mdl/, the canonical folders before they're
materialised) previously got just opacity:0.65 + an "(empty)" hint —
easy to miss, especially next to dimmed-but-real items.

Now they read as placeholders at a glance:

- Dashed left rail (2px, accent-muted) inside the row gutter.
- Italic label in muted text color.
- Lucide icon switches to outline-only (fill:none + stroke:currentColor)
  so virtual folders look sketched, not filled.
- "(empty)" hint italic + accent-muted to match the rail.
- Selected virtual row keeps the rail but switches it to the
  selection accent so "selected + placeholder" reads as both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZDDC 2026-05-15 16:40:41 -05:00
parent 690d185dc2
commit c2423f8873

View file

@ -496,19 +496,52 @@ body {
word-break: break-all; word-break: break-all;
} }
/* Virtual rows: synthesized client-side for folders that aren't on /* Virtual rows: synthesized for folders/files declared by the
disk yet (canonical project folders). Rendered muted so the user cascade but absent from disk. The visual language reads as
reads them as "available but empty" rather than ordinary entries. "expected, not yet materialized" italic label, muted accent
Hover/select states still apply; the hint sits to the right of the color, dashed left rail, and an outlined icon. Hover/select
label. */ chrome still applies on top; the dashed rail sits inside the row
.tree-row--virtual .tree-name__icon, so it doesn't fight padding-left indentation. */
.tree-row--virtual .tree-name__label { .tree-row--virtual {
opacity: 0.65; box-shadow: inset 2px 0 0 0 transparent;
position: relative;
} }
.tree-row--virtual::before {
content: '';
position: absolute;
top: 4px;
bottom: 4px;
left: 2px;
border-left: 2px dashed var(--accent-muted, #8aa4cc);
pointer-events: none;
}
.tree-row--virtual .tree-name__label {
font-style: italic;
color: var(--text-muted, #6b7280);
}
.tree-row--virtual .tree-name__icon {
/* Hollow out the filled Lucide glyph: reduce fill opacity so
the icon reads as an outline-only sketch the conventional
"placeholder, not actual" cue across UI systems. */
opacity: 0.5;
}
.tree-row--virtual .tree-name__icon svg {
fill: none;
stroke: currentColor;
stroke-width: 1.5;
stroke-linecap: round;
stroke-linejoin: round;
}
.tree-row--virtual.is-selected::before {
/* Selected virtual row: rail brightens to selection accent so the
row reads as both selected and placeholder. */
border-left-color: var(--accent, #2868c8);
}
.tree-name__hint { .tree-name__hint {
margin-left: 0.5rem; margin-left: 0.5rem;
font-size: 0.78rem; font-size: 0.78rem;
color: var(--text-muted); color: var(--accent-muted, #8aa4cc);
font-style: italic; font-style: italic;
} }