Skip to content

Commit e4a3bd2

Browse files
committed
tui: saved-prompt UX — queue shows typed input; on execute: redaction ON shows “/name …”, redaction OFF shows “Custom instruction:” then “Saved prompt:”; agent still gets Directive (CustomInstruction > SavedPrompt)
Signed-off-by: Roman Aleynikov <[email protected]>
1 parent 22a2d4e commit e4a3bd2

File tree

2 files changed

+34
-17
lines changed

2 files changed

+34
-17
lines changed

codex-rs/tui/src/bottom_pane/chat_composer.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,13 @@ const LARGE_PASTE_CHAR_THRESHOLD: usize = 1000;
5656
pub enum InputResult {
5757
Submitted(String),
5858
// Submit actual text to the agent but override what is shown in the
59-
// transcript UI with `display`. Used for saved prompts so users see the
60-
// original command they typed (e.g., "/saved-prompt").
61-
SubmittedWithDisplay { text: String, display: String },
59+
// transcript UI with `display`. Optionally include `pretty_unredacted`
60+
// for an unredacted transcript (e.g., custom instruction first, then saved prompt).
61+
SubmittedWithDisplay {
62+
text: String,
63+
display: String,
64+
pretty_unredacted: Option<String>,
65+
},
6266
Command(SlashCommand),
6367
None,
6468
}
@@ -475,19 +479,26 @@ impl ChatComposer {
475479
<Rule>If information is missing, make reasonable assumptions and proceed.</Rule>\
476480
</Rules>\
477481
<CustomInstruction><![CDATA[\
478-
{}\
482+
{custom_instruction}\
479483
]]></CustomInstruction>\
480484
<SavedPrompt><![CDATA[\
481-
{}\
485+
{contents}\
482486
]]></SavedPrompt>\
483-
</Directive>",
484-
custom_instruction, contents
487+
</Directive>"
485488
)
486489
};
490+
let pretty = if custom_instruction.is_empty() {
491+
None
492+
} else {
493+
Some(format!(
494+
"Custom instruction:\n{custom_instruction}\n\nSaved prompt:\n{contents}"
495+
))
496+
};
487497
return (
488498
InputResult::SubmittedWithDisplay {
489499
text: agent_text,
490500
display,
501+
pretty_unredacted: pretty,
491502
},
492503
true,
493504
);
@@ -2452,7 +2463,7 @@ mod tests {
24522463
composer.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
24532464

24542465
match result {
2455-
InputResult::SubmittedWithDisplay { text, display } => {
2466+
InputResult::SubmittedWithDisplay { text, display, .. } => {
24562467
assert!(
24572468
display.starts_with("/my do this"),
24582469
"display should show typed command+instruction: {display}"

codex-rs/tui/src/chatwidget.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ struct UserMessage {
144144
// instead of `text`. This allows showing "/saved-prompt" while sending expanded
145145
// prompt contents to the agent.
146146
display_text: Option<String>,
147+
// Optional pretty version for unredacted transcript (e.g., custom
148+
// instruction followed by saved prompt) when redaction is disabled.
149+
pretty_unredacted: Option<String>,
147150
image_paths: Vec<PathBuf>,
148151
}
149152

@@ -152,6 +155,7 @@ impl From<String> for UserMessage {
152155
Self {
153156
text,
154157
display_text: None,
158+
pretty_unredacted: None,
155159
image_paths: Vec::new(),
156160
}
157161
}
@@ -164,6 +168,7 @@ fn create_initial_user_message(text: String, image_paths: Vec<PathBuf>) -> Optio
164168
Some(UserMessage {
165169
text,
166170
display_text: None,
171+
pretty_unredacted: None,
167172
image_paths,
168173
})
169174
}
@@ -810,6 +815,7 @@ impl ChatWidget {
810815
let user_message = UserMessage {
811816
text,
812817
display_text: None,
818+
pretty_unredacted: None,
813819
image_paths: self.bottom_pane.take_recent_submission_images(),
814820
};
815821
if self.bottom_pane.is_task_running() {
@@ -819,10 +825,15 @@ impl ChatWidget {
819825
self.submit_user_message(user_message);
820826
}
821827
}
822-
InputResult::SubmittedWithDisplay { text, display } => {
828+
InputResult::SubmittedWithDisplay {
829+
text,
830+
display,
831+
pretty_unredacted,
832+
} => {
823833
let user_message = UserMessage {
824834
text,
825835
display_text: Some(display),
836+
pretty_unredacted,
826837
image_paths: self.bottom_pane.take_recent_submission_images(),
827838
};
828839
if self.bottom_pane.is_task_running() {
@@ -1005,6 +1016,7 @@ impl ChatWidget {
10051016
let UserMessage {
10061017
text,
10071018
display_text,
1019+
pretty_unredacted,
10081020
image_paths,
10091021
} = user_message;
10101022
let mut items: Vec<InputItem> = Vec::new();
@@ -1041,7 +1053,7 @@ impl ChatWidget {
10411053
let shown = if self.config.redact_saved_prompt_body {
10421054
display_text.unwrap_or_else(|| text.clone())
10431055
} else {
1044-
text.clone()
1056+
pretty_unredacted.unwrap_or_else(|| text.clone())
10451057
};
10461058
self.add_to_history(history_cell::new_user_prompt(shown));
10471059
}
@@ -1195,13 +1207,7 @@ impl ChatWidget {
11951207
let messages: Vec<String> = self
11961208
.queued_user_messages
11971209
.iter()
1198-
.map(|m| {
1199-
if self.config.redact_saved_prompt_body {
1200-
m.display_text.clone().unwrap_or_else(|| m.text.clone())
1201-
} else {
1202-
m.text.clone()
1203-
}
1204-
})
1210+
.map(|m| m.display_text.clone().unwrap_or_else(|| m.text.clone()))
12051211
.collect();
12061212
self.bottom_pane.set_queued_user_messages(messages);
12071213
}

0 commit comments

Comments
 (0)