feat(cockpit): quick wins — sync chip, cost budget guard, digest, recent commits (W3)

Four small, high-delight features that turn passive capture into glanceable signals.

- Sync chip on project cards: ahead/behind vs upstream + last-commit time, folded
  into the existing concurrent per-repo metadata pass (git rev-list --count
  --left-right @{u}...HEAD + git log -1 --format=%ct; no upstream → undefined, no route).
- Cost budget guard: COST_BUDGET_USD env (0 = off); a per-session one-shot latch
  (Session.budgetNotified, cost is monotonic so never re-armed) fires a single push
  on threshold crossing in manager.handleStatusLine; the already-broadcast telemetry
  frame carries the warn (tg-cost-warn styling derived from costUsd>=budget via
  /config/ui — no new ServerMessage). web-push title added to sw-push.js.
- "While you were away" digest: GET /digest?since= → {finished, needsInput, stuck,
  totalCostUsd, sessions[]} aggregate over manager.list(); FE banner on reconnect.
- Recent commits per project: src/http/git-log.ts (NUL-delimited git log → CommitInfo[]),
  GET /projects/log?path= (isValidGitDir), textContent-inert render in project detail.

All git via execFile (no shell) + validated cwd; new routes read-only; commit
messages rendered via textContent. Verified: typecheck + build:web clean, 1904 pass
at --test-timeout=30000 (two default-5s failures are slow-sandbox real-subprocess
timeout flakes — the known ring-buffer test + a new real-git-clone sync test — not
logic regressions).
This commit is contained in:
Yaojia Wang
2026-07-12 21:27:20 +02:00
parent 7551f8a4b2
commit 1dd12b035a
30 changed files with 1911 additions and 8 deletions

View File

@@ -1534,6 +1534,113 @@ body {
flex: none;
}
/* W3(a): ahead/behind sync chip (mirrors the .proj-branch chip look). */
.proj-sync {
font-size: 11px;
color: var(--amber);
background: var(--accent-soft);
border-radius: 5px;
padding: 2px 7px;
white-space: nowrap;
flex: none;
}
/* W3(b): cost chip in the per-tab telemetry gauge, warn-styled over budget. */
.tg-cost-warn {
color: var(--red);
font-weight: 600;
}
/* W3(d): recent-commit list in the project detail. */
.proj-commitlog {
margin: 4px 0 10px;
}
.proj-commitlog-loading {
font-size: 12px;
color: var(--text-faint);
}
.proj-commitlog-list {
display: flex;
flex-direction: column;
gap: 2px;
}
.proj-commit-row {
display: flex;
align-items: baseline;
gap: 8px;
font-size: 12px;
min-width: 0;
}
.proj-commit-hash {
font-family: Menlo, Consolas, monospace;
color: var(--accent);
flex: none;
}
.proj-commit-time {
color: var(--text-faint);
flex: none;
white-space: nowrap;
}
.proj-commit-subject {
color: var(--text);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
}
.proj-commit-more {
font-size: 11px;
color: var(--text-faint);
margin-top: 4px;
}
/* W3(c): "while you were away" reconnect banner (compact, dismissible top bar). */
.wya-banner {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
display: flex;
align-items: center;
gap: 12px;
padding: 8px 14px;
font-size: 13px;
color: var(--text);
background: var(--accent-soft);
border-bottom: 1px solid var(--accent);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
}
.wya-title {
font-weight: 600;
color: var(--accent);
flex: none;
}
.wya-summary {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.wya-cost {
color: var(--text-faint);
flex: none;
}
.wya-dismiss {
flex: none;
border: none;
background: transparent;
color: var(--text-faint);
cursor: pointer;
font-size: 14px;
line-height: 1;
padding: 2px 6px;
}
.wya-dismiss:hover {
color: var(--text);
}
/* W3: PR + CI status chip (mirrors the .proj-branch chip look). */
.proj-pr-host {
display: inline-flex;