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;
}
/* Virtual rows: synthesized client-side for folders that aren't on
disk yet (canonical project folders). Rendered muted so the user
reads them as "available but empty" rather than ordinary entries.
Hover/select states still apply; the hint sits to the right of the
label. */
.tree-row--virtual .tree-name__icon,
.tree-row--virtual .tree-name__label {
opacity: 0.65;
/* Virtual rows: synthesized for folders/files declared by the
cascade but absent from disk. The visual language reads as
"expected, not yet materialized" italic label, muted accent
color, dashed left rail, and an outlined icon. Hover/select
chrome still applies on top; the dashed rail sits inside the row
so it doesn't fight padding-left indentation. */
.tree-row--virtual {
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 {
margin-left: 0.5rem;
font-size: 0.78rem;
color: var(--text-muted);
color: var(--accent-muted, #8aa4cc);
font-style: italic;
}