第十七节:Screens 界面系统
REPL/Doctor/Resume

作者:小学子 📚 | 日期:2026年4月2日 | 第一阶段 · 模块一


# 第一阶段 · 模块一 · 第十七节:Screens 界面系统

核心问题

什么是 Screens?Claude Code 有哪些主要界面?REPL、Doctor、Resume 等页面是如何实现的?如何自定义 Screen?


◇ 本节位置


        Claude Code 全局架构
        
        ┌─────────────────────────────────────────────────────────────────────┐
        │  UI 层(ui/)                                                       │
        │                                                                      │
        │  Screens ← 本节                                                     │
        │  ├── REPL 交互界面                                                  │
        │  ├── Doctor 诊断界面                                                │
        │  ├── Resume 恢复界面                                                │
        │  └── 自定义界面                                                     │
        └─────────────────────────────────────────────────────────────────────┘
        


一、Screens 概述

1.1 什么是 Screen

源码位置:`src/ui/screens/`

Screen 是 Claude Code 的独立界面单元,每个 Screen 负责特定的功能或用户场景。


        ┌─────────────────────────────────────────────────────────────────────┐
        │  Claude Code 界面                                                    │
        │                                                                      │
        │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐            │
        │  │    REPL     │  │   Doctor    │  │   Resume     │            │
        │  │   主交互    │  │    诊断    │  │    恢复     │            │
        │  └──────────────┘  └──────────────┘  └──────────────┘            │
        └─────────────────────────────────────────────────────────────────────┘
        

1.2 Screen 类型


        // Screen 类型
        
        type ScreenType =
          | 'repl'       // REPL 交互界面
          | 'doctor'      // 诊断界面
          | 'resume'      // 会话恢复界面
          | 'onboarding'  // 引导界面
          | 'settings'    // 设置界面
        

1.3 五问分析

问 1:Screen 和 Component 有什么区别?


        // Component:
        // - 可复用的 UI 单元
        // - 可以嵌入任何地方
        
        // Screen:
        // - 完整的页面
        // - 包含多个 Component
        // - 有独立路由/状态
        


二、REPL Screen

2.1 REPL 实现

源码位置:`src/ui/screens/ReplScreen.tsx`


        // REPL 主界面
        
        function ReplScreen() {
          const [input, setInput] = useState('')
          const [history, setHistory] = useState<Message[]>([])
          const [isProcessing, setIsProcessing] = useState(false)
          
          return (
            <div className="repl-screen">
              <HistoryPanel messages={history} />
              <InputPanel
                value={input}
                onChange={setInput}
                disabled={isProcessing}
              />
            </div>
          )
        }
        

2.2 消息历史


        // 消息历史
        
        interface Message {
          id: string
          role: 'user' | 'assistant' | 'system'
          content: string
          timestamp: Date
          attachments?: Attachment[]
        }
        

2.3 输出缓冲


        // 流式输出处理
        
        function handleStreamChunk(chunk: string) {
          // 1. 追加到当前消息
          appendToCurrentMessage(chunk)
          
          // 2. 实时渲染
          requestAnimationFrame(() => {
            scrollToBottom()
          })
        }
        


三、Doctor Screen

3.1 Doctor 诊断

源码位置:`src/ui/screens/DoctorScreen.tsx`


        // Doctor 诊断界面
        
        function DoctorScreen() {
          const [diagnostics, setDiagnostics] = useState<Diagnostic[]>([])
          const [isRunning, setIsRunning] = useState(false)
          
          const runDiagnostics = async () => {
            setIsRunning(true)
            const results = await runAllDiagnostics()
            setDiagnostics(results)
            setIsRunning(false)
          }
          
          return (
            <div className="doctor-screen">
              <DiagnosticHeader />
              <DiagnosticList items={diagnostics} />
              <RunButton onClick={runDiagnostics} loading={isRunning} />
            </div>
          )
        }
        

3.2 诊断项


        // 诊断项
        
        interface Diagnostic {
          name: string
          status: 'pass' | 'fail' | 'warning'
          message: string
          details?: string
          fixAction?: () => void
        }
        


四、Resume Screen

4.1 会话恢复

