kimi-code/docs/design-system.html
qer 583a4d3fef feat(web): Modern theme polish, onboarding, design system & backend wiring
UI/UX:
- Onboarding: welcome + language/theme prefs (Modern default, applied on start)
- Modern is the default theme; chat surface white; floating dock (input on top,
  status controls as functional pill-buttons below, ctx far right)
- Single-line composer with smaller send; empty-chat hint vertically centered
- Global connecting splash on first load; overlay drift fixes (viewport-unit sizing)
- Wide-screen reading-column cap; font-size pass (chrome up, chat = session list)
- Merged ~/files + ~/diff into one tab (Changed|All, list/tree)
- ThinkingBlock capped to ~3.5 lines with auto-scroll-to-latest
- Workspace rail title + branch on second line

Design system:
- Radius scale tokens (--r-xs/sm/md/lg = 6/8/12/16); unified component radii
- Moved Modern per-component overrides to global style.css (scoped :global() did
  not win the cascade — input/tabs/cards were silently un-styled)
- docs/design-system.html: tokens + live component reference

Backend wiring:
- POST /sessions/{id}/profile for model + runtime (thinking/permission/plan)
- GET /sessions/{id}/status; :compact / :fork; agent.status.updated
- Historical replay no longer re-streams (messagesToTurns dedup)
2026-06-10 14:09:32 +08:00

