---
status: paused
superseded_by: ""
---

# 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。

