249 lines
9.9 KiB
Markdown
249 lines
9.9 KiB
Markdown
# 森林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('<html lang="zh-CN">')) failures.push('index language must be zh-CN')
|
||
if (!html.includes('<title>森林AI</title>')) 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 `<img className="brand-icon" src="/senlinai-icon.svg" alt="" />` 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 `<button>` with a non-nested structure:
|
||
|
||
```tsx
|
||
<div className="status-user">
|
||
<button className="status-profile" type="button" onClick={() => setProfileOpen(true)}>
|
||
<span className="status-avatar" aria-hidden="true">张</span>
|
||
<span className="status-name">张明</span>
|
||
</button>
|
||
<Button className="status-upgrade" size="mini" type="primary" onClick={() => setUpgradeOpen(true)}>升级</Button>
|
||
</div>
|
||
```
|
||
|
||
- [ ] **Step 4: Run full frontend verification**
|
||
|
||
Run: `node scripts/structure-check.mjs; $env:VISUAL_CHECK_PORT='4174'; node scripts/visual-check.mjs; npm run build; npm run lint`
|
||
|
||
Expected: all PASS and browser console errors are empty.
|
||
|
||
- [ ] **Step 5: Commit**
|
||
|
||
```powershell
|
||
git add apps/web_v1/src/pages/workspace-home.tsx apps/web_v1/src/pages/projects apps/web_v1/src/App.css apps/web_v1/scripts/visual-check.mjs
|
||
git commit -m "feat(web): add responsive workbench navigation"
|
||
```
|