Back to Qwen Code

Compact Mode Design: Competitive Analysis & Optimization

docs/design/compact-mode/compact-mode-design.md

0.15.618.4 KB
Original Source

Compact Mode Design: Competitive Analysis & Optimization

Ctrl+O compact/verbose mode toggle — competitive analysis with Claude Code, current implementation review, and optimization recommendations.

User documentation: Settings — ui.compactMode.

1. Executive Summary

Qwen Code and Claude Code both provide a Ctrl+O shortcut for toggling between compact and detailed tool output views, but the design philosophy, default state, and interaction model differ fundamentally. This document provides a deep source-level comparison, identifies UX gaps, and proposes optimizations for Qwen Code.

DimensionClaude CodeQwen Code
Default modeCompact (verbose=false)Verbose (compactMode=false)
Toggle semanticsTemporary peek at detailsPersistent preference switch
PersistenceSession-only, resets on restartPersisted to settings.json
ScopeGlobal screen switch (prompt ↔ transcript)Per-component rendering toggle
Frozen snapshotNone (no concept)None (removed)
Per-tool expand hintYes ("ctrl+o to expand")Yes ("Press Ctrl+O to show full tool output")

2. Claude Code Implementation Analysis

2.1 Architecture

Claude Code uses a screen-based approach rather than a component-level rendering toggle:

┌──────────────────────────────────┐
│         AppState (Zustand)       │
│  verbose: boolean (default: false)│
│  screen: 'prompt' | 'transcript' │
└──────────┬───────────────────────┘
           │
     ┌─────┴──────┐
     │  Ctrl+O    │  toggles screen mode
     │  Handler    │  NOT a rendering flag
     └─────┬──────┘
           │
     ┌─────▼──────────────┐
     │    REPL.tsx         │
     │  screen='prompt'  → compact view (default)
     │  screen='transcript'→ detailed view
     └────────────────────┘

2.2 Key Source Files

ComponentFileKey Logic
Toggle handlersrc/hooks/useGlobalKeybindings.tsx:90-132Switches screen between 'prompt' and 'transcript'
Keybindingsrc/keybindings/defaultBindings.ts:44app:toggleTranscript
State definitionsrc/state/AppStateStore.ts:472verbose: false (session-only)
Expand hintsrc/components/CtrlOToExpand.tsx:29-46Per-tool "(ctrl+o to expand)" text
Message filtersrc/components/Messages.tsx:93-151filterForBriefTool() for compact view
Permissionsrc/components/permissions/PermissionRequest.tsxRendered in overlay layer, never hidden

2.3 Design Decisions

  1. Compact is the default. Users see a clean interface out of the box; detail is opt-in.
  2. Session-scoped. verbose resets to false on every new session — Claude Code assumes users generally prefer the compact view and only need details temporarily.
  3. Screen-level toggle. Ctrl+O doesn't change how components render; it switches the entire display between a "prompt" screen (compact) and a "transcript" screen (detailed).
  4. No frozen snapshot. There is no snapshot freezing concept. When toggling, the display updates immediately with current state.
  5. Permission dialogs are separate. Tool approvals are rendered in a dedicated overlay layer that is never affected by the verbose/compact toggle.
  6. Per-tool hint. CtrlOToExpand component shows a contextual hint on individual tools when they produce large output, suppressed in sub-agents.

2.4 User Flow

Session start → compact mode (default)
     │
     ├─ Tool outputs are summarized in a single line
     ├─ Large tool output shows "(ctrl+o to expand)" hint
     │
     ├─ User presses Ctrl+O
     │     └─→ Screen switches to transcript (detailed view)
     │         └─ User sees all tool output, thinking, etc.
     │
     ├─ User presses Ctrl+O again
     │     └─→ Screen switches back to prompt (compact)
     │
     └─ Session ends → verbose resets to false

3. Qwen Code Implementation Analysis

3.1 Architecture

Qwen Code uses a component-level rendering flag that each UI component reads from context:

