# 森林AI Frontend Foundation Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 将 Web 客户端统一为“森林AI”品牌,并建立可在桌面和移动浏览器使用的基础布局与视觉系统。
**Architecture:** 保留 React、Arco Design 和现有工作台信息架构。品牌资产通过公共 SVG 文件引用,主题令牌集中到独立样式文件,移动导航状态由 `ProjectPage` 管理,避免组件各自实现断点逻辑。
**Tech Stack:** React 18、TypeScript 6、Vite 8、Arco Design、Playwright visual check、CSS custom properties。
## Global Constraints
- 产品可见名称和 HTML title 必须为“森林AI”。
- 主色固定为 `#165DFF`;ICON 使用已批准的年轮知识树方案。
- 桌面优先,但 390×844 视口必须能够访问主内容。
- 不增加新的前端运行时依赖。
- 超出 MVP 的探索同步、支付、窗口停靠和自主 Agent 控件不得伪装为可用功能。
---
### Task 1: 品牌资产与静态契约
**Files:**
- Create: `apps/web_v1/public/senlinai-icon.svg`
- Modify: `apps/web_v1/public/favicon.svg`
- Modify: `apps/web_v1/index.html`
- Modify: `apps/web_v1/scripts/structure-check.mjs`
- Modify: `apps/web_v1/src/pages/login.tsx`
- Modify: `apps/web_v1/src/pages/projects/project-topbar.tsx`
- Modify: `apps/web_v1/src/pages/projects/project-rail.tsx`
**Interfaces:**
- Produces: `/senlinai-icon.svg` as the single brand image consumed by login, topbar and project rail.
- [ ] **Step 1: Add failing brand checks**
Extend `structure-check.mjs` with exact assertions:
```js
const html = readFileSync('index.html', 'utf8')
if (!html.includes('')) failures.push('index language must be zh-CN')
if (!html.includes('
森林AI')) failures.push('document title must be 森林AI')
if (!existsSync('public/senlinai-icon.svg')) failures.push('missing public/senlinai-icon.svg')
for (const file of ['src/pages/login.tsx', 'src/pages/projects/project-topbar.tsx', 'src/pages/projects/project-rail.tsx']) {
const source = readFileSync(file, 'utf8')
if (!source.includes('/senlinai-icon.svg')) failures.push(`${file} must use the brand icon`)
}
```
- [ ] **Step 2: Verify the check fails**
Run: `node scripts/structure-check.mjs`
Expected: FAIL with `missing public/senlinai-icon.svg` and title/asset usage failures.
- [ ] **Step 3: Add the approved SVG and replace all brand placeholders**
Use one `
` at decorative positions, and `aria-label="森林AI 工作台"` on the rail brand button. Set `lang="zh-CN"`, title `森林AI`, and favicon `/senlinai-icon.svg` in `index.html`. Replace visible `森林Agent` and `SenlinAI` labels with `森林AI`.
- [ ] **Step 4: Verify brand contracts**
Run: `node scripts/structure-check.mjs && npm run build && npm run lint`
Expected: all commands PASS; no visible `S` placeholder remains in brand components.
- [ ] **Step 5: Commit**
```powershell
git add apps/web_v1/public apps/web_v1/index.html apps/web_v1/scripts/structure-check.mjs apps/web_v1/src/pages/login.tsx apps/web_v1/src/pages/projects/project-topbar.tsx apps/web_v1/src/pages/projects/project-rail.tsx
git commit -m "feat(web): apply forest AI brand identity"
```
### Task 2: 主题令牌、字体与基础文案
**Files:**
- Create: `apps/web_v1/src/styles/tokens.css`
- Modify: `apps/web_v1/src/main.tsx`
- Modify: `apps/web_v1/src/index.css`
- Modify: `apps/web_v1/src/App.css`
- Modify: `apps/web_v1/src/pages/workspace-body.tsx`
- Modify: `apps/web_v1/src/pages/projects/project-overview.tsx`
- Modify: `apps/web_v1/src/pages/projects/project-ai.tsx`
**Interfaces:**
- Produces: semantic custom properties `--color-*`, `--space-*`, `--radius-*` used by all page CSS.
- [ ] **Step 1: Add failing text and token checks**
Add to `structure-check.mjs`:
```js
const tokenSource = existsSync('src/styles/tokens.css') ? readFileSync('src/styles/tokens.css', 'utf8') : ''
for (const token of ['--color-primary: #165dff', '--font-sans', '--space-4: 16px']) {
if (!tokenSource.toLowerCase().includes(token)) failures.push(`missing token ${token}`)
}
for (const forbidden of ['森林Agent', 'AI智能体', '实时接口', '进度 60%']) {
for (const file of requiredFiles.filter((name) => name.endsWith('.tsx'))) {
if (readFileSync(file, 'utf8').includes(forbidden)) failures.push(`${file} contains forbidden copy ${forbidden}`)
}
}
```
- [ ] **Step 2: Verify failures**
Run: `node scripts/structure-check.mjs`
Expected: FAIL for missing tokens and old copy.
- [ ] **Step 3: Introduce semantic tokens and Chinese system font**
Create tokens beginning with:
```css
:root {
--color-primary: #165dff;
--color-bg: #f7f8fa;
--color-panel: #ffffff;
--color-border: #e5e6eb;
--color-text: #1d2129;
--color-muted: #6b7785;
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-6: 24px;
--radius-control: 8px;
}
```
Import it before `index.css`. Replace old `--senlin-*` variables and scattered brand colors with semantic tokens. Rename visible `AI智能体` to `AI 助手`; remove `实时接口` and fixed progress copy.
- [ ] **Step 4: Verify tokens and copy**
Run: `node scripts/structure-check.mjs && npm run build && npm run lint`
Expected: PASS.
- [ ] **Step 5: Commit**
```powershell
git add apps/web_v1/src/styles apps/web_v1/src/main.tsx apps/web_v1/src/index.css apps/web_v1/src/App.css apps/web_v1/src/pages
git commit -m "refactor(web): unify tokens and product copy"
```
### Task 3: 登录页响应式重构
**Files:**
- Modify: `apps/web_v1/src/pages/login.tsx`
- Modify: `apps/web_v1/src/App.css`
- Modify: `apps/web_v1/scripts/visual-check.mjs`
**Interfaces:**
- Consumes: semantic tokens and `/senlinai-icon.svg`.
- Produces: two-column desktop and one-column mobile login layout.
- [ ] **Step 1: Add desktop and mobile login assertions**
In `visual-check.mjs`, capture login at 1440×1024 and 390×844 and assert:
```js
const loginMetrics = await page.evaluate(() => ({
overflowX: document.documentElement.scrollWidth > document.documentElement.clientWidth,
title: document.title,
columns: getComputedStyle(document.querySelector('.login-card')).gridTemplateColumns,
}))
if (loginMetrics.overflowX) failures.push('login must not overflow horizontally')
if (loginMetrics.title !== '森林AI') failures.push(`unexpected title ${loginMetrics.title}`)
```
At the mobile viewport assert `.login-card` width is at most 390 and its computed column count is one.
- [ ] **Step 2: Verify the current layout fails**
Run: `$env:VISUAL_CHECK_PORT='4174'; node scripts/visual-check.mjs`
Expected: FAIL on mobile overflow or login column assertions.
- [ ] **Step 3: Implement the two-column login**
Keep `.login-brand-panel` and `.login-form-panel`; remove the third marketing panel. Add `@media (max-width: 760px)` that sets `grid-template-columns: 1fr`, reduces outer padding to 16px, and keeps every input/button at full available width.
- [ ] **Step 4: Verify at both viewports**
Run: `$env:VISUAL_CHECK_PORT='4174'; node scripts/visual-check.mjs`
Expected: login checks PASS with no clipping.
- [ ] **Step 5: Commit**
```powershell
git add apps/web_v1/src/pages/login.tsx apps/web_v1/src/App.css apps/web_v1/scripts/visual-check.mjs
git commit -m "fix(web): make login responsive"
```
### Task 4: 移动工作台导航与有效 DOM
**Files:**
- Modify: `apps/web_v1/src/pages/workspace-home.tsx`
- Modify: `apps/web_v1/src/pages/projects/project-topbar.tsx`
- Modify: `apps/web_v1/src/pages/projects/project-rail.tsx`
- Modify: `apps/web_v1/src/pages/projects/project-sidebar.tsx`
- Modify: `apps/web_v1/src/pages/projects/project-statusbar.tsx`
- Modify: `apps/web_v1/src/App.css`
- Modify: `apps/web_v1/scripts/visual-check.mjs`
**Interfaces:**
- Produces: `navOpen: 'projects' | 'channels' | null` state controlled by `ProjectPage`.
- `ProjectTopbar` emits `onOpenProjects` and `onOpenChannels`.
- [ ] **Step 1: Add failing DOM and mobile navigation checks**
Assert that the status user container has no descendant button, the mobile viewport has no horizontal overflow, and buttons labelled `打开项目导航` and `打开频道导航` can reveal their respective asides.
- [ ] **Step 2: Verify failures**
Run: `$env:VISUAL_CHECK_PORT='4174'; node scripts/visual-check.mjs`
Expected: FAIL for nested button console error and missing mobile navigation.
- [ ] **Step 3: Implement controlled drawers**
Add `const [navOpen, setNavOpen] = useState<'projects' | 'channels' | null>(null)` to `ProjectPage`. Pass an `open` class and close callback to rail/sidebar. Add topbar buttons with exact accessible names. CSS must position asides as fixed overlays below 768px, add a backdrop button, and remove `.workbench-shell { min-width: 1180px; }`.
Replace the status user outer `