When switching themes, ratatui's incremental diff engine may miss
color-only changes in sidebar cells that were rendered with
theme-resolved UiTheme fields rather than palette constants routed
through the backend remap layer. This manifests as the sidebar
retaining the previous theme's colors until a window resize or
conversation turn triggers a full repaint.
Add a force_next_full_repaint flag on App that is set whenever a
theme or background_color ConfigUpdated event is processed. The
main render loop merges this into the existing force_terminal_repaint
mechanism, which clears the terminal and redraws every cell.
The task, work, agents, and context sidebar panels were using hardcoded
palette::DEEPSEEK_SKY, palette::TEXT_MUTED, palette::STATUS_WARNING etc.
constants (Whale dark theme colors) that never change when the user
switches themes. This caused the sidebar content to remain in Whale dark
colors even after switching to Claude, Catppuccin, Dracula, or other
community themes.
Root cause:
- task_panel_lines() used palette::DEEPSEEK_SKY, TEXT_MUTED, STATUS_* etc.
- work_panel_lines() and helpers used palette::TEXT_MUTED, STATUS_* etc.
- subagent_panel_lines() used palette::DEEPSEEK_SKY, TEXT_DIM, STATUS_* etc.
- render_context_panel() used palette::DEEPSEEK_SKY, TEXT_MUTED, TEXT_DIM
- tool_status_marker() returned hardcoded palette::STATUS_* colors
- agent_status_marker() returned hardcoded palette::STATUS_* colors
Fix:
- All sidebar panel functions now accept &UiTheme and use theme fields
- task_panel_lines: uses app.ui_theme directly
- work_panel_lines: passes ui_theme to all helpers
- subagent_panel_lines: accepts theme parameter
- render_context_panel: uses app.ui_theme
- tool_status_marker/agent_status_marker: accept theme parameter
- All palette::DEEPSEEK_SKY -> theme.accent_primary
- All palette::TEXT_MUTED -> theme.text_muted
- All palette::TEXT_DIM -> theme.text_dim
- All palette::STATUS_WARNING -> theme.warning
- All palette::STATUS_SUCCESS -> theme.success
- All palette::STATUS_ERROR -> theme.error_fg
This ensures sidebar panels immediately reflect the active theme when
switching, without requiring a conversation turn to trigger a refresh.
Also creates a theme modification guide for future contributors.
Support `! <command>` and `!command` in the TUI composer to run shell commands through the existing exec_shell path.
The shortcut keeps normal approval, sandbox, policy, transcript, and work-panel handling, while avoiding model context
pollution from local-only tool results.
Refs #1546
Refs #1722
Preserves auto_compact as opt-in, adds the saved threshold setting, keeps the 500K hard floor, and wires Ctrl+L as a manual compaction shortcut for context-pressure recovery.
Harvested from PR #1723 by @aboimpinto
Co-authored-by: Paulo Aboim Pinto <aboimpinto@gmail.com>
Remove unneeded return in utils.rs (crates/tui/src/utils.rs:256) that was caught by clippy on the new commit. Also run cargo fmt to satisfy format checks.
- Remove ToolCallBefore observer firing from tool_routing.rs (the turn-loop gate now handles it) to prevent double-firing hooks for each tool call (greptile P1).
- Wrap hook_executor.execute() call in tokio::task::spawn_blocking so the Tokio worker thread is not blocked by child.wait_timeout() during hook execution (greptile P1).
Override `supports_parallel()` to return `true` in `WebSearchTool`,
allowing the engine to batch multiple concurrent web_search calls
into a `FuturesUnordered` parallel group instead of serializing them.
The tool is already read-only, auto-approved, and non-interactive —
parallel-safe by all other criteria. This change removes the final
gate (`supports_parallel() -> false` default) so co-issued searches
run concurrently rather than one-at-a-time.
Closes the ~55s serial wall-clock for 3 simultaneous web searches
(now ~20s, the slowest individual call).
Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit a7dcf63c556268b53ff430747ae2e141e4cd4451)
When MCP servers return tool schemas, the field order within each schema
object and the order of entries in required / dependentRequired arrays
can vary across reconnections. This causes the serialized tool catalog
bytes to change even when the logical schema is unchanged, busting
DeepSeek's KV prefix cache.
Add schema_canonicalize::canonicalize_schema which recursively:
- Sorts every required array alphabetically
- Sorts every dependentRequired sub-array alphabetically
- Rebuilds object keys in alphabetical order
- Recurses into all nested objects and arrays
The canonicalize step runs after schema_sanitize in build_api_tools,
so each tool's input_schema is first cleaned then byte-stabilized.
The existing OnceLock api_cache pins the result, ensuring the tool
catalog bytes are identical across reads and across process restarts.
8 unit tests cover: required sorting, dependentRequired sorting,
equivalent-ordering byte match, recursive nesting, empty schemas,
deeply nested schemas, non-required array preservation, and key
ordering.
(cherry picked from commit 7cee9cd5e12a74e8072bf2f6a1b18555ed0db0bf)