console: declutter the pipeline sidebar

Five fixes to the confusing nav:

- The two per-row numbers had no key. The AI's share now reads "N with AI" in
  quiet grey, so it stops competing with the pink "needs you" badge.
- Removed the outline node circles (and the rail line they hung on) — with one
  queue per group they read as empty checkboxes, not a pipeline. Rows now align
  flush and the active queue is shown by its tint alone.
- Dropped the section-header count that just repeated the single row beneath it.
- Lightened and tightened the group labels so the queues lead, not the headers.
- Closed now matches a queue row — same padding, hover and count badge — while
  staying a foldable disclosure.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Yashas 2026-09-09 16:44:10 +05:30
parent f31d6cb866
commit 90290a8b37
2 changed files with 35 additions and 85 deletions

View File

@ -86,14 +86,15 @@
margin: 0 0 var(--sp-3); margin: 0 0 var(--sp-3);
padding: 0 var(--sp-3); padding: 0 var(--sp-3);
font-size: var(--fs-3xs); font-size: var(--fs-3xs);
font-weight: var(--fw-semi); font-weight: 400;
letter-spacing: 0.1em; letter-spacing: 0.04em;
text-transform: uppercase; text-transform: uppercase;
color: var(--zk-grey); color: var(--zk-grey);
opacity: 0.85;
} }
.shell__group + .shell__group { .shell__group + .shell__group {
margin-top: var(--sp-7); margin-top: var(--sp-5);
} }
/* The stage list is drawn as a rail with a node per state: the sidebar should /* The stage list is drawn as a rail with a node per state: the sidebar should
@ -105,16 +106,6 @@
gap: var(--sp-1); gap: var(--sp-1);
} }
.shell__stages::before {
content: '';
position: absolute;
left: 18px;
top: 20px;
bottom: 20px;
width: 2px;
border-radius: var(--r-pill);
background: linear-gradient(180deg, var(--zk-line) 0%, var(--zk-line-soft) 100%);
}
.shell__stage { .shell__stage {
position: relative; position: relative;
@ -122,7 +113,7 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
gap: var(--sp-2); gap: var(--sp-2);
padding: var(--sp-3) var(--sp-3) var(--sp-3) 40px; padding: 9px 12px;
border-radius: var(--r-md); border-radius: var(--r-md);
text-decoration: none; text-decoration: none;
color: var(--zk-muted); color: var(--zk-muted);
@ -130,41 +121,17 @@
transition: background var(--t-fast), color var(--t-fast), transform var(--t-fast); transition: background var(--t-fast), color var(--t-fast), transform var(--t-fast);
} }
.shell__stage::before {
content: '';
position: absolute;
left: 13px;
top: 50%;
width: 12px;
height: 12px;
margin-top: -6px;
border-radius: 50%;
background: var(--zk-white);
border: 2px solid var(--zk-line);
transition: border-color var(--t-fast), background var(--t-fast), box-shadow var(--t-fast);
}
.shell__stage:hover { .shell__stage:hover {
background: var(--zk-tint); background: var(--zk-tint);
color: var(--zk-ink); color: var(--zk-ink);
} }
.shell__stage:hover::before {
border-color: var(--zk-blue-light);
}
.shell__stage.is-active { .shell__stage.is-active {
background: var(--zk-tint-blue); background: var(--zk-tint-blue);
color: var(--zk-blue-dark); color: var(--zk-blue-dark);
font-weight: 500; font-weight: 500;
} }
.shell__stage.is-active::before {
background: var(--zk-blue);
border-color: var(--zk-blue);
box-shadow: 0 0 0 4px rgba(33, 103, 174, 0.14);
}
/* Queues where the machine stops and a person decides. */ /* Queues where the machine stops and a person decides. */
.shell__stage.is-needed .shell__stagename { .shell__stage.is-needed .shell__stagename {
font-weight: 500; font-weight: 500;
@ -292,12 +259,7 @@
font-style: normal; font-style: normal;
font-size: var(--fs-2xs); font-size: var(--fs-2xs);
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
color: var(--zk-blue-mid); color: var(--zk-grey);
}
.shell__auto::after {
content: '·';
margin-left: 5px;
color: var(--zk-line);
} }
/* A renewal inside a week sitting in this queue. */ /* A renewal inside a week sitting in this queue. */
@ -310,7 +272,6 @@
} }
.shell__stage.is-empty { color: var(--zk-grey); } .shell__stage.is-empty { color: var(--zk-grey); }
.shell__stage.is-empty::before { border-color: var(--zk-line-soft); }
.shell__stage.is-empty.is-needed .shell__stagename { color: var(--zk-muted); font-weight: 400; } .shell__stage.is-empty.is-needed .shell__stagename { color: var(--zk-muted); font-weight: 400; }
/* The one number worth carrying on a group heading: everything this role has /* The one number worth carrying on a group heading: everything this role has
@ -322,51 +283,46 @@
gap: 8px; gap: 8px;
} }
.shell__grouptally {
font-size: var(--fs-2xs);
font-weight: 600;
letter-spacing: 0;
font-variant-numeric: tabular-nums;
color: var(--zk-white);
background: var(--zk-blue);
border-radius: var(--r-pill);
padding: 1px 7px;
}
/* Closed, folded /* Closed, folded
Three of the twelve queues, opened about once a week. */ Three of the twelve queues, opened about once a week. */
.shell__closed { margin-top: 22px; } .shell__closed { margin-top: var(--sp-4); }
/* Closed matches a queue row same padding, hover and count but stays a
disclosure: a chevron marks it as foldable, closed by default because it is
reporting, not work. */
.shell__closed > summary { .shell__closed > summary {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between;
gap: 8px; gap: 8px;
cursor: pointer; cursor: pointer;
list-style: none; list-style: none;
padding: 0 10px 10px; padding: 9px 12px;
font-size: var(--fs-3xs); border-radius: var(--r-md);
font-weight: 500; font-size: var(--fs-xs);
letter-spacing: 0.12em; color: var(--zk-muted);
text-transform: uppercase; transition: background var(--t-fast), color var(--t-fast);
color: var(--zk-grey);
} }
.shell__closed > summary::-webkit-details-marker { display: none; } .shell__closed > summary::-webkit-details-marker { display: none; }
.shell__closed > summary:hover { color: var(--zk-muted); } .shell__closed > summary:hover { background: var(--zk-tint); color: var(--zk-ink); }
.shell__closed > summary b { .shell__closed > summary::before {
font-size: var(--fs-2xs);
font-weight: 500;
letter-spacing: 0;
font-variant-numeric: tabular-nums;
color: var(--zk-grey);
}
.shell__closed > summary::after {
content: ''; content: '';
order: -1; flex: none;
width: 5px; height: 5px; width: 5px; height: 5px;
border-right: 1.5px solid currentColor; border-right: 1.5px solid var(--zk-grey);
border-bottom: 1.5px solid currentColor; border-bottom: 1.5px solid var(--zk-grey);
transform: rotate(-45deg); transform: rotate(-45deg);
transition: transform var(--t-fast); transition: transform var(--t-fast);
} }
.shell__closed[open] > summary::after { transform: rotate(45deg); } .shell__closed[open] > summary::before { transform: rotate(45deg); }
.shell__closed > summary b {
margin-left: auto;
min-width: 20px;
padding: 1px 6px;
border-radius: var(--r-pill);
text-align: center;
font-size: var(--fs-2xs);
font-weight: 500;
font-variant-numeric: tabular-nums;
color: var(--zk-grey);
background: var(--zk-tint);
}

