Skip to content
121 changes: 121 additions & 0 deletions docs/ui-rendering.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# UI 渲染与界面层

> 记录 Claude Code 终端 UI(Ink 框架)的渲染行为、布局模式、常见问题及修复。

## LogoV2 启动欢迎界面

### 组件结构

```
LogoV2.tsx (src/components/LogoV2/)
├── CondensedLogo.tsx — 缩略模式(无边框,日常最常见)
├── LogoV2.tsx — 完整模式(有边框)/ 紧凑模式(有边框)
├── CondensedLogo.tsx — 缩略模式(无边框,日常最常见)
├── Clawd.tsx — ASCII 猫吉祥物
├── AnimatedClawd.tsx — 动画版 Clawd
├── FeedColumn.tsx — 右侧信息流(活动记录/更新日志)
└── WelcomeV2.tsx — 首次 Onboarding 欢迎文本
```
Comment on lines +9 to +18
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add language specifier to fenced code block.

The component tree diagram should have a language specifier for proper rendering. Since this is a plain text tree structure, use text or plaintext.

📝 Suggested fix
-```
+```text
 LogoV2.tsx (src/components/LogoV2/)
 ├── CondensedLogo.tsx   — 缩略模式(无边框,日常最常见)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```
LogoV2.tsx (src/components/LogoV2/)
├── CondensedLogo.tsx — 缩略模式(无边框,日常最常见)
├── LogoV2.tsx — 完整模式(有边框)/ 紧凑模式(有边框)
├── CondensedLogo.tsx — 缩略模式(无边框,日常最常见)
├── Clawd.tsx — ASCII 猫吉祥物
├── AnimatedClawd.tsx — 动画版 Clawd
├── FeedColumn.tsx — 右侧信息流(活动记录/更新日志)
└── WelcomeV2.tsx — 首次 Onboarding 欢迎文本
```
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 9-9: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/ui-rendering.md` around lines 9 - 18, The fenced code block containing
the LogoV2 component tree in docs/ui-rendering.md lacks a language specifier;
update the opening fence for that diagram to include a plaintext specifier
(e.g., replace ``` with ```text or ```plaintext) so the tree (LogoV2.tsx,
CondensedLogo.tsx, Clawd.tsx, AnimatedClawd.tsx, FeedColumn.tsx, WelcomeV2.tsx)
renders correctly as plain text.

Comment on lines +10 to +18
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Duplicate entry in component tree.

CondensedLogo.tsx is listed twice (lines 11 and 13) with identical descriptions. Remove the duplicate.

📝 Suggested fix
 LogoV2.tsx (src/components/LogoV2/)
 ├── CondensedLogo.tsx   — 缩略模式(无边框,日常最常见)
 ├── LogoV2.tsx          — 完整模式(有边框)/ 紧凑模式(有边框)
-├── CondensedLogo.tsx   — 缩略模式(无边框,日常最常见)
 ├── Clawd.tsx           — ASCII 猫吉祥物
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
LogoV2.tsx (src/components/LogoV2/)
├── CondensedLogo.tsx — 缩略模式(无边框,日常最常见)
├── LogoV2.tsx — 完整模式(有边框)/ 紧凑模式(有边框)
├── CondensedLogo.tsx — 缩略模式(无边框,日常最常见)
├── Clawd.tsx — ASCII 猫吉祥物
├── AnimatedClawd.tsx — 动画版 Clawd
├── FeedColumn.tsx — 右侧信息流(活动记录/更新日志)
└── WelcomeV2.tsx — 首次 Onboarding 欢迎文本
```
LogoV2.tsx (src/components/LogoV2/)
├── CondensedLogo.tsx — 缩略模式(无边框,日常最常见)
├── LogoV2.tsx — 完整模式(有边框)/ 紧凑模式(有边框)
├── Clawd.tsx — ASCII 猫吉祥物
├── AnimatedClawd.tsx — 动画版 Clawd
├── FeedColumn.tsx — 右侧信息流(活动记录/更新日志)
└── WelcomeV2.tsx — 首次 Onboarding 欢迎文本
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/ui-rendering.md` around lines 10 - 18, The component tree in
docs/ui-rendering.md contains a duplicate entry for CondensedLogo.tsx (appears
twice in the LogoV2.tsx list); remove the redundant second occurrence so
CondensedLogo.tsx appears only once and ensure the list items
(CondensedLogo.tsx, LogoV2.tsx, Clawd.tsx, AnimatedClawd.tsx, FeedColumn.tsx,
WelcomeV2.tsx) remain correctly ordered and formatted.


