Skip to content

Commit 0299264

Browse files
fix(config): Add config toggle for working messages (#464)
## Summary - Add tui.custom_working_messages config, defaulting to true, with TOML parsing in core and ACP config loaders. - Add a /config toggle that persists the setting and updates the active status indicator. - Use plain Working status text when custom working messages are disabled, and document the setting. ## Test Plan - cargo test -p codex-core --lib tui_config_custom_working_messages - cargo test -p nori-acp test_nori_config --lib - cargo test -p nori-tui custom_working_messages --lib - cargo clippy -p codex-core -p nori-acp -p nori-tui --lib --all-targets - cargo build --bin nori - cargo build -p mock-acp-agent - cargo test -p tui-pty-e2e - cargo fmt - git diff --check Co-authored-by: Cliff <clifford@tilework.tech> Co-authored-by: Clifford Ressel <CSRessel@gmail.com>
1 parent 0d92100 commit 0299264

20 files changed

Lines changed: 221 additions & 42 deletions

File tree

nori-rs/acp/docs.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ Three config enums control notification behavior, all stored in the `[tui]` sect
163163

164164
The `AcpBackendConfig` struct carries both `os_notifications` and `notify_after_idle` so the backend can configure the `UserNotifier` and the idle timer respectively. Terminal notifications flow separately through `codex-core`'s `Config::tui_notifications` bool to the TUI's `ChatWidget::notify()` method.
165165

166+
**TUI Display Configuration** (`config/types/mod.rs`):
167+
168+
The `[tui]` section also owns display-only preferences consumed by `@/nori-rs/tui/`. `custom_working_messages` defaults to `true`; setting it to `false` disables the rotating whimsical status header list and lets the TUI use a plain "Working" label while a task starts. This value is resolved onto `NoriConfig` in `loader.rs`, mirrored through `codex-core`'s config, and can be changed from the `/config` menu.
169+
166170

167171
**Hotkey Configuration** (`config/types/mod.rs`):
168172

nori-rs/acp/src/config/loader.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ impl NoriConfig {
168168
skillset_per_session,
169169
file_manager: toml.tui.file_manager,
170170
pinned_plan_drawer: toml.tui.pinned_plan_drawer.unwrap_or(false),
171+
custom_working_messages: toml.tui.custom_working_messages.unwrap_or(true),
171172
auto_worktree,
172173
footer_segment_config: super::types::FooterSegmentConfig::from_toml(
173174
&toml.tui.footer_segments,

nori-rs/acp/src/config/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ animations = false
125125
terminal_notifications = "disabled"
126126
os_notifications = "disabled"
127127
vertical_footer = true
128+
custom_working_messages = false
128129
"#;
129130
let config: NoriConfigToml = toml::from_str(toml_str).unwrap();
130131

@@ -141,6 +142,7 @@ vertical_footer = true
141142
);
142143
assert_eq!(config.tui.os_notifications, Some(OsNotifications::Disabled));
143144
assert_eq!(config.tui.vertical_footer, Some(true));
145+
assert_eq!(config.tui.custom_working_messages, Some(false));
144146
}
145147

146148
#[test]
@@ -156,6 +158,7 @@ model = "gemini"
156158
[tui]
157159
animations = false
158160
vertical_footer = true
161+
custom_working_messages = false
159162
"#,
160163
)
161164
.unwrap();
@@ -170,6 +173,7 @@ vertical_footer = true
170173
); // default
171174
assert_eq!(config.os_notifications, OsNotifications::Enabled); // default
172175
assert!(config.vertical_footer);
176+
assert!(!config.custom_working_messages);
173177
}
174178

175179
#[test]

nori-rs/acp/src/config/types/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,6 +1385,9 @@ pub struct TuiConfigToml {
13851385

13861386
/// Pin plan updates to a drawer in the viewport instead of history cells.
13871387
pub pinned_plan_drawer: Option<bool>,
1388+
1389+
/// Show rotating custom messages while the agent is working.
1390+
pub custom_working_messages: Option<bool>,
13881391
}
13891392

