Files
codex/codex-rs/tui/src/collaboration_modes.rs
T
Charley Cunningham 11958221a3 tui: add feature-gated /plan slash command to switch to Plan mode (#10103)
## Summary
Adds a simple `/plan` slash command in the TUI that switches the active
collaboration mode to Plan mode. The command is only available when the
`collaboration_modes` feature is enabled.

## Changes
- Add `plan_mask` helper in `codex-rs/tui/src/collaboration_modes.rs`
- Add `SlashCommand::Plan` metadata in
`codex-rs/tui/src/slash_command.rs`
- Implement and hard-gate `/plan` dispatch in
`codex-rs/tui/src/chatwidget.rs`
- Hide `/plan` when collaboration modes are disabled in
`codex-rs/tui/src/bottom_pane/slash_commands.rs`
- Update command popup tests in
`codex-rs/tui/src/bottom_pane/command_popup.rs`
- Add a focused unit test for `/plan` in
`codex-rs/tui/src/chatwidget/tests.rs`

## Behavior notes
- `/plan` is now a no-op if `Feature::CollaborationModes` is disabled.
- When enabled, `/plan` switches directly to Plan mode without opening
the picker.

## Codex author
`codex resume 019c05da-d7c3-7322-ae2c-3ca38d0ef702`
2026-01-29 16:40:43 -08:00

66 lines
2.0 KiB
Rust

use codex_core::models_manager::manager::ModelsManager;
use codex_protocol::config_types::CollaborationModeMask;
use codex_protocol::config_types::ModeKind;
fn is_tui_mode(kind: ModeKind) -> bool {
matches!(kind, ModeKind::Plan | ModeKind::Code)
}
fn filtered_presets(models_manager: &ModelsManager) -> Vec<CollaborationModeMask> {
models_manager
.list_collaboration_modes()
.into_iter()
.filter(|mask| mask.mode.is_some_and(is_tui_mode))
.collect()
}
pub(crate) fn presets_for_tui(models_manager: &ModelsManager) -> Vec<CollaborationModeMask> {
filtered_presets(models_manager)
}
pub(crate) fn default_mask(models_manager: &ModelsManager) -> Option<CollaborationModeMask> {
let presets = filtered_presets(models_manager);
presets
.iter()
.find(|mask| mask.mode == Some(ModeKind::Code))
.cloned()
.or_else(|| presets.into_iter().next())
}
pub(crate) fn mask_for_kind(
models_manager: &ModelsManager,
kind: ModeKind,
) -> Option<CollaborationModeMask> {
if !is_tui_mode(kind) {
return None;
}
filtered_presets(models_manager)
.into_iter()
.find(|mask| mask.mode == Some(kind))
}
/// Cycle to the next collaboration mode preset in list order.
pub(crate) fn next_mask(
models_manager: &ModelsManager,
current: Option<&CollaborationModeMask>,
) -> Option<CollaborationModeMask> {
let presets = filtered_presets(models_manager);
if presets.is_empty() {
return None;
}
let current_kind = current.and_then(|mask| mask.mode);
let next_index = presets
.iter()
.position(|mask| mask.mode == current_kind)
.map_or(0, |idx| (idx + 1) % presets.len());
presets.get(next_index).cloned()
}
pub(crate) fn code_mask(models_manager: &ModelsManager) -> Option<CollaborationModeMask> {
mask_for_kind(models_manager, ModeKind::Code)
}
pub(crate) fn plan_mask(models_manager: &ModelsManager) -> Option<CollaborationModeMask> {
mask_for_kind(models_manager, ModeKind::Plan)
}