# Design Document: Life Script Frontend

## Overview

本设计文档描述了基于 React + Tailwind CSS + Headless UI/Radix UI 技术栈，完整还原 PncyssD 原型设计的前端应用架构。应用采用组件化架构，使用 Zustand 进行状态管理，Framer Motion 实现动画效果，并通过 React Router 管理路由。

## Architecture

### 技术栈选型

| 类别 | 技术 | 说明 |
|------|------|------|
| 框架 | React 18 + Vite | 现代化构建工具，快速开发体验 |
| 样式 | Tailwind CSS 3.x | 原子化CSS，完美还原毛玻璃设计 |
| UI组件 | Radix UI | 无样式可访问组件库 |
| 状态管理 | Zustand | 轻量级状态管理，支持持久化 |
| 路由 | React Router v6 | 声明式路由管理 |
| 动画 | Framer Motion | 声明式动画库，替代GSAP |
| 图标 | Lucide React | 与原型一致的图标库 |
| HTTP | Axios | API请求封装 |

### 应用架构图

```mermaid
graph TB
    subgraph "Application Layer"
        App[App.jsx]
        Router[React Router]
    end
    
    subgraph "Pages"
        Login[LoginPage]
        Onboarding[OnboardingPage]
        Dashboard[DashboardPage]
    end
    
    subgraph "Dashboard Views"
        Timeline[TimelineView]
        Script[ScriptView]
        Path[PathView]
    end
    
    subgraph "Shared Components"
        GlassCard[GlassCard]
        GlassButton[GlassButton]
        GlassInput[GlassInput]
        Modal[Modal]
        Header[Header]
        Sidebar[Sidebar]
    end
    
    subgraph "State Management"
        Store[Zustand Store]
        Persist[localStorage Persist]
    end
    
    subgraph "Services"
        AIService[AI Service]
        AuthService[Auth Service]
    end
    
    App --> Router
    Router --> Login
    Router --> Onboarding
    Router --> Dashboard
    
    Dashboard --> Timeline
    Dashboard --> Script
    Dashboard --> Path
    
    Login --> GlassCard
    Login --> GlassInput
    Onboarding --> GlassCard
    Dashboard --> Sidebar
    Dashboard --> Header
    
    Timeline --> Modal
    Script --> GlassCard
    Path --> GlassCard
    
    Store --> Persist
    Timeline --> AIService
    Script --> AIService
    Path --> AIService
```

### 目录结构

```
life-script/
├── public/
│   └── assets/
│       └── images/          # 背景图片、logo等
├── src/
│   ├── components/
│   │   ├── ui/              # 基础UI组件
│   │   │   ├── GlassCard.jsx
│   │   │   ├── GlassButton.jsx
│   │   │   ├── GlassInput.jsx
│   │   │   ├── GlassTextarea.jsx
│   │   │   ├── GlassSelect.jsx
│   │   │   └── index.js
│   │   ├── layout/          # 布局组件
│   │   │   ├── Header.jsx
│   │   │   ├── Sidebar.jsx
│   │   │   ├── Background.jsx
│   │   │   └── index.js
│   │   ├── Modal.jsx        # 模态弹窗
│   │   ├── Loader.jsx       # 加载动画
│   │   └── PromptTag.jsx    # 灵感标签
│   ├── pages/
│   │   ├── LoginPage.jsx
│   │   ├── OnboardingPage.jsx
│   │   └── DashboardPage.jsx
│   ├── views/               # Dashboard子视图
│   │   ├── TimelineView.jsx
│   │   ├── ScriptView.jsx
│   │   ├── PathView.jsx
│   │   └── ProfileModal.jsx
│   ├── store/
│   │   └── useStore.js      # Zustand store
│   ├── services/
│   │   ├── ai.js            # AI服务
│   │   └── api.js           # API封装
│   ├── hooks/
│   │   ├── useTransition.js # 页面过渡hook
│   │   └── useCountdown.js  # 倒计时hook
│   ├── styles/
│   │   └── index.css        # 全局样式
│   ├── utils/
│   │   └── constants.js     # 常量定义
│   ├── App.jsx
│   └── main.jsx
├── index.html
├── tailwind.config.js
├── vite.config.js
└── package.json
```

