519423cb0d
- landing/app/mira/wiki: tag=mira-wiki list now shows all wiki paragraphs (depends on KBDB tag filter exposed in matrix/kbdb commit, separate repo) - landing: app/mira hub + feed split + various WIP from prior sessions - registry/components: claude_api / kbdb_create_block / kbdb_get / km_writer / platform_crypto / auth_oauth2 contracts + main.go (accumulated) - .component-builds: pkg-lock updates + index.ts adjustments (WIP) - .agents/specs/arcrun/frontend-redesign: design notes - docs/test_credentials, docs/user_requirements/arcrun-landing-page: WIP docs - cypher-executor: auth-dispatcher / wasi-shim adjustments (WIP) Includes accumulated work from prior sessions plus the wiki UI tag-filter update that surfaces the AI-generated wiki paragraphs at /mira/wiki. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
301 lines
13 KiB
Markdown
301 lines
13 KiB
Markdown
# Frontend Redesign — Design
|
||
|
||
> 讀此檔前請先讀 `requirements.md` 和 `design-source/index.html`。
|
||
> 視覺 spec 的 single source of truth 是 `design-source/`(Claude Design 匯出的 HTML/JSX prototype)。
|
||
|
||
---
|
||
|
||
## 1. 架構總覽
|
||
|
||
```
|
||
landing/ (Next.js 15 App Router)
|
||
├── app/
|
||
│ ├── layout.tsx ← 全站 layout:next/font + design tokens + 全域 CSS 匯入
|
||
│ ├── globals.css ← 匯入 design-tokens.css;Tailwind @import
|
||
│ ├── design-tokens.css ← 新增:從 design-source 抽出的 CSS variables(:root {...})
|
||
│ ├── page.tsx ← Landing(RSC)
|
||
│ ├── auth/
|
||
│ │ └── page.tsx ← Auth("use client")
|
||
│ ├── dashboard/
|
||
│ │ └── page.tsx ← Dashboard("use client",仍靠 middleware 保護)
|
||
│ ├── keys/
|
||
│ │ └── page.tsx ← API Keys("use client")
|
||
│ ├── workflows/
|
||
│ │ ├── page.tsx ← Workflows 清單(redirect 到 dashboard 的 table,本身極簡)
|
||
│ │ └── [name]/page.tsx ← Workflow Viewer("use client")
|
||
│ ├── integrations/page.tsx ← 保留現有
|
||
│ ├── api-docs/page.tsx ← 保留現有
|
||
│ └── login/page.tsx ← 保留現有(redirect /auth 同義;見 §9 遷移策略)
|
||
├── components/
|
||
│ ├── shell/
|
||
│ │ ├── Logo.tsx
|
||
│ │ ├── Icon.tsx
|
||
│ │ ├── TopNav.tsx
|
||
│ │ ├── Footer.tsx
|
||
│ │ └── Sidebar.tsx
|
||
│ ├── primitives/
|
||
│ │ ├── Button.tsx ← btn / btn-primary / btn-secondary / btn-ghost 對應 class
|
||
│ │ ├── Pill.tsx
|
||
│ │ ├── Toggle.tsx
|
||
│ │ ├── Terminal.tsx ← landing hero 右卡用
|
||
│ │ └── ChatPreview.tsx ← landing hero 右卡用
|
||
│ └── workflow/
|
||
│ ├── Canvas.tsx ← wf-viewer 本體(節點 + SVG edges)
|
||
│ ├── NodeCard.tsx
|
||
│ ├── DetailPanel.tsx
|
||
│ ├── Minimap.tsx
|
||
│ └── ZoomControls.tsx
|
||
├── lib/
|
||
│ ├── api.ts ← typed fetch wrapper(fetch ${API_BASE}${path}, credentials: 'include')
|
||
│ ├── workflows.ts ← listWorkflows / getWorkflow / getWorkflowYaml
|
||
│ ├── apiKeys.ts ← listKeys / createKey / patchKey / deleteKey
|
||
│ └── me.ts ← 已存在邏輯,集中到此
|
||
├── middleware.ts ← 擴展 matcher(加 /keys, /workflows/*)
|
||
└── ...(既有 package.json / wrangler.toml 不變)
|
||
```
|
||
|
||
**路由對照設計稿的 5 screen**:
|
||
|
||
| Screen | Route |
|
||
|---|---|
|
||
| Landing | `/` |
|
||
| Auth | `/auth`(新增;`/login` 保留並內部 `redirect('/auth')`) |
|
||
| Dashboard | `/dashboard` |
|
||
| API Keys | `/keys` |
|
||
| Workflow Viewer | `/workflows/[name]` |
|
||
|
||
---
|
||
|
||
## 2. Design tokens 對應
|
||
|
||
設計稿所有 CSS 變數抄進 `app/design-tokens.css`,**不解析、不改名**:
|
||
|
||
```css
|
||
:root {
|
||
--bg: #0F0F0F;
|
||
--bg-1: #141414;
|
||
--card: #1A1A1A;
|
||
--card-2: #222222;
|
||
--line: #262626;
|
||
--line-2: #303030;
|
||
--text: #EDEDED;
|
||
--text-dim: #A0A0A0;
|
||
--text-mute: #6B6B6B;
|
||
--primary: #6366F1;
|
||
--primary-2: #8B5CF6;
|
||
--primary-soft: rgba(99, 102, 241, 0.12);
|
||
--primary-ring: rgba(99, 102, 241, 0.32);
|
||
--success: #22C55E;
|
||
--warn: #F59E0B;
|
||
--danger: #EF4444;
|
||
--gradient: linear-gradient(135deg, #6366F1 0%, #8B5CF6 100%);
|
||
--gradient-soft: linear-gradient(135deg, rgba(99,102,241,0.16) 0%, rgba(139,92,246,0.16) 100%);
|
||
}
|
||
```
|
||
|
||
並在 Tailwind v4 的 `@theme inline` block 內對應出:
|
||
|
||
```css
|
||
@theme inline {
|
||
--color-bg: var(--bg);
|
||
--color-card: var(--card);
|
||
--color-card-2: var(--card-2);
|
||
--color-line: var(--line);
|
||
--color-line-2: var(--line-2);
|
||
--color-text: var(--text);
|
||
--color-text-dim: var(--text-dim);
|
||
--color-text-mute: var(--text-mute);
|
||
--color-primary: var(--primary);
|
||
--color-primary-2: var(--primary-2);
|
||
}
|
||
```
|
||
|
||
這樣 JSX 裡可用 `bg-bg / text-text-dim / border-line`,又保留 CSS 變數語義。
|
||
|
||
**現有的 `--background: #0a0a0a` 要換成 `#0F0F0F`**(視覺 breaking change;受影響:所有沿用 `bg-[#0a0a0a]` 的 inline 值)。
|
||
|
||
---
|
||
|
||
## 3. 字型
|
||
|
||
```tsx
|
||
// app/layout.tsx
|
||
import { Inter, JetBrains_Mono } from 'next/font/google';
|
||
|
||
const inter = Inter({
|
||
subsets: ['latin'],
|
||
variable: '--font-inter',
|
||
weight: ['300', '400', '500', '600', '700', '800'],
|
||
});
|
||
const mono = JetBrains_Mono({
|
||
subsets: ['latin'],
|
||
variable: '--font-mono',
|
||
weight: ['400', '500', '600'],
|
||
});
|
||
|
||
// body class = `${inter.variable} ${mono.variable}`
|
||
```
|
||
|
||
`globals.css` 中的 `body { font-family: var(--font-inter), -apple-system, sans-serif; }`,`.mono` class 用 `font-family: var(--font-mono)`。
|
||
|
||
**移除**:
|
||
- `design-source/index.html` 第 7-9 行的 `<link rel="preconnect"> / <link href="fonts.googleapis.com">`(不寫入 production)。
|
||
- React / Babel standalone script 標籤(prototype 專用,不進 production)。
|
||
|
||
---
|
||
|
||
## 4. 元件 porting 規則
|
||
|
||
Claude Design 用了 `window.Icon / window.Logo / window.AppIcon / window.TopNav ...` 的 globals 風格 — 那是 prototype 專用。Port 到 Next.js 時:
|
||
|
||
1. 每個元件拆單檔、具名 export。
|
||
2. 用 Tailwind + `className` 模板字串;共用 variant(如 btn)用 `cva`-style helper 即可(自己寫 5 行的 `clsx`-alike 函式),**不引入 class-variance-authority / clsx 套件**(避免新依賴)。
|
||
3. Icon 的 `paths` 直接搬,但每個 icon 拆成自己的 functional component 或集中在一個 `<Icon name="..." />`(沿用 design source 的 pattern)。
|
||
4. SVG arc wordmark 的 logo 直接 port。
|
||
|
||
---
|
||
|
||
## 5. 各 screen 實作細節
|
||
|
||
### 5.1 Landing — `app/page.tsx`
|
||
|
||
- 結構:`<TopNav />` + `<Hero />` + `<Paths />` + `<Strip />` + `<Footer />`。
|
||
- Hero:heading、eyebrow、CTA、radial grid bg(純 CSS)。
|
||
- Paths 左卡(Developer):install tabs (`npm` / `pip` / `bun`) + 兩個 terminal block;**code 範例用 dogfooding 範例**(`acr` CLI),不留 `Arcrun` SDK 假 API。
|
||
- Paths 右卡(Everyone):chat preview 結構保留;assistant 對話中的 tool call 用「arcrun · digest/weekly」不動。
|
||
- Strip:4 cell。
|
||
- `LandingClientTabs` 因為有 tabs state,需標 `"use client"`;外層保持 RSC。
|
||
|
||
### 5.2 Auth — `app/auth/page.tsx`
|
||
|
||
- `"use client"`。state:`mode: 'signin' | 'signup'`, `email`, `pw`, `remember`。
|
||
- Submit:`fetch(${API_BASE}/auth/password-login, { method: 'POST', credentials: 'include' })`(**若 cypher-executor 尚未支援 password auth,先顯示「Password 登入尚未開放,請用 OAuth」警示,不偽造成功流程**)。
|
||
- OAuth 按鈕:直接 `<a href={API_BASE}/auth/google/start?redirect=/dashboard>`,和現行 `/login` 同樣機制。
|
||
- 下標提示「By signing up, you agree to our Terms ...」保留 static 字串。
|
||
- 保留 `/login` 路由向後相容(RSC 裡 `redirect('/auth')`)。
|
||
|
||
### 5.3 Dashboard — `app/dashboard/page.tsx`
|
||
|
||
- `"use client"` 或 split(外層 RSC 抓 /me,內層 Client)。
|
||
- 由 `<Sidebar current="dashboard" />` + main。
|
||
- 主要區塊:
|
||
- Main head:breadcrumb「{email 的 domain} › Dashboard」、heading「Welcome back, {display_name}」、subtitle 顯示 app/workflow 總數(從 `/apps` + `/workflows` 計算;若 endpoint 404 → 顯示 `—`)。
|
||
- Apps Grid:`/apps` 的結果渲染;每列永遠有一個 `app-empty` 卡(新建 CTA)。
|
||
- Workflows Table:`/workflows` 的結果渲染;空時改為全寬「No workflows yet. Run `acr push` to add one.」內嵌指令框。
|
||
- 「Open app」「View」按鈕導向 `/workflows/[name]`。
|
||
- 「Edit in Claude」按鈕本次不做動作,僅保 UI(disabled + tooltip「Coming soon」)。
|
||
|
||
### 5.4 API Keys — `app/keys/page.tsx`
|
||
|
||
- `"use client"`。
|
||
- Fetch `/api-keys`:若回傳為空陣列但 `/me` 有 api_key,fallback 顯示 `/me.api_key` 為唯一一列(單 key 相容模式)。
|
||
- 頂部 new-key-box:只在「剛剛建立新 key」的一次性狀態顯示(`useState` + `sessionStorage` flag,reload 後消失)。
|
||
- 表格、toggle、trash:對應 PATCH / DELETE。
|
||
- 「Create new key」按鈕:呼叫 `POST /api-keys`,拿到後打 highlight box。
|
||
- Revoke 警告文字維持設計稿「within 60 seconds」。
|
||
|
||
### 5.5 Workflow Viewer — `app/workflows/[name]/page.tsx`
|
||
|
||
- `"use client"`,param `name` 來自動態路由。
|
||
- Mount 後呼叫 `GET /workflows/:name`:後端回傳 `{ name, nodes: Node[], edges: Edge[], yaml, last_run: {...} }`(若 endpoint 未實作 → 顯示「Workflow viewer 尚未啟用」empty state,不用假資料)。
|
||
- `<Canvas>` 內:
|
||
- SVG 的 `<marker>`, `<path>` 定義抄設計稿。
|
||
- Node 用絕對定位(x/y 直接用 API 資料;資料沒有 coord 時做自動 layout — 階段性做簡單 dagre-free 的「column by depth」排版,避免新依賴)。
|
||
- 點選節點 → 右側 detail panel 顯示 input/output schema;若 type 含 `ai.*`,顯示 triplet 編輯器(model / temp / prompt)— 編輯本次 **read-only**(disabled input + 「Edit via acr CLI」提示)。
|
||
- 「Export YAML」按 `GET /workflows/:name/yaml` → `download` blob。
|
||
- 「Edit in Claude」:本次只開新 tab 到 `https://claude.ai/new?q=...`(文案「coming soon」按鈕),避免偽裝已整合。
|
||
- Zoom controls、minimap:純 UI,`zoom` state 實際不套 transform(或簡單 `style={{ transform: scale(zoom/100) }}` 套在 `.wf-nodes` + svg)。
|
||
|
||
---
|
||
|
||
## 6. API wrapper(`lib/api.ts`)
|
||
|
||
```ts
|
||
export const API_BASE = process.env.NEXT_PUBLIC_API_BASE ?? 'https://cypher.arcrun.dev';
|
||
|
||
export async function arcrunFetch<T>(path: string, init: RequestInit = {}): Promise<T> {
|
||
const res = await fetch(`${API_BASE}${path}`, {
|
||
credentials: 'include',
|
||
headers: { 'Accept': 'application/json', ...(init.headers ?? {}) },
|
||
...init,
|
||
});
|
||
if (res.status === 401 && typeof window !== 'undefined') {
|
||
window.location.href = `/auth?redirect=${encodeURIComponent(location.pathname)}`;
|
||
throw new Error('unauthenticated');
|
||
}
|
||
if (!res.ok) throw new Error(`arcrun ${path}: ${res.status}`);
|
||
return res.json() as Promise<T>;
|
||
}
|
||
```
|
||
|
||
所有頁面透過這個 wrapper。**禁止在 page.tsx 裡 hard-code `fetch('https://...')`**(測試可以 grep)。
|
||
|
||
---
|
||
|
||
## 7. Middleware
|
||
|
||
```ts
|
||
export const config = {
|
||
matcher: ['/dashboard/:path*', '/keys/:path*', '/workflows/:path*'],
|
||
};
|
||
```
|
||
|
||
現有邏輯(讀 `arcrun_session` cookie,沒有就 redirect `/login?redirect=...`)保留,`/login` 改為內部 redirect `/auth`。
|
||
|
||
---
|
||
|
||
## 8. 不做的設計稿功能
|
||
|
||
| 設計元素 | 取捨 |
|
||
|---|---|
|
||
| 底部的 `proto-switch`(5 個 screen 切換 pill) | **刪**。那是 prototype 用的 demo 切換器,不進 production。 |
|
||
| Sidebar 的 `count` badge | 先保留;數字從 `/workflows` / `/apps` 的長度派生;無資料時藏起來。 |
|
||
| Sidebar bottom 的 avatar + "Maya Rivera / maya@northwind.co" | 換成 `{display_name} / {email}`(真資料)。 |
|
||
| Workflow Viewer 的 triplet 可編輯 | 本次 disabled,僅顯示。 |
|
||
| 「Edit in Claude」整合 | 按鈕保留,點擊開新 tab 到 claude.ai,不串 MCP/API。 |
|
||
| 多 workspace breadcrumb | 固定顯示用戶 email domain 或「Personal」。 |
|
||
|
||
---
|
||
|
||
## 9. 既有頁面遷移
|
||
|
||
| 既有 | 處理 |
|
||
|---|---|
|
||
| `/page.tsx` | **rewrite**:沿用設計稿結構,code demo 字串改為 `acr` 實際指令(現有的 `auth.bind(...)` 寫法可保留在 Python tab) |
|
||
| `/login` | 改為 `redirect('/auth')`(Next.js RSC redirect),保留舊連結相容 |
|
||
| `/dashboard` | **rewrite**:舊 dashboard 變成 API Keys 獨立頁 + 新 Dashboard 總覽。原本 dashboard 裡的 Key 卡片搬到 `/keys`。 |
|
||
| `/api-docs` | 不動 |
|
||
| `/integrations` | 不動;在 Dashboard Apps Grid 旁提供 link |
|
||
|
||
---
|
||
|
||
## 10. 開發順序(高度相依)
|
||
|
||
見 `tasks.md`。總則:
|
||
|
||
1. 先做 design tokens + shell(Logo / Icon / Button / Sidebar / TopNav / Footer) — 其他頁面都吃這些。
|
||
2. 然後 Landing(可直接驗證視覺基準)。
|
||
3. 然後 Auth(獨立)。
|
||
4. 然後 API Keys(後端依賴少)。
|
||
5. 然後 Dashboard(依賴 `/workflows` + `/apps`,若未實作先 empty state)。
|
||
6. 最後 Workflow Viewer(依賴最重,多 endpoint)。
|
||
|
||
---
|
||
|
||
## 11. 風險與未解
|
||
|
||
| 風險 | 緩解 |
|
||
|---|---|
|
||
| cypher-executor 尚未有 `/workflows`, `/apps`, `/api-keys` CRUD | 前端先做,統一走 404 → empty state;另開 task 去 cypher-executor SDD 增補。本次 SDD 不負責後端實作。 |
|
||
| Password auth 沒實作 | Auth 頁 email/password form 在 submit 時顯示「OAuth only」提示 |
|
||
| `acr push` 未記錄 node 座標 | Canvas 自動排版(by topological depth),不強制 YAML 載入 layout |
|
||
| `next-on-pages` 對 `"use client"` 大量頁面的 edge runtime 支援 | 本來就用 `next-on-pages`,問題不大;必要時 per-page `export const runtime = 'edge'` |
|
||
| 舊 `/dashboard` 的 bookmark 使用者 | 現行 `/dashboard` 的 Key 管理被搬走;保留 Key 區塊 + 顯示提示「New page: /keys」引導 |
|
||
|
||
---
|
||
|
||
## 12. 與封測的關係
|
||
|
||
此 SDD 的實作**不解除封測阻擋**(封測阻擋在 Credential Primitives WASM)。此重設計與 Phase 0.6 / 0.7 / 1-3 是並行軌道。richblack 可決定先後順序,但本 SDD 獨立可 ship。
|
||
|