### 三种渲染模式

| 模式 | 触发条件 | 边框 | 说明 |
|------|---------|------|------|
| **缩略模式** (Condensed) | ~~无 Release Notes 且无首次引导~~ | ❌ | **已永久关闭**(2026-04-10),不再进入 |
| **紧凑模式** (Compact) | 终端宽度 < 70 列 | ✅ | 紫色圆角边框,内容居中 |
| **完整模式** (Full) | 默认路径 | ✅ | 紫色圆角边框,双栏布局 + 信息流 |

> **注**:缩略模式已在 `LogoV2.tsx` 中永久关闭(`isCondensedMode = false`),所有启动都走完整模式或紧凑模式。

### 模式判断逻辑(源码:`src/components/LogoV2/LogoV2.tsx`)

```typescript
// 原始逻辑:三种模式切换
const isCondensedMode =
!hasReleaseNotes &&
!showOnboarding &&
!isEnvTruthy(process.env.CLAUDE_CODE_FORCE_FULL_LOGO)

// 环境变量强制:始终显示完整模式
// CLAUDE_CODE_FORCE_FULL_LOGO=1 bun run dev
```

**CondensedLogo 内部**(`src/components/LogoV2/CondensedLogo.tsx`):无任何边框,纯文本 + ASCII 猫。

### 边框渲染机制

边框由 `@anthropic/ink` 的 `Box` 组件通过 `cli-boxes` 库绘制:

- `borderStyle="round"` → 使用 `cli-boxes` 的 `round` 样式(`╭─╮` `│` `╰─╯`)
- `borderColor="claude"` → 紫色主题色
- `borderText` → 边框顶部嵌入标题文本 "Claude Code"

详见 `packages/@ant/ink/src/core/render-border.ts`。

## 常见问题

### 问题:启动时欢迎边框时有时无

**原因**:日常启动(无 Release Notes、非首次使用)走缩略模式,不显示边框。

**修复**:在 `LogoV2.tsx` 中永久关闭缩略模式:

```diff
- const isCondensedMode =
- !hasReleaseNotes &&
- !showOnboarding &&
- !isEnvTruthy(process.env.CLAUDE_CODE_FORCE_FULL_LOGO)
+ const isCondensedMode = false

- if (
- !hasReleaseNotes &&
- !showOnboarding &&
- !isEnvTruthy(process.env.CLAUDE_CODE_FORCE_FULL_LOGO)
- ) {
+ if (false) {
return <CondensedLogo />
}
```

修改后,无论何种情况启动,都会显示完整模式的紫色圆角边框。

**影响**:
- 每次启动都会渲染完整的边框 + 信息流(活动记录或更新日志)
- 轻微增加启动渲染开销(从 0 边框到有边框双栏)
- 不再走 CondensedLogo(节省了 GuestPassesUpsell 等副作用计数逻辑)

### 问题:终端宽度不足导致边框变形

**原因**:`getLayoutMode(columns)` 在终端 < 70 列时切换到 compact 模式。

**参考**:`src/utils/logoV2Utils.ts` — `getLayoutMode(columns: number): LayoutMode`

### 问题:Recent Activity 显示 "No recent activity"

**原因**:`src/setup.ts` 中 `getRecentActivity()` 只在 `hasReleaseNotes` 为 true 时才调用。日常启动(无新 Release Notes)时,`cachedActivity` 始终为空数组。

**修复**(`src/setup.ts` 第 383-395 行):

```diff
if (!isBareMode()) {
- const { hasReleaseNotes } = await checkForReleaseNotes(
- getGlobalConfig().lastReleaseNotesSeen,
- )
- if (hasReleaseNotes) {
- await getRecentActivity()
- }
+ // Populate release notes cache (side effect: fetches changelog if needed)
+ void checkForReleaseNotes(getGlobalConfig().lastReleaseNotesSeen)
+ // Load recent activity unconditionally (not tied to release notes)
+ try {
+ await getRecentActivity()
+ } catch (error) {
+ logError('Failed to load recent activity:', error)
+ }
}
```

**关键变化**:
1. `getRecentActivity()` 从 `if (hasReleaseNotes)` 块中移出,无条件调用
2. 增加 try-catch 防止损坏的会话文件阻塞启动
3. `checkForReleaseNotes` 改为 `void` 调用(保留 cache 填充的副作用,但不阻塞等待结果)
Loading