Files
web-terminal/docs/mockups/project-manager-final.html
Yaojia Wang dc5d073374 chore(v0.6): checkpoint foundation — Amber theme, P1 types, P3 config, design docs
Prior-session prep for the v0.6 Project Manager, checkpointed on the
v0.6-projects branch as a clean base for the parallel builders:

- P1 (src/types.ts): ProjectInfo + ProjectSessionRef contracts; Config
  gains projectRoots/projectScanDepth/projectScanTtlMs/projectDirtyCheck.
- P3 (src/config.ts): parseBool + parseProjectRoots helpers; parse the
  4 PROJECT_* env vars with defaults ([homeDir]/4/10000/true).
- UI: public/style.css indigo -> Amber (#e3a64a) accent theme.
- Docs: FEATURE_PROJECT_MANAGER.md (1:N session design), THEME.md,
  docs/mockups/ (final-amber.png is the locked visual).

Typecheck (backend + web) green. No behavior change to runtime code yet;
P2/P4/P5/P6 implement the feature on top of this.
2026-06-30 10:05:40 +02:00

176 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Project Manager — final</title>
<style>
:root{
--bg:#100f0d; --panel:#181613; --card:#1b1916;
--border:rgba(255,255,255,.07); --border-strong:rgba(255,255,255,.14);
--text:#ece9e3; --text-dim:#a8a299; --text-faint:#6f6a61;
--idle:#8b8a86; --red:#ff6b6b;
--shadow:0 12px 44px rgba(0,0,0,.55); --radius:9px; --radius-lg:14px;
--ui-font:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;
--mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
/* default = amber */
--accent:#e3a64a;--accent-2:#c9892f;--soft:rgba(227,166,74,.14);--line:rgba(227,166,74,.36);
--ink:#1a1305;--working:#e3a64a;--waiting:#e6b84e;--dirty:#e0975a;--glow:rgba(227,166,74,.10);
}
body[data-p="sand"]{--accent:#cdb486;--accent-2:#b39c6e;--soft:rgba(205,180,134,.13);--line:rgba(205,180,134,.34);
--ink:#1a1408;--working:#cdb486;--waiting:#e0975a;--dirty:#d9a26a;--glow:rgba(205,180,134,.09)}
body[data-p="clay"]{--accent:#cf6b4f;--accent-2:#b1543b;--soft:rgba(207,107,79,.15);--line:rgba(207,107,79,.38);
--ink:#ffffff;--working:#cf6b4f;--waiting:#e0aa4e;--dirty:#dd9a55;--glow:rgba(207,107,79,.10)}
body[data-p="brass"]{--accent:#d9b441;--accent-2:#bd9a2c;--soft:rgba(217,180,65,.15);--line:rgba(217,180,65,.38);
--ink:#1a1404;--working:#d9b441;--waiting:#e08a3c;--dirty:#dca64a;--glow:rgba(217,180,65,.10)}
body[data-p="slate"]{--accent:#9aa3ab;--accent-2:#7f888f;--soft:rgba(154,163,171,.12);--line:rgba(154,163,171,.30);
--ink:#12140f;--working:#aab2b9;--waiting:#e3a64a;--dirty:#cfa45e;--glow:rgba(154,163,171,.08)}
*{box-sizing:border-box}
body{margin:0;background:radial-gradient(1100px 520px at 82% -12%, var(--glow), transparent 62%),var(--bg);
color:var(--text);font-family:var(--ui-font);min-height:100vh;-webkit-font-smoothing:antialiased}
.topbar{height:50px;display:flex;align-items:center;gap:14px;padding:0 18px;
border-bottom:1px solid var(--border);background:rgba(24,22,19,.62);backdrop-filter:blur(14px);
position:sticky;top:0;z-index:5}
.brand{display:flex;align-items:center;gap:9px;font-weight:650;letter-spacing:.2px}
.brand .dot{width:11px;height:11px;border-radius:50%;
background:conic-gradient(from 200deg,var(--accent),var(--accent-2),var(--accent))}
.seg{display:flex;background:#0c0b09;border:1px solid var(--border);border-radius:999px;padding:3px;gap:2px;margin-left:6px}
.seg button{all:unset;cursor:pointer;font-size:13px;color:var(--text-dim);padding:5px 15px;border-radius:999px;
transition:.15s;display:flex;align-items:center;gap:6px}
.seg button.active{background:var(--soft);color:var(--accent);box-shadow:inset 0 0 0 1px var(--line)}
.seg .mini{font-size:10px;background:var(--accent);color:var(--ink);border-radius:999px;padding:1px 6px;font-weight:700}
.spacer{flex:1}
.icns{display:flex;gap:6px}
.icns button{all:unset;cursor:pointer;width:33px;height:33px;border-radius:8px;display:grid;place-items:center;
color:var(--text-dim);font-size:15px;transition:.15s}
.icns button:hover{background:var(--soft);color:var(--text)}
.wrap{max-width:1100px;margin:0 auto;padding:26px 22px 60px}
.head{display:flex;align-items:baseline;gap:12px;margin:4px 0 22px}
.head h1{font-size:20px;margin:0;font-weight:680;letter-spacing:.2px}
.head .sub{color:var(--text-faint);font-size:13px}
.head .grow{flex:1}
.search{display:flex;align-items:center;gap:8px;background:var(--panel);border:1px solid var(--border);
border-radius:999px;padding:8px 15px;width:260px;color:var(--text-faint);font-size:13px;transition:.15s}
.search:focus-within{border-color:var(--line)}
.search input{all:unset;flex:1;color:var(--text);font-size:13px}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(290px,1fr));gap:16px}
.card{background:linear-gradient(180deg,var(--card),#161410);border:1px solid var(--border);
border-radius:var(--radius-lg);padding:15px;display:flex;flex-direction:column;gap:11px;
box-shadow:var(--shadow);transition:.18s;position:relative;overflow:hidden}
.card.running{border-color:var(--line)}
.card.running::before{content:"";position:absolute;left:0;right:0;top:0;height:2px;
background:linear-gradient(90deg,transparent,var(--accent),transparent);opacity:.85}
.crow{display:flex;align-items:center;gap:8px;min-width:0}
.name{font-weight:680;font-size:15px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.star{color:var(--waiting);font-size:13px}
.badge{margin-left:auto;font-size:11px;padding:3px 10px;border-radius:999px;display:flex;align-items:center;gap:6px;white-space:nowrap}
.badge.idle{background:rgba(255,255,255,.05);color:var(--text-faint)}
.countbadge{margin-left:auto;font-size:11px;padding:3px 10px;border-radius:999px;background:var(--soft);
color:var(--accent);display:flex;align-items:center;gap:6px;font-weight:600}
.countbadge .d{width:6px;height:6px;border-radius:50%;background:var(--accent);box-shadow:0 0 9px var(--accent)}
.meta{display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.chip{font-size:11px;padding:3px 9px;border-radius:999px;border:1px solid var(--border);color:var(--text-dim);
display:flex;align-items:center;gap:5px;font-family:var(--mono)}
.chip.dirty{color:var(--dirty);border-color:var(--line)}
.chip .gd{width:6px;height:6px;border-radius:50%;background:var(--dirty)}
.path{font-family:var(--mono);font-size:11px;color:var(--text-faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:-2px}
.sessions{display:flex;flex-direction:column;gap:7px}
.srow{display:flex;align-items:center;gap:10px;padding:9px 11px;border-radius:10px;
background:rgba(255,255,255,.025);border:1px solid var(--border);cursor:pointer;transition:.15s;position:relative}
.srow::before{content:"";position:absolute;left:0;top:8px;bottom:8px;width:2px;border-radius:2px;background:var(--rail,transparent)}
.srow:hover{background:var(--soft);border-color:var(--line)}
.srow.working{--rail:var(--working)} .srow.waiting{--rail:var(--waiting)} .srow.idle{--rail:var(--idle)}
.srow .st{width:8px;height:8px;border-radius:50%;flex:0 0 auto;margin-left:3px}
.st.working{background:var(--working);box-shadow:0 0 9px var(--working)}
.st.waiting{background:var(--waiting);box-shadow:0 0 9px var(--waiting);animation:blink 1.1s steps(2) infinite}
.st.idle{background:var(--idle)}
@keyframes blink{50%{opacity:.25}}
.srow .stitle{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.srow .smeta{font-size:11px;color:var(--text-faint);font-family:var(--mono);white-space:nowrap}
.srow .mir{font-size:10px;color:var(--text-dim);background:rgba(255,255,255,.06);padding:2px 7px;border-radius:999px;white-space:nowrap}
.srow .x{color:var(--text-faint);font-size:13px;padding:2px 5px;border-radius:6px;transition:.12s}
.srow .x:hover{color:var(--red);background:rgba(255,107,107,.13)}
.spush{margin-left:auto}
.thumb.empty{height:96px;border-radius:10px;border:1px dashed var(--border-strong);
background:rgba(255,255,255,.015);display:grid;place-items:center;color:var(--text-faint)}
.thumb.empty .plus{font-size:13px;color:var(--accent);border:1px dashed var(--line);padding:7px 16px;border-radius:999px}
.actions{display:flex;gap:8px;margin-top:2px}
.btn{all:unset;cursor:pointer;flex:1;text-align:center;font-size:12px;font-weight:680;padding:9px;border-radius:10px;
background:var(--accent);color:var(--ink);transition:.15s}
.btn:hover{filter:brightness(1.07)}
.btn.new{background:var(--soft);color:var(--accent);box-shadow:inset 0 0 0 1px var(--line)}
.btn.shell{background:rgba(255,255,255,.07);color:var(--text)}
.btn.ghost{flex:0 0 auto;padding:9px 13px;background:transparent;color:var(--text-dim);border:1px solid var(--border)}
.btn.ghost:hover{border-color:var(--border-strong);color:var(--text)}
.card.add{border-style:dashed;align-items:center;justify-content:center;color:var(--text-faint);min-height:236px;gap:6px;cursor:pointer}
.card.add:hover{color:var(--text);border-color:var(--line)}
.card.add .big{font-size:30px;color:var(--accent)}
.note{margin-top:28px;color:var(--text-faint);font-size:12px;text-align:center;line-height:1.7}
</style>
</head>
<body data-p="amber">
<div class="topbar">
<div class="brand"><span class="dot"></span> web-terminal</div>
<div class="seg"><button>Sessions</button><button class="active">Projects <span class="mini">2</span></button></div>
<div class="spacer"></div>
<div class="icns"><button></button><button>🗂</button><button></button></div>
</div>
<div class="wrap">
<div class="head">
<h1>Projects</h1><span class="sub" id="palname">Amber</span>
<span class="grow"></span>
<div class="search">🔍 <input placeholder="search projects…"></div>
</div>
<div class="grid">
<div class="card running">
<div class="crow"><span class="star"></span><span class="name">web-terminal</span><span class="countbadge"><span class="d"></span>3 sessions</span></div>
<div class="meta"><span class="chip">⎇ main</span><span class="chip dirty"><span class="gd"></span>dirty</span><span class="chip">2h ago</span></div>
<div class="path">~/Documents/git/web-terminal</div>
<div class="sessions">
<div class="srow working"><span class="st working"></span><span class="stitle">claude</span><span class="smeta">⚙ working</span><span class="mir spush">👥 2</span><span class="x"></span></div>
<div class="srow waiting"><span class="st waiting"></span><span class="stitle">claude #2</span><span class="smeta">⏳ waiting approval</span><span class="x spush"></span></div>
<div class="srow idle"><span class="st idle"></span><span class="stitle">shell · npm test</span><span class="smeta">✓ idle</span><span class="x spush"></span></div>
</div>
<div class="actions"><button class="btn new"> New claude</button><button class="btn ghost"></button></div>
</div>
<div class="card running">
<div class="crow"><span class="name">colacoder-api</span><span class="countbadge"><span class="d"></span>1 session</span></div>
<div class="meta"><span class="chip">⎇ feat/auth</span><span class="chip">5d ago</span></div>
<div class="path">~/Documents/git/colacoder-api</div>
<div class="sessions"><div class="srow idle"><span class="st idle"></span><span class="stitle">claude</span><span class="smeta">✓ idle · 20m</span><span class="x spush"></span></div></div>
<div class="actions"><button class="btn new"> New claude</button><button class="btn ghost"></button></div>
</div>
<div class="card">
<div class="crow"><span class="star"></span><span class="name">dotfiles</span><span class="badge idle">idle</span></div>
<div class="meta"><span class="chip">⎇ main</span><span class="chip">12d ago</span></div>
<div class="path">~/.dotfiles</div>
<div class="thumb empty"><span class="plus"> start claude here</span></div>
<div class="actions"><button class="btn">Open ▸ claude</button><button class="btn ghost"></button></div>
</div>
<div class="card">
<div class="crow"><span class="name">scratch</span><span class="badge idle">idle</span></div>
<div class="meta"><span class="chip" style="opacity:.6">not a git repo</span></div>
<div class="path">~/scratch</div>
<div class="thumb empty"><span class="plus"> start shell here</span></div>
<div class="actions"><button class="btn shell">Open ▸ shell</button><button class="btn ghost"></button></div>
</div>
<div class="card add"><div class="big"></div><div>Add folder…</div><div style="font-size:11px;opacity:.7">browse a path to track</div></div>
</div>
<div class="note">UI mockup · 一项目可挂多 session每行可单独进入 / ✕Kill· working=强调色 waiting=琥珀 idle=中性灰</div>
</div>
<script>
const NAMES={amber:'Amber 琥珀金',sand:'Sand 沙褐',clay:'Clay 陶土',brass:'Brass 黄铜',slate:'Slate 冷石灰'};
const p=(location.hash||'#amber').slice(1);
document.body.dataset.p=p;
document.getElementById('palname').textContent=NAMES[p]||p;
</script>
</body>
</html>