## Components and Interfaces

### 1. 基础UI组件

#### GlassCard

```typescript
interface GlassCardProps {
  children: React.ReactNode;
  className?: string;
  variant?: 'default' | 'highlight' | 'ai';
  padding?: 'sm' | 'md' | 'lg';
}
```

样式规范：
- 背景: `rgba(15, 17, 26, 0.4)`
- 模糊: `backdrop-filter: blur(25px) saturate(180%)`
- 边框: `1px solid rgba(255, 255, 255, 0.08)`
- 圆角: `32px` (移动端 `20px`)
- 阴影: `0 20px 50px -12px rgba(0, 0, 0, 0.5)`

#### GlassButton

```typescript
interface GlassButtonProps {
  children: React.ReactNode;
  onClick?: () => void;
  variant?: 'default' | 'primary' | 'icon';
  disabled?: boolean;
  loading?: boolean;
  className?: string;
}
```

样式规范：
- 背景: `rgba(255, 255, 255, 0.03)`
- Hover: `rgba(255, 255, 255, 0.08)`
- Primary变体: `bg-orange-200/5 text-orange-200 border-orange-200/20`
- 过渡: `all 0.5s cubic-bezier(0.23, 1, 0.32, 1)`

#### GlassInput

```typescript
interface GlassInputProps {
  label?: string;
  type?: 'text' | 'tel' | 'date';
  placeholder?: string;
  value: string;
  onChange: (value: string) => void;
  maxLength?: number;
  className?: string;
}
```

样式规范：
- 背景: `rgba(0, 0, 0, 0.2)`
- 边框: `1px solid rgba(255, 255, 255, 0.05)`
- Focus: `border-color: #FFAB91; box-shadow: 0 0 20px rgba(255, 171, 145, 0.1)`
- 圆角: `16px`
- 内边距: `14px 20px`

#### GlassTextarea

```typescript
interface GlassTextareaProps {
  label?: string;
  placeholder?: string;
  value: string;
  onChange: (value: string) => void;
  rows?: number;
  className?: string;
}
```

#### GlassSelect

```typescript
interface GlassSelectProps {
  label?: string;
  options: Array<{ value: string; label: string }>;
  value: string;
  onChange: (value: string) => void;
  className?: string;
}
```

### 2. 布局组件

#### Background

动态流体背景组件，包含：
- 渐变底层: `from-[#1a1c2c] via-[#0a0c10] to-[#2d1b10]`
- 浮动模糊圆: 蓝色 (`bg-blue-900/20`) 和橙色 (`bg-orange-900/10`)
- 纹理叠加层: `mix-blend-overlay opacity-30`

#### Header

```typescript
interface HeaderProps {
  showNav?: boolean;
  onProfileClick?: () => void;
}
```

固定定位，包含logo和用户按钮。

#### Sidebar

```typescript
interface SidebarProps {
  activeView: 'timeline' | 'script' | 'path';
  onViewChange: (view: string) => void;
}
```

导航分组：
- 回溯过去: 生命长河
- 创造未来: 爽文剧本, 实现路径

### 3. 模态弹窗

#### Modal

```typescript
interface ModalProps {
  isOpen: boolean;
  onClose: () => void;
  children: React.ReactNode;
  maxWidth?: 'sm' | 'md' | 'lg';
}
```

使用 Radix UI Dialog 实现，样式：
- 遮罩: `bg-black/60 backdrop-blur-xl`
- 内容: GlassCard样式
- 关闭按钮: 右上角X图标

### 4. 页面组件

#### LoginPage

状态：
- phone: string
- code: string
- countdown: number
- isLoading: boolean

流程：
1. 输入手机号 → 点击获取验证码 → 60秒倒计时
2. 输入验证码 → 点击登录 → 验证成功跳转Onboarding

#### OnboardingPage

状态：
- currentStep: 1-5
- formData: RegistrationData

步骤内容：
1. 基础信息 (nickname, gender, mbti, zodiac, hobbies)
2. 童年记忆 (date, text) + 灵感标签
3. 开心经历 (date, text) + 灵感标签
4. 低谷时刻 (date, text) + 灵感标签
5. 未来愿景 (vision, ideal)

#### DashboardPage

