ZDDC/browse/css/tree.css
ZDDC bbb75a87af chore(headers): standardize across all 7 tools
Bring every tool's header in line with archive's pattern:

  [logo] [title] [version] [Add Local Directory] [⟳] ............... [◐] [?]
  ------------- header-left ---------------       ----- header-right -

Changes per tool:

* browse: rename "Select Directory" → "Add Local Directory"; add the
  red-non-stable wrap to the build label (was missing); add a help
  panel + bundle shared/help.js.

* classifier: rename selectDirectoryBtn → addDirectoryBtn,
  refreshBtn → refreshHeaderBtn for consistency. Update all JS
  callers and welcome-screen copy to the new label.

* mdedit: same id rename. Move the previously-in-pane refresh
  button into the header. Stop renaming the dir button to
  "Directory: <name>" once a folder is loaded — instead use the
  shared btn--subtle variant to de-emphasize while keeping the
  standard label.

* transmittal: convert non-standard <div class="app-header"> with
  spacer/icons containers to <header class="app-header"> with the
  canonical header-left/header-right pair. Move the publish split-
  button into header-left (Transmittal-specific primary action).
  Remove dead .app-header__spacer/__icons/header-icon-btn CSS now
  that nothing references those classes.

* landing, form: add help-btn + help-panel + bundle shared/help.js.
  Each panel is tool-specific (project picker docs for landing,
  schema-driven form docs for form).

Cross-cutting:

* shared/base.css: promote .btn--subtle from browse/css/tree.css
  so any tool with an online mode can de-emphasize Add Local
  Directory consistently.

Verified all 7 tools in headless Chromium: header structure correct,
build label red on non-stable cuts, help panel opens + closes via
button + Esc.
2026-05-03 22:17:02 -05:00

284 lines
6.2 KiB
CSS