┌─────────────────────────────────────┐
│      CompactModeContext             │
│  compactMode: boolean (default: false)│
│  setCompactMode: (v) => void        │
└──────────┬──────────────────────────┘
           │
     ┌─────┴──────┐
     │  Ctrl+O    │  toggles compactMode
     │  Handler    │  persists to settings
     └─────┬──────┘
           │
     ┌─────▼──────────────────┐
     │  Each component reads  │
     │  compactMode and       │
     │  decides how to render │
     └────────────────────────┘
           │
     ┌─────▼──────────────────────────────┐
     │  ToolGroupMessage                   │
     │    showCompact = compactMode        │
     │      && !hasConfirmingTool          │
     │      && !hasErrorTool               │
     │      && !isEmbeddedShellFocused     │
     │      && !isUserInitiated            │
     └────────────────────────────────────┘

3.2 Key Source Files

ComponentFileKey Logic
Toggle handlerAppContainer.tsx:1684-1690Toggles compactMode, persists to settings
ContextCompactModeContext.tsxcompactMode, setCompactMode
Tool groupToolGroupMessage.tsx:105-110showCompact with 4 force-expand conditions
Tool messageToolMessage.tsx:346-350Hides displayRenderer in compact mode
Compact displayCompactToolGroupDisplay.tsx:49-108Single-line summary with status + hint
ConfirmationToolConfirmationMessage.tsx:113-147Simplified 3-option compact approval
TipsTips.tsx:14-29Startup tip rotation includes compact mode hint
Settings syncSettingsDialog.tsx:189-193Syncs with CompactModeContext + refreshStatic
MainContentMainContent.tsx:60-76Renders live pendingHistoryItems
ThinkingHistoryItemDisplay.tsx:123-133Hides gemini_thought in compact mode

3.3 Design Decisions

  1. Verbose is the default. Users see all tool output and thinking by default.
  2. Persistent preference. compactMode is saved to settings.json and survives across sessions.
  3. Component-level rendering. Each component reads compactMode from context and adjusts its own rendering.
  4. Force-expand protection. Four conditions override compact mode to ensure critical UI elements are always visible (confirmations, errors, shell, user-initiated).
  5. No snapshot freezing. The toggle always shows live output — no frozen snapshots.
  6. Settings dialog sync. Toggling compact mode from Settings updates React state immediately via setCompactMode.
  7. Non-intrusive discoverability. Compact mode is introduced via the startup Tips rotation rather than a persistent footer indicator, avoiding UI clutter.

3.4 User Flow

Session start → verbose mode (default)
     │
     ├─ All tool outputs, thinking, details visible
     │
     ├─ User presses Ctrl+O (or toggles in Settings)
     │     └─→ compactMode = true, persisted
     │         ├─ Tool groups show single-line summary
     │         ├─ Thinking/thought content hidden
     │         └─ Confirmations, errors, shell still expanded
     │
     ├─ User presses Ctrl+O again
     │     └─→ compactMode = false, persisted
     │         └─ All details visible again
     │
     └─ Next session → same mode as last session

4. Key Differences Deep Dive

4.1 Default Mode Philosophy

AspectClaude Code (compact default)Qwen Code (verbose default)
First impressionClean, minimal — professional feelInformation-rich — full transparency
Learning curveUser must learn Ctrl+O to see detailsUser can immediately see everything
Target audienceExperienced users who trust the toolUsers who want to understand what's happening
Information overloadAvoided by defaultPossible for new users
DiscoverabilityPer-tool "(ctrl+o to expand)" hintsStartup Tips rotation + ? shortcuts + /help

Analysis: Claude Code's compact default works because its user base is generally experienced developers who trust the tool and don't need to see every tool invocation. Qwen Code's verbose default is appropriate for its earlier stage where building user trust through transparency is important.

4.2 Persistence Model

AspectClaude CodeQwen Code
Persisted?No — session-onlyYes — to settings.json
RationaleVerbose is temporary peekMode is user preference
Restart behaviorAlways starts compactStarts with last-used mode

Analysis: Claude Code treats detail viewing as a momentary need — you look, then go back. Qwen Code treats it as a stable preference — some users always want details, others always want compact. Both are valid; Qwen Code's approach is more flexible.

4.3 Confirmation Protection

AspectClaude CodeQwen Code
MechanismOverlay/modal layer (structurally separate)Force-expand conditions in showCompact
CoverageComplete — approvals can never be hiddenComplete — 4 conditions cover all interactive states
Compact confirmation UIN/A (overlay is always full)Simplified 3-option RadioButtonSelect

