Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/web2-bundled-ui.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": minor
---

web: Replace the bundled web UI with the design-first kimi-web2 prototype built from the Kimi Design System. Run kimi web and open the printed URL to try it.
2 changes: 1 addition & 1 deletion apps/kimi-code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"provenance": true
},
"scripts": {
"build": "pnpm -C ../kimi-web run build && tsdown && node scripts/copy-native-assets.mjs && node scripts/copy-web-assets.mjs",
"build": "tsdown && node scripts/copy-native-assets.mjs && node scripts/copy-web-assets.mjs",
"prebuild": "node scripts/build-vis-asset.mjs",
"catalog:update": "node scripts/update-catalog.mjs --out dist/built-in-catalog.json",
"smoke": "node scripts/smoke.mjs",
Expand Down
24 changes: 15 additions & 9 deletions apps/kimi-code/scripts/copy-web-assets.mjs
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import { cp, rm, stat } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import { basename, dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

const appRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const repoRoot = resolve(appRoot, '../..');
const source = resolve(repoRoot, 'apps/kimi-web/dist');
// This branch ships the design-first kimi-web2 UI as the bundled web assets.
// kimi-web2 is a static, no-build app, so it is copied directly (no dist step).
const source = resolve(repoRoot, 'apps/kimi-web2');
const target = resolve(appRoot, 'dist-web');

async function assertBuiltWeb() {
// Dev/docs files that should not ship in the web asset bundle.
const EXCLUDE = new Set(['serve.mjs', 'README.md', 'CONVENTIONS.md']);

async function assertWebSource() {
try {
const info = await stat(resolve(source, 'index.html'));
if (!info.isFile()) {
throw new Error('index.html is not a file');
}
} catch {
throw new Error(
`Kimi web build output was not found at ${source}. Run \`pnpm --filter @moonshot-ai/kimi-web run build\` first.`,
);
throw new Error(`Kimi web assets were not found at ${source}.`);
}
}

await assertBuiltWeb();
await assertWebSource();
await rm(target, { recursive: true, force: true });
await cp(source, target, { recursive: true });
await cp(source, target, {
recursive: true,
filter: (src) => !EXCLUDE.has(basename(src)),
});

console.log(`Copied Kimi web assets to ${target}`);
console.log(`Copied Kimi web assets (kimi-web2) to ${target}`);
42 changes: 42 additions & 0 deletions apps/kimi-web2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# kimi-web2 — Kimi Design System web UI (design-first rewrite)

从 Kimi Design System(设计 skill)出发、从零构建的 kimi-web 界面。零依赖、无构建(vanilla
HTML/CSS/JS),可离线以 stub 数据预览设计,也可接上真实 kimi-code 服务器打通前后端。

## 预览(纯设计,无后端)

直接打开 `index.html` 即可(file:// 也可以),所有数据为 stub。

直达参数:`?open=settings | aw | search | models | login` · `?theme=dark`(可叠加)。

## 接真实服务器

服务器 CORS 是白名单制,推荐用自带的同源代理:

```bash
node serve.mjs # 默认 --port 8101 --target http://127.0.0.1:58627
open "http://localhost:8101/?token=<server token>"
```

`?token=` 只需带一次(会存入 localStorage)。有 token 时 `live.js` 会:

- 拉取真实 workspaces / sessions 替换 stub;
- 打开会话时按需拉取该会话最近 100 条消息并渲染(文本/思考/工具调用);
- 通过 WS 接收实时事件(回复流、工具进度、回合结束);
- 发送消息 POST 到该会话(本地新建的 stub 会话仍走 stub 回复)。

无 token 时完全离线,行为与设计稿一致。

## 文件

- `index.html` / `styles.css` / `app.js` — 外壳、渲染、交互(tokens 见 styles.css 顶部)。
- `data.js` — stub 数据 + 轻量 store(`window.Store`)。
- `live.js` — 真实服务器接线(REST + WS 协议说明见文件顶部注释)。
- `serve.mjs` — 零依赖静态 + `/api/v1` 代理(HTTP + WS)。
- `features/` — 自包含功能模块(模型选择、登录、右侧面板、设置四页),约定见
`features/CONVENTIONS.md`。

## 设计基准

Kimi Design System:kimiDark 中性强调(蓝色仅品牌/数据)、状态用填充非边框、0.5px hairline、
PingFang 优先、圆角 8/10/12/16/20、动效 `cubic-bezier(0.23,1,0.32,1)`、浅色 + 深色。
Loading
Loading