View File

@ -60,7 +60,7 @@ export default function Shell() {
{c.urgent ? <span className="shell__urgent" title={`${c.urgent} expiring within 7 days`} /> : null} {c.urgent ? <span className="shell__urgent" title={`${c.urgent} expiring within 7 days`} /> : null}
{/* The agents' share is shown in the middle dot notation rather {/* The agents' share is shown in the middle dot notation rather
than added in: it is in the queue, it is not your work. */} than added in: it is in the queue, it is not your work. */}
{c.working ? <em className="shell__auto" title={`${c.working} being worked by an agent`}>{c.working}</em> : null} {c.working ? <em className="shell__auto" title={`${c.working} being worked by an agent`}>{c.working} with AI</em> : null}
<b className={'shell__n' + (n ? '' : ' is-zero')}>{n}</b> <b className={'shell__n' + (n ? '' : ' is-zero')}>{n}</b>
</span> </span>
) : null} ) : null}
@ -106,15 +106,9 @@ export default function Shell() {
{NAV_GROUPS.filter((g) => g.kind !== 'end').map((group) => { {NAV_GROUPS.filter((g) => g.kind !== 'end').map((group) => {
const inGroup = stages.filter((s) => s.kind === group.kind) const inGroup = stages.filter((s) => s.kind === group.kind)
if (!inGroup.length) return null if (!inGroup.length) return null
const outstanding = inGroup.reduce((t, s) => t + (counts[s.uid]?.waiting || 0), 0)
return ( return (
<div className="shell__group" key={group.kind}> <div className="shell__group" key={group.kind}>
<p className="shell__navlabel"> <p className="shell__navlabel">{group.label}</p>
{group.label}
{ready && group.kind === 'needs' && outstanding ? (
<span className="shell__grouptally">{outstanding}</span>
) : null}
</p>
<div className="shell__stages">{inGroup.map(queue)}</div> <div className="shell__stages">{inGroup.map(queue)}</div>
</div> </div>
) )