状态：
- activeView: 'timeline' | 'script' | 'path'
- isProfileOpen: boolean

布局：
- 左侧: Sidebar (3/12 列)
- 右侧: 内容区 (9/12 列)

### 5. 视图组件

#### TimelineView

```typescript
interface LifeEvent {
  id: number;
  title: string;
  time: string;
  content: string;
  aiFeedback: string;
}
```

功能：
- 显示事件列表（时间线样式）
- 添加新事件模态框
- AI分析反馈

#### ScriptView

```typescript
interface Script {
  id: number;
  theme: string;
  style: string;
  length: string;
  content: string;
  date: string;
}
```

布局：
- 左侧面板: 角色设定卡片 + 创作需求表单 + 历史卷轴列表
- 右侧面板: 剧本内容展示

#### PathView

功能：
- 检查是否有选中的剧本
- 生成路径步骤
- 展示路径卡片列表

## Data Models

### State Schema

```typescript
interface AppState {
  // 认证状态
  isLoggedIn: boolean;
  phone: string;
  
  // 视图状态
  view: 'login' | 'onboarding' | 'dashboard';
  currentStep: number;
  
  // 用户注册数据
  registrationData: {
    nickname: string;
    gender: string;
    zodiac: string;
    mbti: string;
    profession: string;
    hobbies: string[];
    childhood: { date: string; text: string };
    joy: { date: string; text: string };
    low: { date: string; text: string };
    future: { vision: string; ideal: string };
  };
  
  // 生命事件
  lifeEvents: LifeEvent[];
  
  // 剧本
  scripts: Script[];
  selectedScriptId: number | null;
  
  // 路径
  selectedPath: string | null;
  
  // Actions
  save: () => void;
  load: () => void;
  updateRegistration: (data: Partial<RegistrationData>) => void;
  addLifeEvent: (event: Omit<LifeEvent, 'id'>) => void;
  addScript: (script: Omit<Script, 'id' | 'date'>) => void;
  setPath: (path: string) => void;
  clear: () => void;
}
```

### 灵感标签数据

```typescript
const inspirationClusters = {
  childhood: ['秋千', '晚霞', '糖果', '奔跑', '蝉鸣', '雨后泥土', '旧书包', '风筝'],
  joy: ['海浪', '拥抱', '掌声', '晨曦', '破土而出', '默契', '星空', '释放'],
  low: ['落叶', '雨伞', '长廊', '深呼吸', '自愈', '沉潜', '坚韧', '等待', '破茧']
};
```

### 下拉选项数据

```typescript
const scriptStyles = [
  { value: '都市', label: '都市沉浮' },
  { value: '古风', label: '快意恩仇' },
  { value: '爱情', label: '唯美浪漫' },
  { value: '科幻', label: '星际远征' },
  { value: '喜剧', label: '荒诞不经' },
  { value: '悬疑', label: '迷雾重重' },
  { value: '恐怖', label: '午夜回响' }
];

const scriptLengths = [
  { value: '短', label: '极简' },
  { value: '中', label: '连载' },
  { value: '长', label: '史诗' }
];
```


## Correctness Properties

*A property is a characteristic or behavior that should hold true across all valid executions of a system—essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*


Based on the prework analysis, the following correctness properties have been identified:

### Property 1: State Persistence Round-Trip

*For any* valid application state object, serializing to localStorage and then deserializing on page reload SHALL produce an equivalent state object with all user data intact.

**Validates: Requirements 9.1, 9.2, 9.4**

### Property 2: Login Validation and Navigation

*For any* phone number input, the system SHALL:
- Accept only 11-digit numbers as valid
- Start countdown only for valid phone numbers
- Navigate to onboarding only when credentials match (phone + code "888888")
- Display error messages for all invalid inputs

**Validates: Requirements 2.3, 2.4, 2.5, 2.6**

### Property 3: Onboarding Step Progression

*For any* step number N (1-5), the onboarding flow SHALL:
- Display the correct content for step N
- Show "返回" button if and only if N > 1
- Preserve all form data when navigating between steps
- Update progress indicator to highlight step N

**Validates: Requirements 3.1, 3.8, 3.10, 3.11**

### Property 4: Inspiration Tag Appending