Analysis: Claude Code's architectural separation (overlay layer) is more robust. Qwen Code's force-expand approach is effective but requires each new interactive state to be explicitly added to the condition list.

4.4 Rendering Approach

AspectClaude CodeQwen Code
Toggle scopeScreen-level (prompt ↔ transcript)Component-level (each component decides)
GranularityAll-or-nothingFine-grained per component
FlexibilityLow — global switchHigh — components can override
ConsistencyGuaranteedDepends on each component's implementation

Analysis: Qwen Code's component-level approach is more flexible (e.g., force-expand for specific conditions) but requires more discipline to maintain consistency. Claude Code's screen-level approach is simpler and guarantees consistent behavior.

5. Optimization Recommendations

5.1 [P0] Keep Verbose as Default — No Change Needed

Qwen Code's verbose default is the right choice for its current stage. Users who are new to the tool need transparency to build trust. As the product matures, consider making compact the default (like Claude Code).

5.2 [P1] Per-Tool Expansion for Large Outputs

Claude Code shows "(ctrl+o to expand)" on individual tools that produce large output. Qwen Code currently only has a global toggle. Consider:

  • When a single tool produces output exceeding N lines, show a per-tool "expand" hint in compact mode.
  • Scope: future enhancement, not current priority.

5.3 [P2] Consider Session-Scoped Override

Some users may want compact mode as their default but occasionally need verbose for a specific session. Consider supporting both:

  • settings.json → persistent default (current behavior)
  • Ctrl+O during session → temporary override for current session only (Claude Code behavior)
  • On session restart → revert to settings.json value

This gives users the best of both worlds. Implementation would require separating "settings default" from "session override" state.

5.4 [P2] Structural Separation for Confirmations

Currently, confirmation protection relies on showCompact conditions in ToolGroupMessage. Consider a more robust approach:

  • Render confirmations in a separate layer (like Claude Code's overlay approach).
  • This would make it architecturally impossible for compact mode to affect confirmations.
  • Lower priority since the current force-expand approach works correctly.

6. Current Implementation Status

After the feat/compact-mode-optimization branch changes:

FeatureStatusNotes
Startup Tips hintDoneCompact mode tip in Tips rotation (non-intrusive)
Ctrl+O in keyboard shortcuts (?)DoneAdded to KeyboardShortcuts component
Ctrl+O in /helpDoneAdded to Help component
Settings dialog syncDoneSyncs compactMode with CompactModeContext
No snapshot freezingDoneToggle always shows live output
Confirmation protectionDoneForce-expand + WaitingForConfirmation guard
Shell protectionDone!isEmbeddedShellFocused force-expand
Error protectionDone!hasErrorTool force-expand
User docs updatedDonesettings.md, keyboard-shortcuts.md

7. File Reference

Qwen Code

FilePurpose
packages/cli/src/ui/AppContainer.tsxToggle handler, state initialization, context provider
packages/cli/src/ui/contexts/CompactModeContext.tsxContext definition
packages/cli/src/ui/components/messages/ToolGroupMessage.tsxForce-expand logic
packages/cli/src/ui/components/messages/ToolMessage.tsxPer-tool output hiding
packages/cli/src/ui/components/messages/CompactToolGroupDisplay.tsxCompact view rendering
packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsxCompact confirmation UI
packages/cli/src/ui/components/MainContent.tsxPending history items rendering
packages/cli/src/ui/components/Tips.tsxStartup tip with compact mode hint
packages/cli/src/ui/components/Help.tsx/help shortcut entry
packages/cli/src/ui/components/KeyboardShortcuts.tsx? shortcut entry
packages/cli/src/ui/components/SettingsDialog.tsxSettings sync
packages/cli/src/ui/components/HistoryItemDisplay.tsxThinking content hiding
packages/cli/src/config/settingsSchema.tsSetting definition
packages/cli/src/config/keyBindings.tsCtrl+O binding

Claude Code (Reference)

FilePurpose
src/hooks/useGlobalKeybindings.tsxToggle handler
src/state/AppStateStore.tsState definition (verbose: false)
src/components/CtrlOToExpand.tsxPer-tool expand hint
src/components/Messages.tsxBrief message filter
src/screens/REPL.tsxScreen-level mode switching
src/components/permissions/PermissionRequest.tsxOverlay-based confirmation