源码位置:`src/ui/screens/ResumeScreen.tsx`


        // Resume 恢复界面
        
        function ResumeScreen() {
          const [sessions, setSessions] = useState<Session[]>([])
          const [selectedSession, setSelectedSession] = useState<Session | null>(null)
          
          return (
            <div className="resume-screen">
              <SessionList
                sessions={sessions}
                onSelect={setSelectedSession}
              />
              <SessionPreview session={selectedSession} />
              <ResumeButton disabled={!selectedSession} />
            </div>
          )
        }
        

4.2 会话预览


        // 会话预览
        
        function SessionPreview({ session }: { session: Session }) {
          return (
            <div className="session-preview">
              <h3>{session.title}</h3>
              <p className="session-date">
                {formatDate(session.createdAt)}
              </p>
              <div className="session-summary">
                {session.messageCount} 条消息
              </div>
              <MessageList messages={session.messages.slice(-5)} />
            </div>
          )
        }
        


五、Screen 路由

5.1 路由配置


        // Screen 路由
        
        const screenRoutes = {
          '/': ReplScreen,
          '/doctor': DoctorScreen,
          '/resume': ResumeScreen,
          '/settings': SettingsScreen,
          '/onboarding': OnboardingScreen
        }
        

5.2 导航


        // Screen 导航
        
        function navigateTo(path: string) {
          window.location.hash = path
        }
        
        // 或使用 React Router
        <Link to="/doctor">打开 Doctor</Link>
        


六、自定义 Screen

6.1 创建自定义 Screen


        // 自定义 Screen
        
        function CustomScreen() {
          return (
            <div className="custom-screen">
              <h2>My Custom Screen</h2>
              {/* 自定义内容 */}
            </div>
          )
        }
        
        // 注册 Screen
        registerScreen('custom', CustomScreen)
        

6.2 Screen 生命周期


        // Screen 生命周期
        
        interface ScreenLifecycle {
          onEnter?: () => void      // 进入时
          onLeave?: () => void       // 离开时
          onResume?: () => void      // 重新激活时
          onPause?: () => void       // 暂停时
        }
        


七、思考题

思考题 1:如何选择使用哪个 Screen?**

答案


        // 选择依据:
        
        // 1. REPL
        // - 主交互界面
        // - 大部分时间使用
        
        // 2. Doctor
        // - 排错时使用
        // - 检查配置问题
        
        // 3. Resume
        // - 恢复历史会话
        // - 继续之前的工作
        


思考题 2:如何调试 Screen 问题?**

答案


        // 调试方法:
        
        // 1. 检查路由
        // - URL 是否正确
        // - 路由是否注册
        
        // 2. 检查 Props
        // - 数据是否传入
        // - 状态是否正确
        
        // 3. 检查生命周期
        // - onEnter 是否执行
        // - 清理是否正确
        



思考题 4:Screen 路由如何实现?

答案


        // 路由实现:
        
        // 1. 路由配置
        const routes = {
          '/': ReplScreen,
          '/doctor': DoctorScreen,
          '/resume': ResumeScreen
        }
        
        // 2. 路由匹配
        function matchRoute(path: string): Screen {
          return routes[path] || DefaultScreen
        }
        
        // 3. 导航
        function navigate(path: string): void {
          window.location.hash = path
        }
        
        // 4. 守卫
        function canNavigate(from: string, to: string): boolean {
          // 检查权限
          // 返回 true/false
        }
        


思考题 5:Screen 生命周期如何管理?

答案


        // 生命周期管理:
        
        interface ScreenLifecycle {
          onEnter(): void      // 进入时
          onLeave(): void       // 离开时
          onResume(): void      // 重新激活
          onPause(): void       // 暂停时
        }
        
        // 实现:
        class ScreenManager {
          private currentScreen: Screen | null = null
          
          navigate(newScreen: Screen): void {
            // 1. 调用当前屏幕的 onLeave
            this.currentScreen?.onLeave()
            
            // 2. 切换屏幕
            this.currentScreen = newScreen
            
            // 3. 调用新屏幕的 onEnter
            this.currentScreen.onEnter()
          }
        }
        

八、延伸阅读

资源说明
`src/ui/screens/ReplScreen.tsx`REPL 实现
`src/ui/screens/DoctorScreen.tsx`Doctor 实现
`src/ui/screens/ResumeScreen.tsx`Resume 实现