13901393
/// Resolved TUI configuration
@@ -1561,6 +1564,9 @@ pub struct NoriConfig {
15611564
/// Pin plan updates to a drawer in the viewport instead of history cells.
15621565
pub pinned_plan_drawer: bool,
15631566

1567+
/// Show rotating custom messages while the agent is working.
1568+
pub custom_working_messages: bool,
1569+
15641570
/// Footer segment visibility configuration.
15651571
pub footer_segment_config: FooterSegmentConfig,
15661572

@@ -1653,6 +1659,7 @@ impl Default for NoriConfig {
16531659
skillset_per_session: false,
16541660
file_manager: None,
16551661
pinned_plan_drawer: false,
1662+
custom_working_messages: true,
16561663
footer_segment_config: FooterSegmentConfig::default(),
16571664
nori_home: PathBuf::from(".nori/cli"),
16581665
cwd: std::env::current_dir().unwrap_or_default(),

nori-rs/core/docs.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ Notification modes:
115115
1. **Native notifications** (`use_native: true`): Uses `notify-rust` for desktop notifications. All calls to `send_native()` are non-blocking -- they spawn a background thread to call `notif.show()`, because some platforms (notably macOS) block synchronously on that call. On X11 Linux, the spawned thread also handles click-to-focus via `wmctrl` or `xdotool`. The `use_native` flag is controlled by `OsNotifications` in the ACP config layer (`@/nori-rs/acp/src/config/types.rs`).
116116
2. **External script** (`notify_command` configured): Invokes user-specified command with JSON payload.
117117

118-
Core's `Config::tui_notifications` is a simple `bool` that controls whether the TUI sends OSC 9 terminal escape sequence notifications. It derives its value from the ACP config's `TerminalNotifications` enum during config loading.
118+
Core's `Config::tui_notifications` is a simple `bool` that controls whether the TUI sends OSC 9 terminal escape sequence notifications. It derives its value from the ACP config's `TerminalNotifications` enum during config loading. Core also carries TUI display booleans such as `animations` and `custom_working_messages`; the latter mirrors `[tui].custom_working_messages` from Nori config so the TUI can choose between rotating custom working headers and the plain `Working` label without re-reading config.
119119

120120
### Things to Know
121121

nori-rs/core/src/config/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ pub struct Config {
154154
/// Enable ASCII animations and shimmer effects in the TUI.
155155
pub animations: bool,
156156

157+
/// Show rotating custom messages while the agent is working.
158+
pub custom_working_messages: bool,
159+
157160
/// The directory that should be treated as the current working directory
158161
/// for the session. All relative paths inside the business-logic layer are
159162
/// resolved against this path.
@@ -1261,6 +1264,11 @@ impl Config {
12611264
.map(|t| t.terminal_notifications)
12621265
.unwrap_or(true),
12631266
animations: cfg.tui.as_ref().map(|t| t.animations).unwrap_or(true),
1267+
custom_working_messages: cfg
1268+
.tui
1269+
.as_ref()
1270+
.map(|t| t.custom_working_messages)
1271+
.unwrap_or(true),
12641272
otel: {
12651273
let t: OtelConfigToml = cfg.otel.unwrap_or_default();
12661274
let log_user_prompt = t.log_user_prompt.unwrap_or(false);

nori-rs/core/src/config/tests/part1.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,33 @@ fn tui_config_missing_terminal_notifications_field_defaults_to_true() {
4646
assert!(tui.terminal_notifications);
4747
}
4848

49+
#[test]
50+
fn tui_config_custom_working_messages_defaults_to_true() {
51+
let cfg = r#"
52+
[tui]
53+
"#;
54+
55+
let parsed = toml::from_str::<ConfigToml>(cfg)
56+
.expect("TUI config without custom_working_messages should succeed");
57+
let tui = parsed.tui.expect("config should include tui section");
58+
59+
assert!(tui.custom_working_messages);
60+
}
61+
62+
#[test]
63+
fn tui_config_custom_working_messages_can_be_disabled() {
64+
let cfg = r#"
65+
[tui]
66+
custom_working_messages = false
67+
"#;
68+
69+
let parsed = toml::from_str::<ConfigToml>(cfg)
70+
.expect("TUI config with custom_working_messages disabled should succeed");
71+
let tui = parsed.tui.expect("config should include tui section");
72+
73+
assert!(!tui.custom_working_messages);
74+
}
75+
4976
#[test]
5077
fn test_sandbox_config_parsing() {
5178
let sandbox_full_access = r#"

nori-rs/core/src/config/tests/part3.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,7 @@ fn test_precedence_fixture_with_o3_profile() -> std::io::Result<()> {
547547
disable_paste_burst: false,
548548
tui_notifications: true,
549549
animations: true,
550+
custom_working_messages: true,
550551
otel: OtelConfig::default(),
551552
acp_allow_http_fallback: false,
552553
},
@@ -620,6 +621,7 @@ fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
620621
disable_paste_burst: false,
621622
tui_notifications: true,
622623
animations: true,
624+
custom_working_messages: true,
623625
otel: OtelConfig::default(),
624626
acp_allow_http_fallback: false,
625627
};

nori-rs/core/src/config/tests/part4.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ fn test_precedence_fixture_with_zdr_profile() -> std::io::Result<()> {
6666
disable_paste_burst: false,
6767
tui_notifications: true,
6868
animations: true,
69+
custom_working_messages: true,
6970
otel: OtelConfig::default(),
7071
acp_allow_http_fallback: false,
7172
};
@@ -140,6 +141,7 @@ fn test_precedence_fixture_with_gpt5_profile() -> std::io::Result<()> {
140141
disable_paste_burst: false,
141142
tui_notifications: true,
142143
animations: true,
144+
custom_working_messages: true,
143145
otel: OtelConfig::default(),
144146
acp_allow_http_fallback: false,
145147
};

nori-rs/core/src/config/types.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,11 @@ pub struct Tui {
370370
/// Defaults to `true`.
371371
#[serde(default = "default_true")]
372372
pub animations: bool,
373+
374+
/// Show rotating custom messages while the agent is working.
375+
/// Defaults to `true`.
376+
#[serde(default = "default_true")]
377+
pub custom_working_messages: bool,
373378
}
374379

375380
const fn default_true() -> bool {

0 commit comments

Comments
 (0)