コンパクトモード設計:競合分析と最適化
Ctrl+O によるコンパクト/詳細モードの切り替え — Claude Code との競合分析、現在の実装レビュー、および最適化の提案。
ユーザー向けドキュメント: Settings — ui.compactMode。
1. エグゼクティブサマリー
Qwen Code と Claude Code はどちらも、ツール出力のコンパクト表示と詳細表示を切り替える Ctrl+O ショートカットを提供していますが、設計思想、デフォルト状態、インタラクションモデルは根本的に異なります。本ドキュメントではソースレベルでの詳細な比較を行い、UX のギャップを特定した上で、Qwen Code 向けの最適化案を提案します。
| 項目 | Claude Code | Qwen Code |
|---|---|---|
| デフォルトモード | コンパクト (verbose=false) | 詳細 (compactMode=false) |
| 切り替えのセマンティクス | 詳細の一時的な表示 | 永続的な設定の切り替え |
| 永続化 | セッションのみ(再起動でリセット) | settings.json に保存 |
| スコープ | グローバル画面切り替え(プロンプト ↔ トランスクリプト) | コンポーネント単位のレンダリング切り替え |
| フローズンスナップショット | なし(概念なし) | なし(削除済み) |
| ツール単位の展開ヒント | あり (“ctrl+o to expand”) | あり (“Press Ctrl+O to show full tool output”) |
2. Claude Code の実装分析
2.1 アーキテクチャ
Claude Code はコンポーネント単位のレンダリング切り替えではなく、画面ベースのアプローチを採用しています:
┌──────────────────────────────────┐
│ AppState (Zustand) │
│ verbose: boolean (default: false)│
│ screen: 'prompt' | 'transcript' │
└──────────┬───────────────────────┘
│
┌─────┴──────┐
│ Ctrl+O │ 画面モードを切り替え
│ Handler │ レンダリングフラグではない
└─────┬──────┘
│
┌─────▼──────────────┐
│ REPL.tsx │
│ screen='prompt' → コンパクト表示(デフォルト)
│ screen='transcript'→ 詳細表示
└────────────────────────┘2.2 主要ソースファイル
| コンポーネント | ファイル | 主要ロジック |
|---|---|---|
| 切り替えハンドラ | src/hooks/useGlobalKeybindings.tsx:90-132 | screen を 'prompt' と 'transcript' 間で切り替え |
| キーバインディング | src/keybindings/defaultBindings.ts:44 | app:toggleTranscript |
| 状態定義 | src/state/AppStateStore.ts:472 | verbose: false(セッションのみ) |
| 展開ヒント | src/components/CtrlOToExpand.tsx:29-46 | ツール単位の “(ctrl+o to expand)” テキスト |
| メッセージフィルタ | src/components/Messages.tsx:93-151 | コンパクト表示用の filterForBriefTool() |
| 権限 | src/components/permissions/PermissionRequest.tsx | オーバーレイレイヤーでレンダリングされ、非表示にならない |
2.3 設計上の判断
- コンパクトがデフォルト。 ユーザーは最初からクリーンなインターフェースを利用でき、詳細表示はオプトイン形式です。
- セッションスコープ。
verboseは新しいセッションごとにfalseにリセットされます。Claude Code は、ユーザーは通常コンパクト表示を好み、詳細は一時的にだけ必要になると想定しています。 - 画面レベルの切り替え。 Ctrl+O はコンポーネントのレンダリング方法を変更するのではなく、画面全体を「プロンプト」画面(コンパクト)と「トランスクリプト」画面(詳細)に切り替えます。
- フローズンスナップショットなし。 スナップショットを固定する概念はありません。切り替えると、表示は現在の状態で即座に更新されます。
- 権限ダイアログは分離されています。 ツールの承認は専用のオーバーレイレイヤーでレンダリングされ、詳細/コンパクトの切り替えの影響を一切受けません。
- ツール単位のヒント。
CtrlOToExpandコンポーネントは、個々のツールが大量の出力を生成した際に文脈に応じたヒントを表示し、サブエージェントでは抑制されます。
2.4 ユーザーフロー
セッション開始 → コンパクトモード(デフォルト)
│
├─ ツール出力は1行に要約される
├─ 大量のツール出力には "(ctrl+o to expand)" ヒントが表示される
│
├─ ユーザーが Ctrl+O を押す
│ └─→ 画面がトランスクリプト(詳細表示)に切り替わる
│ └─ すべてのツール出力、思考プロセスなどが表示される
│
├─ ユーザーが再度 Ctrl+O を押す
│ └─→ 画面がプロンプト(コンパクト)に戻る
│
└─ セッション終了 → verbose が false にリセットされる3. Qwen Code の実装分析
3.1 アーキテクチャ
Qwen Code は、各 UI コンポーネントがコンテキストから読み取るコンポーネント単位のレンダリングフラグを使用しています:
┌─────────────────────────────────────┐
│ CompactModeContext │
│ compactMode: boolean (default: false)│
│ setCompactMode: (v) => void │
└──────────┬──────────────────────────┘
│
┌─────┴──────┐
│ Ctrl+O │ compactMode を切り替え
│ Handler │ 設定に永続化
└─────┬──────┘
│
┌─────▼──────────────────┐
│ 各コンポーネントが │
│ compactMode を読み取り │
│ レンダリング方法を決定 │
└────────────────────────┘
│
┌─────▼──────────────────────────────┐
│ ToolGroupMessage │
│ showCompact = compactMode │
│ && !hasConfirmingTool │
│ && !hasErrorTool │
│ && !isEmbeddedShellFocused │
│ && !isUserInitiated │
└────────────────────────────────────┘3.2 主要ソースファイル
| コンポーネント | ファイル | 主要ロジック |
|---|---|---|
| 切り替えハンドラ | AppContainer.tsx:1684-1690 | compactMode を切り替え、設定に永続化 |
| コンテキスト | CompactModeContext.tsx | compactMode, setCompactMode |
| ツールグループ | ToolGroupMessage.tsx:105-110 | 4つの強制展開条件を持つ showCompact |
| ツールメッセージ | ToolMessage.tsx:346-350 | コンパクトモードで displayRenderer を非表示 |
| コンパクト表示 | CompactToolGroupDisplay.tsx:49-108 | ステータスとヒントを含む1行要約 |
| 確認 | ToolConfirmationMessage.tsx:113-147 | 簡略化された3択のコンパクト承認 |
| ヒント | Tips.tsx:14-29 | 起動時のヒントローテーションにコンパクトモードのヒントを含む |
| 設定同期 | SettingsDialog.tsx:189-193 | CompactModeContext と同期 + refreshStatic |
| MainContent | MainContent.tsx:60-76 | 保留中の pendingHistoryItems をライブレンダリング |
| 思考 | HistoryItemDisplay.tsx:123-133 | コンパクトモードで gemini_thought を非表示 |
3.3 設計上の判断
- 詳細表示がデフォルト。 ユーザーはデフォルトですべてのツール出力と思考プロセスを確認できます。
- 永続的な設定。
compactModeはsettings.jsonに保存され、セッションを跨いで維持されます。 - コンポーネント単位のレンダリング。 各コンポーネントはコンテキストから
compactModeを読み取り、独自のレンダリングを調整します。 - 強制展開による保護。 重要なUI要素が常に表示されるよう、コンパクトモードを上書きする4つの条件が設定されています(確認、エラー、シェル、ユーザー操作)。
- スナップショットの固定なし。 切り替えは常にライブ出力を表示し、固定されたスナップショットは使用しません。
- 設定ダイアログとの同期。 設定からコンパクトモードを切り替えると、
setCompactModeを介して React の状態が即座に更新されます。 - 邪魔にならない発見性。 コンパクトモードは永続的なフッターインジケーターではなく、起動時の Tips ローテーションで紹介され、UI の混雑を回避しています。
3.4 ユーザーフロー
セッション開始 → 詳細モード(デフォルト)
│
├─ すべてのツール出力、思考、詳細が表示される
│
├─ ユーザーが Ctrl+O を押す(または設定で切り替え)
│ └─→ compactMode = true、永続化される
│ ├─ ツールグループが1行要約を表示
│ ├─ 思考/思考内容が非表示
│ └─ 確認、エラー、シェルは展開されたまま
│
├─ ユーザーが再度 Ctrl+O を押す
│ └─→ compactMode = false、永続化される
│ └─ すべての詳細が再度表示される
│
└─ 次回のセッション → 前回のセッションと同じモードで開始4. 主要な違いの詳細分析
4.1 デフォルトモードの設計思想
| 観点 | Claude Code(コンパクトがデフォルト) | Qwen Code(詳細がデフォルト) |
|---|---|---|
| 第一印象 | クリーンでミニマル — プロフェッショナルな印象 | 情報量豊富 — 完全な透明性 |
| 学習コスト | 詳細を見るには Ctrl+O を学ぶ必要がある | すぐにすべてを確認できる |
| ターゲットユーザー | ツールを信頼する経験豊富なユーザー | 内部の動作を理解したいユーザー |
| 情報過多 | デフォルトで回避 | 新規ユーザーで発生する可能性あり |
| 発見性 | ツール単位の “(ctrl+o to expand)” ヒント | 起動時 Tips ローテーション + ? ショートカット + /help |
分析: Claude Code のコンパクトデフォルトが機能するのは、ユーザーベースが一般的にツールを信頼しており、すべてのツール呼び出しを確認する必要がない経験豊富な開発者であるためです。Qwen Code の詳細デフォルトは、透明性を通じてユーザーの信頼を構築することが重要な初期段階において適切です。
4.2 永続化モデル
| 観点 | Claude Code | Qwen Code |
|---|---|---|
| 永続化の有無? | なし — セッションのみ | あり — settings.json に保存 |
| 理由 | 詳細表示は一時的な確認用 | モードはユーザーの好み |
| 再起動時の動作 | 常にコンパクトで開始 | 最後に使用したモードで開始 |
分析: Claude Code は詳細表示を一時的なニーズとして扱います(確認したら戻る)。Qwen Code は安定した設定として扱います(常に詳細が欲しいユーザーもいれば、常にコンパクトが欲しいユーザーもいる)。どちらも有効ですが、Qwen Code のアプローチの方が柔軟性があります。
4.3 確認ダイアログの保護
| 観点 | Claude Code | Qwen Code |
|---|---|---|
| メカニズム | オーバーレイ/モーダルレイヤー(構造的に分離) | showCompact 内の強制展開条件 |
| カバレッジ | 完全 — 承認が非表示になることはない | 完全 — 4つの条件がすべてのインタラクティブ状態をカバー |
| コンパクト確認UI | 該当なし(オーバーレイは常にフル表示) | 簡略化された3択の RadioButtonSelect |
分析: Claude Code のアーキテクチャ分離(オーバーレイレイヤー)の方が堅牢です。Qwen Code の強制展開アプローチは効果的ですが、新しいインタラクティブ状態が追加されるたびに条件リストに明示的に追加する必要があります。
4.4 レンダリングアプローチ
| 観点 | Claude Code | Qwen Code |
|---|---|---|
| 切り替えスコープ | 画面レベル(プロンプト ↔ トランスクリプト) | コンポーネントレベル(各コンポーネントが決定) |
| 粒度 | 一括切り替え | コンポーネント単位で細かく制御 |
| 柔軟性 | 低い — グローバルスイッチ | 高い — コンポーネントが上書き可能 |
| 一貫性 | 保証される | 各コンポーネントの実装に依存 |
分析: Qwen Code のコンポーネントレベルのアプローチは柔軟性が高い(例:特定の条件での強制展開)ですが、一貫性を維持するためにはより厳格な管理が必要です。Claude Code の画面レベルのアプローチはシンプルで、動作の一貫性が保証されます。
5. 最適化の提案
5.1 [P0] デフォルトを詳細表示のまま維持 — 変更不要
Qwen Code の詳細デフォルトは、現在の段階において適切な選択です。ツールに不慣れなユーザーは、信頼を構築するために透明性を必要とします。製品が成熟するにつれて、(Claude Code のように)コンパクトをデフォルトにすることを検討してください。
5.2 [P1] 大量出力に対するツール単位の展開
Claude Code は大量の出力を生成する個々のツールに “(ctrl+o to expand)” を表示します。Qwen Code には現在グローバルな切り替えのみがあります。以下の対応を検討してください:
- 単一のツールが N 行を超える出力を生成した場合、コンパクトモードでツール単位の「展開」ヒントを表示する。
- スコープ:将来の機能強化であり、現在の優先度は低め。
5.3 [P2] セッションスコープの上書きを検討
一部のユーザーはデフォルトでコンパクトモードを希望しつつ、特定のセッションでのみ詳細表示を必要とする場合があります。両方をサポートすることを検討してください:
settings.json→ 永続的なデフォルト(現在の動作)- セッション中の Ctrl+O → 現在のセッションのみ一時的に上書き(Claude Code の動作)
- セッション再起動時 →
settings.jsonの値に戻す
これにより、ユーザーは両方の利点を享受できます。実装には、「設定のデフォルト」と「セッションの上書き」の状態を分離する必要があります。
5.4 [P2] 確認ダイアログの構造的な分離
現在、確認ダイアログの保護は ToolGroupMessage 内の showCompact 条件に依存しています。より堅牢なアプローチを検討してください:
- 確認ダイアログを別のレイヤーでレンダリングする(Claude Code のオーバーレイアプローチと同様)。
- これにより、アーキテクチャ的にコンパクトモードが確認ダイアログに影響を与えることを不可能にできる。
- 現在の強制展開アプローチは正しく機能しているため、優先度は低め。
6. 現在の実装状況
feat/compact-mode-optimization ブランチの変更後:
| 機能 | 状態 | 備考 |
|---|---|---|
| 起動時 Tips ヒント | 完了 | Tips ローテーションにコンパクトモードのヒントを追加(邪魔にならない) |
| キーボードショートカット (?) の Ctrl+O | 完了 | KeyboardShortcuts コンポーネントに追加 |
| /help の Ctrl+O | 完了 | Help コンポーネントに追加 |
| 設定ダイアログ同期 | 完了 | compactMode を CompactModeContext と同期 |
| スナップショットの固定なし | 完了 | 切り替えは常にライブ出力を表示 |
| 確認ダイアログ保護 | 完了 | 強制展開 + WaitingForConfirmation ガード |
| シェル保護 | 完了 | !isEmbeddedShellFocused による強制展開 |
| エラー保護 | 完了 | !hasErrorTool による強制展開 |
| ユーザー向けドキュメント更新 | 完了 | settings.md, keyboard-shortcuts.md |
7. ファイルリファレンス
Qwen Code
| ファイル | 目的 |
|---|---|
packages/cli/src/ui/AppContainer.tsx | 切り替えハンドラ、状態の初期化、コンテキストプロバイダー |
packages/cli/src/ui/contexts/CompactModeContext.tsx | コンテキスト定義 |
packages/cli/src/ui/components/messages/ToolGroupMessage.tsx | 強制展開ロジック |
packages/cli/src/ui/components/messages/ToolMessage.tsx | ツール単位の出力非表示 |
packages/cli/src/ui/components/messages/CompactToolGroupDisplay.tsx | コンパクト表示のレンダリング |
packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx | コンパクト確認UI |
packages/cli/src/ui/components/MainContent.tsx | 保留中の履歴アイテムのレンダリング |
packages/cli/src/ui/components/Tips.tsx | コンパクトモードヒントを含む起動時 Tips |
packages/cli/src/ui/components/Help.tsx | /help ショートカットのエントリ |
packages/cli/src/ui/components/KeyboardShortcuts.tsx | ? ショートカットのエントリ |
packages/cli/src/ui/components/SettingsDialog.tsx | 設定同期 |
packages/cli/src/ui/components/HistoryItemDisplay.tsx | 思考内容の非表示 |
packages/cli/src/config/settingsSchema.ts | 設定の定義 |
packages/cli/src/config/keyBindings.ts | Ctrl+O のバインディング |
Claude Code(参考)
| ファイル | 目的 |
|---|---|
src/hooks/useGlobalKeybindings.tsx | 切り替えハンドラ |
src/state/AppStateStore.ts | 状態定義(verbose: false) |
src/components/CtrlOToExpand.tsx | ツール単位の展開ヒント |
src/components/Messages.tsx | 簡易メッセージフィルタ |
src/screens/REPL.tsx | 画面レベルのモード切り替え |
src/components/permissions/PermissionRequest.tsx | オーバーレイベースの確認 |