/* Toolbar above the listing */
.browse-root {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
overflow: hidden;
}
.browse-table-wrap {
flex: 1;
overflow: auto;
min-height: 0;
}
.toolbar {
display: flex;
align-items: center;
gap: 1rem;
padding: 0.6rem 1rem;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
flex-shrink: 0;
flex-wrap: wrap;
}
/* Breadcrumb path. The root node is a 🏠 link to "/" (online) or
the FS handle name (offline). Each segment is a clickable link in
server mode that re-navigates the browser; in FS-API mode they
render as plain spans because we don't keep ancestor handles. */
.breadcrumbs {
flex: 1;
min-width: 0;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
font-family: Consolas, Monaco, monospace;
font-size: 0.9rem;
color: var(--text-muted);
padding: 0.1rem 0;
/* Hide the scrollbar but keep horizontal scroll for very deep paths */
scrollbar-width: thin;
}
.breadcrumbs .bc-link {
color: var(--primary);
text-decoration: none;
padding: 0.1rem 0.25rem;
border-radius: 3px;
}
.breadcrumbs .bc-link:hover {
background: var(--bg-hover, rgba(0,0,0,0.05));
text-decoration: underline;
}
.breadcrumbs .bc-link--current {
color: var(--text);
font-weight: 500;
cursor: default;
}
.breadcrumbs .bc-link--current:hover {
background: transparent;
text-decoration: none;
}
.breadcrumbs .bc-sep {
color: var(--text-muted);
margin: 0 0.05rem;
}
.breadcrumbs .bc-root {
display: inline-flex;
align-items: center;
line-height: 1;
}
.bc-home-icon {
width: 1rem;
height: 1rem;
display: block;
color: currentColor;
}
.toolbar__count {
font-size: 0.8rem;
color: var(--text-muted);
white-space: nowrap;
}
/* Auto-filter rows in <thead>. Two rows — one targets file rows
(📄 icon, with file-name + ext inputs), one targets folder rows
(📁 icon, with folder-name input). The icons make it visually
obvious which row controls which kind of filter. The rows are
non-sticky (only the sortable header row sticks) — keeps the
stack-positioning math out of the picture and accepts that
filters scroll out of view on long lists. */
.browse-table thead .filter-row th {
position: static;
padding: 0.25rem 0.6rem;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
cursor: default;
font-weight: normal;
z-index: 0;
}
.browse-table thead .filter-row th:hover {
background: var(--bg-secondary);
}
.filter-row__icon {
display: inline-block;
width: 1.2rem;
text-align: center;
margin-right: 0.3rem;
vertical-align: middle;
font-size: 0.95rem;
color: var(--text-muted);
}
.column-filter {
width: calc(100% - 1.5rem);
padding: 0.2rem 0.4rem;
border: 1px solid var(--border);
border-radius: 3px;
background: var(--bg);
color: var(--text);
font-size: 0.8rem;
font-family: Consolas, Monaco, monospace;
box-sizing: border-box;
}
.filter-row th.col-name .column-filter {
width: calc(100% - 1.7rem); /* leave space for the icon */
}
.column-filter:focus {
outline: 1px solid var(--primary);
outline-offset: -1px;
}
/* Table — folders + files in a tree */
.browse-table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
background: var(--bg);
/* No flex:1 — tables don't reliably distribute extra height across
rows the way flex columns do. With few rows we'd get tall rows
that shrink as more children are loaded. The wrap div handles
scrolling instead. */
}
.browse-table tbody tr {
/* Pin rows to a deterministic height so table layout never
redistributes vertical space across them. */
line-height: 1.4;
}
.browse-table thead th {
position: sticky;
top: 0;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
text-align: left;
padding: 0.5rem 0.75rem;
font-weight: 600;
color: var(--text);
user-select: none;
z-index: 1;
}
.browse-table th.sortable {
cursor: pointer;
}
.browse-table th.sortable:hover {
background: var(--bg-hover, #e8e8e8);
}
.sort-arrow {
display: inline-block;
width: 0.7rem;
color: var(--text-muted);
font-size: 0.7rem;
margin-left: 0.2rem;
}
.browse-table th.sort-asc .sort-arrow::after { content: "▲"; color: var(--text); }
.browse-table th.sort-desc .sort-arrow::after { content: "▼"; color: var(--text); }
.browse-table tbody td {
padding: 0.3rem 0.75rem;
border-bottom: 1px solid var(--border);
vertical-align: middle;
}
.browse-table tbody tr:hover {
background: var(--bg-hover, #f6faff);
}
/* Tree-row — name cell with indent + chevron */
.tree-name {
display: flex;
align-items: center;
gap: 0.4rem;
min-width: 0;
}
.tree-name__indent {
flex: 0 0 auto;
}
.tree-name__chevron {
width: 1rem;
text-align: center;
color: var(--text-muted);
cursor: pointer;
user-select: none;
flex: 0 0 1rem;
line-height: 1;
}
.tree-name__chevron--leaf { visibility: hidden; }
.tree-name__chevron::before { content: "▶"; font-size: 0.65rem; }
.tree-row.expanded > td .tree-name__chevron::before { content: "▼"; }
.tree-name__icon {
flex: 0 0 1.1rem;
text-align: center;
color: var(--text-muted);
font-size: 1rem;
line-height: 1;
}
.tree-name__label {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--text);
}
.tree-name__label.is-folder {
font-weight: 500;
}
.tree-name__label.is-file {
cursor: pointer;
color: var(--primary);
text-decoration: none;
}
.tree-name__label.is-file:hover {
text-decoration: underline;
}
/* Numeric columns right-aligned */
.col-size, .col-date {
text-align: right;
font-variant-numeric: tabular-nums;
white-space: nowrap;
color: var(--text-muted);
}
.col-ext {
color: var(--text-muted);
font-family: Consolas, Monaco, monospace;
font-size: 0.85rem;
}
/* Loading row */
.tree-row--loading td {
color: var(--text-muted);
font-style: italic;
padding: 0.5rem 1rem 0.5rem calc(0.75rem + 2.4rem);
}