302 lines
20 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" />
<meta name="color-scheme" content="light" />
<title>Kimi Web 设计规范</title>
<style>
/* ===== Tokens — kept in sync with apps/kimi-web/src/style.css ===== */
:root{
--ink:#14171c; --text:#3f454d; --dim:#697079; --muted:#8b929b; --faint:#aeb4bc;
--line:#e7eaee; --line2:#eef1f4; --panel:#fafbfc; --panel2:#f3f5f8; --bg:#fff;
--blue:#1565c0; --blue2:#0d4f9e; --soft:#e9f0fa; --bd:#cfe0f6;
--bluebg:#eef4fc; --blueln:#cfe0f7; --ok:#0e7a38; --warn:#92660a; --err:#b91c1c;
--r-xs:6px; --r-sm:8px; --r-md:12px; --r-lg:16px;
--mono:"SF Mono",ui-monospace,Menlo,Consolas,monospace;
--sans:-apple-system,"Segoe UI",Roboto,"PingFang SC","Microsoft YaHei",sans-serif;
--canvas:#f4f6fa;
--sh:0 1px 3px rgba(28,40,66,.05),0 6px 18px rgba(28,40,66,.06);
--shc:0 1px 2px rgba(28,40,66,.05);
}
*{box-sizing:border-box}
html,body{margin:0;background:var(--canvas);color:var(--text);font-family:var(--sans);font-size:14px;line-height:1.55}
.wrap{max-width:1040px;margin:0 auto;padding:32px 22px 120px}
header h1{font-size:26px;color:var(--ink);margin:0 0 4px;letter-spacing:-.01em}
header p{color:var(--muted);margin:0 0 6px;font-size:13.5px}
code{font-family:var(--mono);font-size:.88em;background:var(--panel2);border:1px solid var(--line);border-radius:5px;padding:1px 5px;color:var(--dim)}
/* sticky TOC as a floating overlay (not part of layout) */
.toc{position:fixed;top:18px;right:18px;z-index:50;background:#fff;border:1px solid var(--line);border-radius:var(--r-md);box-shadow:var(--sh);padding:10px 12px;font-size:12px;max-width:180px}
.toc b{display:block;color:var(--ink);font-size:11px;text-transform:uppercase;letter-spacing:.06em;margin-bottom:6px;color:var(--muted)}
.toc a{display:block;color:var(--dim);text-decoration:none;padding:2px 0}
.toc a:hover{color:var(--blue)}
@media (max-width:1180px){.toc{display:none}}
section{margin-top:38px}
h2{font-size:18px;color:var(--ink);margin:0 0 4px;padding-bottom:8px;border-bottom:1px solid var(--line)}
.sub{color:var(--muted);font-size:13px;margin:0 0 18px}
h3{font-size:13.5px;color:var(--ink);margin:22px 0 10px;font-weight:600}
.card{background:#fff;border:1px solid var(--line);border-radius:var(--r-lg);padding:20px;box-shadow:var(--shc)}
.grid{display:grid;gap:12px}
.g-color{grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
.swatch{border:1px solid var(--line);border-radius:var(--r-sm);overflow:hidden;background:#fff}
.swatch .chip{height:46px}
.swatch .meta{padding:7px 9px;font-family:var(--mono);font-size:11px}
.swatch .nm{color:var(--ink);font-weight:600}
.swatch .hx{color:var(--muted)}
table{width:100%;border-collapse:collapse;font-size:13px}
th,td{text-align:left;padding:8px 10px;border-bottom:1px solid var(--line2);vertical-align:middle}
th{color:var(--muted);font-weight:600;font-size:11px;text-transform:uppercase;letter-spacing:.05em}
td code{white-space:nowrap}
.demo-box{display:inline-flex;align-items:center;justify-content:center;width:64px;height:40px;background:var(--soft);border:1px solid var(--bd);color:var(--blue2);font-family:var(--mono);font-size:11px}
/* live component demos */
.stage{background:var(--bg);border:1px dashed var(--line);border-radius:var(--r-md);padding:22px;display:flex;flex-wrap:wrap;gap:16px;align-items:center}
.stage.canvas{background:var(--canvas)}
.label{font-family:var(--mono);font-size:11px;color:var(--faint);width:100%;margin-bottom:-4px}
/* === real components (mirrors of the app) === */
.btn{height:30px;padding:0 14px;border:none;border-radius:var(--r-sm);font-family:var(--sans);font-size:12px;font-weight:600;cursor:pointer}
.btn.primary{background:var(--blue);color:#fff;box-shadow:0 1px 2px rgba(20,23,28,.1)}
.btn.primary:hover{background:var(--blue2)}
.btn.ghost{background:none;color:var(--dim);border:1px solid var(--line)}
.btn.ghost:hover{background:var(--soft);color:var(--blue2);border-color:var(--bd)}
.cin{display:flex;align-items:flex-end;gap:6px;width:100%;max-width:560px;border:1px solid var(--line);background:#fff;border-radius:var(--r-md);padding:6px 6px 6px 14px;box-shadow:var(--sh)}
.cin .ph{flex:1;color:var(--faint);font-size:13px;min-height:22px;display:flex;align-items:center}
.cin .ico{width:30px;height:30px;border-radius:var(--r-sm);display:flex;align-items:center;justify-content:center;color:var(--muted)}
.cin .snd{height:30px;padding:0 13px;border-radius:var(--r-sm);background:var(--blue);color:#fff;font-size:12px;font-weight:600;display:flex;align-items:center;box-shadow:0 1px 2px rgba(20,23,28,.1)}
.se{display:flex;flex-direction:column;gap:2px;width:230px;padding:8px 11px;border-radius:var(--r-sm);cursor:pointer}
.se:hover{background:#eef1f6}
.se.on{background:var(--soft);box-shadow:inset 0 0 0 1px var(--bd)}
.se .t{color:var(--ink);font-size:13px;font-weight:500}
.se .m{color:var(--faint);font-family:var(--mono);font-size:11px}
.wschip{display:flex;align-items:center;gap:9px;width:200px;padding:7px 9px;border-radius:var(--r-sm);cursor:pointer}
.wschip:hover{background:var(--panel2)}
.wschip.on{background:var(--ink)}
.wschip .ava{width:26px;height:26px;border-radius:var(--r-xs);background:var(--panel2);color:var(--dim);display:flex;align-items:center;justify-content:center;font-family:var(--mono);font-weight:700;font-size:12px}
.wschip.on .ava{background:#000;color:#fff}
.wschip .nm{font-family:var(--mono);font-size:13px;color:var(--text)}
.wschip.on .nm{color:#fff}
.tabs{display:flex;gap:2px;background:#fff;border:1px solid var(--line);border-radius:var(--r-sm);padding:4px}
.tb{padding:5px 12px;border-radius:var(--r-sm);font-family:var(--mono);font-size:13px;color:var(--muted);cursor:pointer}
.tb:hover{background:var(--canvas);color:var(--ink)}
.tb.on{background:var(--soft);color:var(--blue2)}
.kv{display:inline-flex;align-items:center;gap:5px;height:22px;padding:0 9px;border-radius:var(--r-sm);font-size:12px;color:var(--muted);cursor:pointer}
.kv b{color:var(--ink);font-weight:600}
.kv:hover{background:var(--soft);color:var(--blue2)}
.box{width:100%;max-width:520px;border:1px solid var(--line);background:#fff;border-radius:var(--r-md);box-shadow:var(--shc)}
.box .bh{display:flex;align-items:center;gap:7px;padding:8px 11px;font-size:13px}
.box .bh .a{color:var(--blue2);font-weight:700}
.box .bh .p{color:var(--dim)}
.box .bb{border-top:1px solid var(--line);padding:9px 12px;font-family:var(--mono);font-size:11.5px;color:var(--dim);line-height:1.7}
.bub{max-width:60%;background:var(--bluebg);border:1px solid var(--blueln);border-radius:18px 18px 6px 18px;padding:11px 15px;color:var(--ink);font-size:14px;box-shadow:var(--shc);margin-left:auto}
.dialog{width:340px;background:#fff;border:1px solid var(--line);border-top:2px solid var(--blue);border-radius:var(--r-lg);box-shadow:0 8px 30px rgba(20,23,28,.16);padding:16px}
.dialog .dt{color:var(--ink);font-weight:700;font-size:14px;margin-bottom:4px}
.dialog .dd{color:var(--muted);font-size:12.5px}
.radius-sample{display:flex;gap:14px;flex-wrap:wrap}
.rs{width:84px;text-align:center}
.rs .b{height:60px;background:var(--soft);border:1px solid var(--bd)}
.rs .l{font-family:var(--mono);font-size:11px;color:var(--dim);margin-top:6px}
.rs .u{font-size:10.5px;color:var(--faint)}
.type-row{display:flex;align-items:baseline;gap:14px;padding:7px 0;border-bottom:1px solid var(--line2)}
.type-row .sz{font-family:var(--mono);font-size:11px;color:var(--muted);width:54px;flex:none}
.note{font-size:12.5px;color:var(--muted);background:var(--panel);border:1px solid var(--line);border-left:3px solid var(--blue);border-radius:0 var(--r-sm) var(--r-sm) 0;padding:10px 14px;margin-top:12px}
</style>
</head>
<body>
<nav class="toc">
<b>目录</b>
<a href="#intro">概览</a>
<a href="#color">颜色</a>
<a href="#radius">圆角</a>
<a href="#type">字体</a>
<a href="#shadow">阴影</a>
<a href="#components">组件</a>
<a href="#themes">两个主题</a>
</nav>
<div class="wrap">
<header id="intro">
<h1>Kimi Web 设计规范</h1>
<p>这是 Web 端(<code>apps/kimi-web</code>)的设计 token 与组件标准。所有数值与 <code>src/style.css</code><code>:root</code> token 保持一致——改 token 时同步改这里。</p>
<p>结论先行:颜色、圆角、字体、阴影都收敛成一套可复用的 token;圆角按 <b>8 / 12 / 16</b> 的规律分层(列表项 → 输入框/卡片 → 对话框)。</p>
</header>
<!-- ============ COLOR ============ -->
<section id="color">
<h2>颜色 Color</h2>
<p class="sub">中性色撑起层级(墨→正文→次要→淡),蓝色是唯一强调色,语义色只在状态里出现。</p>
<h3>中性 / 文字 / 线</h3>
<div class="grid g-color">
<div class="swatch"><div class="chip" style="background:#14171c"></div><div class="meta"><div class="nm">ink</div><div class="hx">#14171c · 标题/主文字</div></div></div>
<div class="swatch"><div class="chip" style="background:#3f454d"></div><div class="meta"><div class="nm">text</div><div class="hx">#3f454d · 正文</div></div></div>
<div class="swatch"><div class="chip" style="background:#697079"></div><div class="meta"><div class="nm">dim</div><div class="hx">#697079 · 次要</div></div></div>
<div class="swatch"><div class="chip" style="background:#8b929b"></div><div class="meta"><div class="nm">muted</div><div class="hx">#8b929b · 弱化</div></div></div>
<div class="swatch"><div class="chip" style="background:#aeb4bc"></div><div class="meta"><div class="nm">faint</div><div class="hx">#aeb4bc · 占位/最弱</div></div></div>
<div class="swatch"><div class="chip" style="background:#e7eaee"></div><div class="meta"><div class="nm">line</div><div class="hx">#e7eaee · 边框</div></div></div>
<div class="swatch"><div class="chip" style="background:#f3f5f8"></div><div class="meta"><div class="nm">panel2</div><div class="hx">#f3f5f8 · 浅底</div></div></div>
<div class="swatch"><div class="chip" style="background:#f4f6fa;border-bottom:1px solid var(--line)"></div><div class="meta"><div class="nm">canvas</div><div class="hx">#f4f6fa · 画布(Modern)</div></div></div>
</div>
<h3>蓝色强调 + 语义色</h3>
<div class="grid g-color">
<div class="swatch"><div class="chip" style="background:#1565c0"></div><div class="meta"><div class="nm">blue</div><div class="hx">#1565c0 · 主操作</div></div></div>
<div class="swatch"><div class="chip" style="background:#0d4f9e"></div><div class="meta"><div class="nm">blue2</div><div class="hx">#0d4f9e · hover/强调字</div></div></div>
<div class="swatch"><div class="chip" style="background:#e9f0fa"></div><div class="meta"><div class="nm">soft</div><div class="hx">#e9f0fa · 选中底/hover</div></div></div>
<div class="swatch"><div class="chip" style="background:#eef4fc;border-bottom:1px solid var(--blueln)"></div><div class="meta"><div class="nm">bluebg</div><div class="hx">#eef4fc · 用户气泡</div></div></div>
<div class="swatch"><div class="chip" style="background:#0e7a38"></div><div class="meta"><div class="nm">ok</div><div class="hx">#0e7a38 · 成功</div></div></div>
<div class="swatch"><div class="chip" style="background:#92660a"></div><div class="meta"><div class="nm">warn</div><div class="hx">#92660a · 警告/活动</div></div></div>
<div class="swatch"><div class="chip" style="background:#b91c1c"></div><div class="meta"><div class="nm">err</div><div class="hx">#b91c1c · 错误</div></div></div>
</div>
</section>
<!-- ============ RADIUS ============ -->
<section id="radius">
<h2>圆角 Radius</h2>
<p class="sub">这是这次重点统一的部分。Modern 用一套 <b>8 / 12 / 16</b> 的规律:越"大块"的容器圆角越大。Terminal 主题刻意保持 34px 的硬朗感,不走这套。</p>
<div class="card">
<div class="radius-sample">
<div class="rs"><div class="b" style="border-radius:var(--r-xs)"></div><div class="l">--r-xs · 6px</div><div class="u">徽章/头像方块</div></div>
<div class="rs"><div class="b" style="border-radius:var(--r-sm)"></div><div class="l">--r-sm · 8px</div><div class="u">列表项·tab·按钮·状态控件</div></div>
<div class="rs"><div class="b" style="border-radius:var(--r-md)"></div><div class="l">--r-md · 12px</div><div class="u">输入框·工具卡</div></div>
<div class="rs"><div class="b" style="border-radius:var(--r-lg)"></div><div class="l">--r-lg · 16px</div><div class="u">对话框·引导卡</div></div>
</div>
<div class="note">用户提到的"输入框圆角和 session/workspace 列表成规律":列表项(session 行、workspace chip)= <code>--r-sm</code> 8px,输入框高一档 = <code>--r-md</code> 12px,对话框再高一档 = <code>--r-lg</code> 16px。圆形头像/点用 <code>50%</code>;气泡用专门的非对称圆角 <code>18px 18px 6px 18px</code>(表达"指向")。</div>
</div>
<table style="margin-top:14px">
<tr><th>Token</th><th></th><th>用在哪</th><th>样例</th></tr>
<tr><td><code>--r-xs</code></td><td>6px</td><td>workspace 头像方块、计数徽章</td><td><span class="demo-box" style="border-radius:var(--r-xs)">xs</span></td></tr>
<tr><td><code>--r-sm</code></td><td>8px</td><td>session 行、workspace chip、tab、发送/附件键、状态控件</td><td><span class="demo-box" style="border-radius:var(--r-sm)">sm</span></td></tr>
<tr><td><code>--r-md</code></td><td>12px</td><td>输入框、工具调用卡</td><td><span class="demo-box" style="border-radius:var(--r-md)">md</span></td></tr>
<tr><td><code>--r-lg</code></td><td>16px</td><td>对话框、引导卡、移动端底部 sheet</td><td><span class="demo-box" style="border-radius:var(--r-lg)">lg</span></td></tr>
</table>
</section>
<!-- ============ TYPE ============ -->
<section id="type">
<h2>字体 Typography</h2>
<p class="sub">两套字族分工:<b>chrome/代码/路径/时间戳</b>用等宽 <code>--mono</code>;<b>Modern 的正文</b><code>--sans</code>。这是 Modern 和 Terminal 观感差异的关键。</p>
<div class="card">
<div class="type-row"><span class="sz">26px</span><span style="font-size:26px;color:var(--ink);font-weight:700">标题 H1</span></div>
<div class="type-row"><span class="sz">18px</span><span style="font-size:18px;color:var(--ink);font-weight:600">区块标题 H2</span></div>
<div class="type-row"><span class="sz">14px</span><span style="font-size:14px">正文 / 聊天内容 / session 标题(基准 body)</span></div>
<div class="type-row"><span class="sz">13px</span><span style="font-size:13px">输入框、tab、控件</span></div>
<div class="type-row"><span class="sz">12px</span><span style="font-size:12px;color:var(--dim)">状态行、次要信息</span></div>
<div class="type-row"><span class="sz">11px</span><span style="font-size:11px;color:var(--muted);font-family:var(--mono)">时间戳 · 徽章 · 路径(mono)</span></div>
<div class="note"><code>--mono</code> = SF Mono / ui-monospace;<code>--sans</code> = -apple-system / PingFang SC。Terminal 全程 mono;Modern 下 <code>body</code> 切到 sans,但路径/代码/时间戳仍按元素保留 mono。</div>
</div>
</section>
<!-- ============ SHADOW ============ -->
<section id="shadow">
<h2>阴影 Shadow</h2>
<p class="sub">只在 Modern 用,把白色卡片/输入框从画布上"抬起"。两档:卡片轻投影 + 浮层重投影。</p>
<div class="stage canvas">
<div style="background:#fff;border:1px solid var(--line);border-radius:var(--r-md);padding:18px 24px;box-shadow:var(--shc)"><code>--shc</code> 轻(卡片/气泡)</div>
<div style="background:#fff;border:1px solid var(--line);border-radius:var(--r-md);padding:18px 24px;box-shadow:var(--sh)"><code>--sh</code> 重(输入框/弹层)</div>
</div>
</section>
<!-- ============ COMPONENTS ============ -->
<section id="components">
<h2>组件 Components</h2>
<p class="sub">下面都是用上面 token 拼出来的真实组件镜像(Modern 主题)。</p>
<h3>按钮 Button</h3>
<div class="stage">
<span class="label">primary主操作,blue + --r-sm· ghost次操作</span>
<button class="btn primary">发送 ↵</button>
<button class="btn ghost">取消</button>
</div>
<h3>输入框 Composer单行,--r-md,发送键 --r-sm 靠右下)</h3>
<div class="stage canvas">
<div class="cin">
<span class="ph">输入消息…</span>
<span class="ico" title="附件">
<svg viewBox="0 0 16 16" width="15" height="15" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="12" height="10" rx="1.5"/><circle cx="5.5" cy="6.5" r="1"/><polyline points="2,13 5.5,9 8,11.5 10.5,8.5 14,13"/></svg>
</span>
<span class="snd">发送 ↵</span>
</div>
</div>
<h3>状态控件 Status controls服务于输入框的功能按钮,--r-sm,hover 软蓝)</h3>
<div class="stage canvas">
<span class="kv">模型 <b>kimi-k2.6</b></span>
<span class="kv">思考 <b>high</b></span>
<span class="kv">计划 <b></b></span>
<span class="kv">权限 <b>手动</b></span>
<span style="margin-left:auto;color:var(--muted);font-size:12px">ctx <b style="color:var(--ink)">12k/200k</b></span>
</div>
<h3>会话列表项 Session row--r-sm,选中=软蓝+内描边)</h3>
<div class="stage">
<div style="display:flex;flex-direction:column;gap:2px;width:248px">
<div class="se on"><span class="t">现在的整个页面的字号…</span><span class="m">0m ago</span></div>
<div class="se"><span class="t">民法典关于居住权的规定</span><span class="m">3h ago</span></div>
</div>
</div>
<h3>工作区 Workspace chip--r-sm,头像 --r-xs,选中=墨底)</h3>
<div class="stage">
<div style="display:flex;flex-direction:column;gap:4px">
<div class="wschip on"><span class="ava">K</span><span class="nm">kimi-code-web</span></div>
<div class="wschip"><span class="ava">K</span><span class="nm">kimi-code</span></div>
</div>
</div>
<h3>标签栏 Tabs--r-sm 软胶囊)</h3>
<div class="stage">
<div class="tabs"><span class="tb on">~/chat</span><span class="tb">~/files</span><span class="tb">~/tasks</span></div>
</div>
<h3>工具调用卡 Tool card--r-md,白底+轻投影)</h3>
<div class="stage canvas">
<div class="box">
<div class="bh"><span style="color:var(--faint)"></span><span class="a">Read</span><span class="p">· src/style.css</span><span style="margin-left:auto;color:var(--ok);font-weight:700"></span></div>
<div class="bb">读取 312 行…</div>
</div>
</div>
<h3>用户气泡 User bubble非对称大圆角)</h3>
<div class="stage canvas">
<div style="width:100%"><div class="bub">把超时改成可配置</div></div>
</div>
<h3>对话框 Dialog--r-lg,顶部 2px 蓝条)</h3>
<div class="stage canvas">
<div class="dialog"><div class="dt">切换模型</div><div class="dd">选择一个用于当前会话的模型。</div></div>
</div>
</section>
<!-- ============ THEMES ============ -->
<section id="themes">
<h2>两个主题 Themes</h2>
<p class="sub">同一套数据,两种外观。Modern 是默认。</p>
<table>
<tr><th></th><th>Terminal</th><th>Modern默认)</th></tr>
<tr><td>字体</td><td>全程 mono</td><td>正文 sans,chrome/代码 mono</td></tr>
<tr><td>圆角</td><td>硬朗 34px</td><td>8 / 12 / 16 规律</td></tr>
<tr><td>聊天</td><td>行式 <code>user@kimi $</code></td><td>气泡(用户右侧软蓝,助手左侧纯文本)</td></tr>
<tr><td>聊天底色</td><td></td><td>白(正文区),侧栏浅灰</td></tr>
<tr><td>阴影/动画</td><td></td><td>轻投影 + 入场微动画</td></tr>
</table>
<div class="note">实现:<code>html[data-theme="modern"]</code> 挂在根节点。Modern 的逐组件覆盖<b>必须写在全局 <code>src/style.css</code></b>——组件内 scoped 的 <code>:global(html[data-theme=modern]) .x</code> 赢不过基础样式(踩过坑:输入框一直是方角、tab 没胶囊就是这原因)。</div>
</section>
</div>
</body>
</html>