*For any* inspiration tag click in the onboarding flow, the corresponding textarea value SHALL be appended with the tag text, preserving any existing content.

**Validates: Requirements 3.7**

### Property 5: Timeline Event Ordering

*For any* collection of life events with different timestamps, the Timeline view SHALL display them in reverse chronological order (newest first), and each event card SHALL contain all required fields (title, date, content, aiFeedback).

**Validates: Requirements 5.3, 5.4**

### Property 6: Script Generation and Selection

*For any* script generation request with valid parameters, the system SHALL:
- Add the generated script to the scripts list
- Set it as the selected script
- Display it in the script view
- Allow selection of any historical script from the list

**Validates: Requirements 6.6, 6.7, 6.8, 6.9, 6.10**

### Property 7: Path Generation Conditional Display

*For any* dashboard state:
- If no script is selected, Path view SHALL display the "generate script first" prompt
- If a script is selected, Path view SHALL display the script theme and generation button
- After path generation, all path steps SHALL be displayed with sequential numbering

**Validates: Requirements 7.1, 7.2, 7.3, 7.4, 7.5**

### Property 8: Modal Open/Close Behavior

*For any* modal trigger action, the modal SHALL open with the correct content, and clicking the close button SHALL hide the modal and return to the previous state.

**Validates: Requirements 8.1, 8.3, 8.5, 11.4**

### Property 9: Corrupted State Recovery

*For any* corrupted or invalid JSON in localStorage, the State_Manager SHALL gracefully handle the error and initialize with default state values.

**Validates: Requirements 9.5**

## Error Handling

### 用户输入错误

| 场景 | 处理方式 |
|------|----------|
| 手机号格式错误 | 显示 alert 提示 "请输入正确的手机号" |
| 验证码错误 | 显示 alert 提示 "验证失败，请检查手机号或验证码" |
| 事件表单不完整 | 显示 alert 提示 "请完整填写记录" |
| 剧本主题为空 | 显示 alert 提示 "请输入主题" |

### AI 服务错误

| 场景 | 处理方式 |
|------|----------|
| API 请求失败 | 返回默认文本 "（AI 暂时陷入了沉思，请稍后再试）" |
| 网络超时 | 同上，使用 try-catch 捕获 |

### 状态持久化错误

| 场景 | 处理方式 |
|------|----------|
| localStorage 解析失败 | 使用 console.error 记录，使用默认状态 |
| localStorage 不可用 | 应用正常运行，数据不持久化 |

## Testing Strategy

### 单元测试 (Unit Tests)

使用 Vitest + React Testing Library：

1. **组件渲染测试**
   - GlassCard 渲染正确的样式类
   - GlassButton 各变体渲染正确
   - GlassInput 显示 label 和 placeholder
   - Modal 打开/关闭状态

2. **页面测试**
   - LoginPage 初始渲染
   - OnboardingPage 各步骤内容
   - DashboardPage 布局结构

3. **边缘情况**
   - 空数据状态显示
   - 长文本截断
   - 特殊字符处理

### 属性测试 (Property-Based Tests)

使用 fast-check 库，最少 100 次迭代：

1. **Property 1: State Round-Trip**
   - 生成随机状态对象
   - 序列化到 localStorage
   - 反序列化并比较

2. **Property 2: Login Validation**
   - 生成随机手机号字符串
   - 验证 11 位数字通过，其他拒绝

3. **Property 3: Step Progression**
   - 生成随机步骤序列
   - 验证数据保持和 UI 状态

4. **Property 5: Event Ordering**
   - 生成随机事件列表
   - 验证排序结果

### 测试配置

```javascript
// vitest.config.js
export default {
  test: {
    environment: 'jsdom',
    setupFiles: ['./src/test/setup.js'],
    coverage: {
      reporter: ['text', 'html'],
      exclude: ['node_modules/', 'src/test/']
    }
  }
}
```

### 测试标注格式

每个属性测试必须包含注释：

```javascript
/**
 * Feature: life-script-frontend
 * Property 1: State Persistence Round-Trip
 * Validates: Requirements 9.1, 9.2, 9.4
 */
test.prop([fc.record({...})])('state round-trip', (state) => {
  // test implementation
});
```
