Add data-use disclosures to the user report dialog (#42798)

## What changed

- Show audience-specific data-use disclosure copy and links before report submission.
- Keep the note editor and submit/cancel controls visible in short panes while allowing the disclosure to scroll with `PageUp` and `PageDown`.
- Clarify the employee completion message with its report ID and link.

## Testing

- Add snapshot coverage for both audiences, narrow widths, short panes, and four-row panes.
- Cover submit and cancel behavior in the report note view.

GitOrigin-RevId: c025bcfbbd39e462685b211ef9392664e7de4f71
This commit is contained in:
Eric Traut
2026-09-04 16:39:46 +00:00
committed by copyberry
parent 47b0f7d540
commit 80ab0ffafe
16 changed files with 482 additions and 50 deletions
+1 -1
View File
@@ -1695,7 +1695,7 @@ impl App {
category,
include_logs,
} => {
self.chat_widget.open_feedback_note(category, include_logs);
self.chat_widget.open_feedback_note(category, include_logs, self.feedback_audience);
}
AppEvent::OpenFeedbackConsent { category } => {
self.chat_widget.open_feedback_consent(category);
@@ -1,4 +1,5 @@
//! Collects an optional feedback note and submits it with the selected log consent.
//! Collects a feedback note with audience-specific disclosure and independent log consent.
//! The editor and submission controls remain visible when the disclosure must scroll.
use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;
@@ -12,19 +13,33 @@ use ratatui::widgets::Clear;
use ratatui::widgets::Paragraph;
use ratatui::widgets::StatefulWidgetRef;
use ratatui::widgets::Widget;
use std::cell::Cell;
use std::cell::RefCell;
use crate::app_event::AppEvent;
use crate::app_event::FeedbackCategory;
use crate::app_event_sender::AppEventSender;
use crate::key_hint;
use crate::render::renderable::Renderable;
use crate::terminal_hyperlinks::mark_underlined_hyperlink;
use crate::wrapping::RtOptions;
use crate::wrapping::word_wrap_lines;
use super::CancellationEvent;
use super::bottom_pane_view::BottomPaneView;
use super::popup_consts::standard_popup_hint_line;
use super::feedback_view::FeedbackAudience;
use super::popup_consts::accept_cancel_hint_line;
use super::textarea::TextArea;
use super::textarea::TextAreaState;
const FEEDBACK_POLICY_URL: &str = "https://help.openai.com/en/articles/5722486-how-your-data-is-used-to-improve-model-performance";
const EMPLOYEE_FEEDBACK_FAQ_URL: &str = "https://go/codex-employee-feedback-faq";
enum IntroContent {
Full,
DisclosureOnly,
}
/// Minimal input overlay to collect an optional feedback note, then submit it
/// through the app-server-managed feedback flow.
pub(crate) struct FeedbackNoteView {
@@ -32,10 +47,13 @@ pub(crate) struct FeedbackNoteView {
turn_id: Option<String>,
app_event_tx: AppEventSender,
include_logs: bool,
feedback_audience: FeedbackAudience,
// UI state
textarea: TextArea,
textarea_state: RefCell<TextAreaState>,
intro_scroll: Cell<usize>,
intro_page_height: Cell<usize>,
complete: bool,
}
@@ -45,14 +63,18 @@ impl FeedbackNoteView {
turn_id: Option<String>,
app_event_tx: AppEventSender,
include_logs: bool,
feedback_audience: FeedbackAudience,
) -> Self {
Self {
category,
turn_id,
app_event_tx,
include_logs,
feedback_audience,
textarea: TextArea::new(),
textarea_state: RefCell::new(TextAreaState::default()),
intro_scroll: Cell::new(0),
intro_page_height: Cell::new(1),
complete: false,
}
}
@@ -73,6 +95,26 @@ impl FeedbackNoteView {
impl BottomPaneView for FeedbackNoteView {
fn handle_key_event(&mut self, key_event: KeyEvent) {
match key_event {
KeyEvent {
code: KeyCode::PageUp,
..
} => {
self.intro_scroll.set(
self.intro_scroll
.get()
.saturating_sub(self.intro_page_height.get()),
);
}
KeyEvent {
code: KeyCode::PageDown,
..
} => {
self.intro_scroll.set(
self.intro_scroll
.get()
.saturating_add(self.intro_page_height.get()),
);
}
KeyEvent {
code: KeyCode::Esc, ..
} => {
@@ -117,21 +159,21 @@ impl BottomPaneView for FeedbackNoteView {
impl Renderable for FeedbackNoteView {
fn desired_height(&self, width: u16) -> u16 {
self.intro_lines(width).len() as u16 + self.input_height(width) + 2u16
self.intro_lines(width, IntroContent::Full).len() as u16 + self.input_height(width) + 2u16
}
fn cursor_pos(&self, area: Rect) -> Option<(u16, u16)> {
if area.height < 2 || area.width <= 2 {
return None;
}
let intro_height = self.intro_lines(area.width).len() as u16;
let text_area_height = self.input_height(area.width).saturating_sub(1);
let input_area = self.input_area(area);
let text_area_height = input_area.height.saturating_sub(1);
if text_area_height == 0 {
return None;
}
let textarea_rect = Rect {
x: area.x.saturating_add(2),
y: area.y.saturating_add(intro_height).saturating_add(1),
y: input_area.y.saturating_add(1),
width: area.width.saturating_sub(2),
height: text_area_height,
};
@@ -144,11 +186,34 @@ impl Renderable for FeedbackNoteView {
return;
}
let intro_lines = self.intro_lines(area.width);
// Four rows leave room for one intro line; show the disclosure before submission.
let intro_content = if area.height == 4 {
IntroContent::DisclosureOnly
} else {
IntroContent::Full
};
let intro_lines = self.intro_lines(area.width, intro_content);
let input_area = self.input_area(area);
let intro_area = Rect {
height: input_area.y.saturating_sub(area.y),
..area
};
let (_, placeholder) = feedback_title_and_placeholder(self.category);
let input_height = self.input_height(area.width);
let input_height = input_area.height;
let max_scroll = intro_lines
.len()
.saturating_sub(usize::from(intro_area.height));
self.intro_scroll
.set(self.intro_scroll.get().min(max_scroll));
self.intro_page_height
.set(usize::from(intro_area.height.max(1)));
for (offset, line) in intro_lines.iter().enumerate() {
for (offset, line) in intro_lines
.iter()
.skip(self.intro_scroll.get())
.take(usize::from(intro_area.height))
.enumerate()
{
Paragraph::new(line.clone()).render(
Rect {
x: area.x,
@@ -159,14 +224,13 @@ impl Renderable for FeedbackNoteView {
buf,
);
}
let disclosure_url = match self.feedback_audience {
FeedbackAudience::OpenAiEmployee => EMPLOYEE_FEEDBACK_FAQ_URL,
FeedbackAudience::External => FEEDBACK_POLICY_URL,
};
mark_underlined_hyperlink(buf, intro_area, disclosure_url);
// Input line
let input_area = Rect {
x: area.x,
y: area.y.saturating_add(intro_lines.len() as u16),
width: area.width,
height: input_height,
};
if input_area.width >= 2 {
for row in 0..input_area.height {
Paragraph::new(Line::from(vec![gutter()])).render(
@@ -206,7 +270,8 @@ impl Renderable for FeedbackNoteView {
}
let hint_blank_y = input_area.y.saturating_add(input_height);
if hint_blank_y < area.y.saturating_add(area.height) {
let hint_spacing = u16::from(area.height > 4);
if hint_spacing > 0 && hint_blank_y < area.y.saturating_add(area.height) {
let blank_area = Rect {
x: area.x,
y: hint_blank_y,
@@ -214,11 +279,26 @@ impl Renderable for FeedbackNoteView {
height: 1,
};
Clear.render(blank_area, buf);
if max_scroll > 0 {
Paragraph::new(Line::from(vec![
key_hint::plain(KeyCode::PageUp).into(),
"/".into(),
key_hint::plain(KeyCode::PageDown).into(),
" scroll disclosure".into(),
]))
.render(blank_area, buf);
}
}
let hint_y = hint_blank_y.saturating_add(1);
let hint_y = hint_blank_y.saturating_add(hint_spacing);
if hint_y < area.y.saturating_add(area.height) {
Paragraph::new(standard_popup_hint_line()).render(
Paragraph::new(accept_cancel_hint_line(
Some(key_hint::plain(KeyCode::Enter).into()),
"to submit",
Some(key_hint::plain(KeyCode::Esc).into()),
"to cancel",
))
.render(
Rect {
x: area.x,
y: hint_y,
@@ -232,15 +312,59 @@ impl Renderable for FeedbackNoteView {
}
impl FeedbackNoteView {
fn input_area(&self, area: Rect) -> Rect {
// Reserve the editor and submit/cancel hints before allocating disclosure rows.
let footer_height = if area.height > 4 { 2 } else { 1 };
let input_height = self
.input_height(area.width)
.min(area.height.saturating_sub(footer_height + 1).max(2))
.min(area.height);
let intro_height = (self.intro_lines(area.width, IntroContent::Full).len() as u16)
.min(area.height.saturating_sub(input_height + footer_height));
Rect {
y: area.y.saturating_add(intro_height),
height: input_height,
..area
}
}
fn input_height(&self, width: u16) -> u16 {
let usable_width = width.saturating_sub(2);
let text_height = self.textarea.desired_height(usable_width).clamp(1, 8);
text_height.saturating_add(1).min(9)
}
fn intro_lines(&self, _width: u16) -> Vec<Line<'static>> {
fn intro_lines(&self, width: u16, content: IntroContent) -> Vec<Line<'static>> {
let (title, _) = feedback_title_and_placeholder(self.category);
vec![Line::from(vec![gutter(), title.bold()])]
let mut lines = match content {
IntroContent::Full => vec![title.bold().into(), "".into()],
IntroContent::DisclosureOnly => Vec::new(),
};
match self.feedback_audience {
FeedbackAudience::OpenAiEmployee => {
lines.push(
"Your data may be used to improve our models and products"
.bold()
.into(),
);
lines.push(Line::from(vec![
"By submitting feedback, you agree that OpenAI can use your feedback for safety purposes and internal model training, as explained in more detail ".dim(),
"here".cyan().underlined(),
". Please avoid sharing tented information under NDA, or privileged, HR-related, or sensitive personal information.".dim(),
]));
}
FeedbackAudience::External => lines.push(Line::from(vec![
"Your feedback can be used to improve ChatGPT. ".dim(),
"Learn more".cyan().underlined(),
".".dim(),
])),
}
word_wrap_lines(
lines,
RtOptions::new(usize::from(width.max(1)))
.initial_indent(Line::from(gutter()))
.subsequent_indent(Line::from(gutter())),
)
}
}
@@ -18,7 +18,7 @@ fn render_buffer(area: Rect, buf: &Buffer) -> String {
if symbol.is_empty() {
line.push(' ');
} else {
line.push_str(symbol);
line.push_str(&crate::terminal_hyperlinks::strip_osc8(symbol));
}
}
line.trim_end().to_string()
@@ -38,7 +38,11 @@ fn make_view(category: FeedbackCategory) -> FeedbackNoteView {
let (tx_raw, _rx) = tokio::sync::mpsc::unbounded_channel::<AppEvent>();
let tx = AppEventSender::new(tx_raw);
FeedbackNoteView::new(
category, /*turn_id*/ None, tx, /*include_logs*/ true,
category,
/*turn_id*/ None,
tx,
/*include_logs*/ true,
FeedbackAudience::External,
)
}
@@ -86,12 +90,137 @@ fn feedback_view_with_connectivity_diagnostics() {
/*turn_id*/ None,
tx,
/*include_logs*/ false,
FeedbackAudience::External,
);
let rendered = render(&view, /*width*/ 60);
insta::assert_snapshot!("feedback_view_with_connectivity_diagnostics", rendered);
}
#[test]
fn feedback_disclosure_matches_audience_at_normal_and_narrow_widths() {
let mut rendered = Vec::new();
for (audience, name, url, link_label) in [
(
FeedbackAudience::External,
"external",
FEEDBACK_POLICY_URL,
"Learnmore",
),
(
FeedbackAudience::OpenAiEmployee,
"employee",
EMPLOYEE_FEEDBACK_FAQ_URL,
"here",
),
] {
let mut view = make_view(FeedbackCategory::Bug);
view.feedback_audience = audience;
for width in [80, 40] {
let area = Rect::new(
/*x*/ 0,
/*y*/ 0,
width,
view.desired_height(width),
);
let mut buf = Buffer::empty(area);
view.render(area, &mut buf);
let linked_text = buf
.content
.iter()
.filter(|cell| cell.symbol().contains(&format!("\x1b]8;;{url}\x07")))
.map(|cell| crate::terminal_hyperlinks::strip_osc8(cell.symbol()))
.collect::<String>();
assert_eq!(linked_text, link_label);
rendered.push(format!(
"{name} ({width} columns)\n{}",
render_buffer(area, &buf)
));
}
}
insta::assert_snapshot!("feedback_disclosures", rendered.join("\n\n"));
}
#[test]
fn feedback_disclosure_scrolls_without_hiding_input_in_short_panes() {
let mut rendered = Vec::new();
for (width, height) in [(40, 12), (80, 8)] {
let mut view = make_view(FeedbackCategory::Bug);
view.feedback_audience = FeedbackAudience::OpenAiEmployee;
view.handle_paste("A visible note.".to_string());
let area = Rect::new(/*x*/ 0, /*y*/ 0, width, height);
for (stage, key) in [
("initial", None),
("scrolled", Some(KeyCode::PageDown)),
("restored", Some(KeyCode::PageUp)),
] {
if let Some(key) = key {
view.handle_key_event(KeyEvent::from(key));
}
let mut buf = Buffer::empty(area);
view.render(area, &mut buf);
let text = render_buffer(area, &buf);
assert!(text.contains("A visible note."));
assert!(text.contains("enter to submit"));
assert!(
view.cursor_pos(area)
.is_some_and(|(x, y)| x < width && y < height)
);
assert_eq!(view.is_complete(), false);
if stage == "scrolled" {
assert!(text.contains("sensitive personal"));
}
rendered.push(format!("{width}x{height} {stage}\n{text}"));
}
}
insta::assert_snapshot!("feedback_disclosure_short_panes", rendered.join("\n\n"));
}
#[test]
fn feedback_disclosure_keeps_editor_visible_in_four_rows() {
let mut view = make_view(FeedbackCategory::Bug);
view.feedback_audience = FeedbackAudience::OpenAiEmployee;
view.handle_paste("A visible note.".to_string());
let area = Rect::new(
/*x*/ 0, /*y*/ 0, /*width*/ 80, /*height*/ 4,
);
let mut buf = Buffer::empty(area);
view.render(area, &mut buf);
assert!(
view.cursor_pos(area)
.is_some_and(|(x, y)| area.contains((x, y).into()))
);
let initial = render_buffer(area, &buf);
assert!(initial.contains("Your data may be used to improve our models and products"));
for _ in view.intro_lines(area.width, IntroContent::Full) {
view.handle_key_event(KeyEvent::from(KeyCode::PageDown));
buf = Buffer::empty(area);
view.render(area, &mut buf);
}
let scrolled = render_buffer(area, &buf);
assert!(scrolled.contains("personal information."));
insta::assert_snapshot!(
"feedback_disclosure_four_rows",
format!("{initial}\n\n{scrolled}")
);
let narrow_area = Rect::new(
/*x*/ 0, /*y*/ 0, /*width*/ 24, /*height*/ 4,
);
for (audience, disclosure) in [
(FeedbackAudience::OpenAiEmployee, "Your data"),
(FeedbackAudience::External, "Your feedback"),
] {
let mut view = make_view(FeedbackCategory::SafetyCheck);
view.feedback_audience = audience;
let mut buf = Buffer::empty(narrow_area);
view.render(narrow_area, &mut buf);
assert!(render_buffer(narrow_area, &buf).contains(disclosure));
}
}
#[test]
fn submit_feedback_emits_submit_event_with_trimmed_note() {
let (tx_raw, mut rx) = tokio::sync::mpsc::unbounded_channel::<AppEvent>();
@@ -101,10 +230,11 @@ fn submit_feedback_emits_submit_event_with_trimmed_note() {
Some("turn-123".to_string()),
tx,
/*include_logs*/ true,
FeedbackAudience::OpenAiEmployee,
);
view.textarea.insert_str(" something broke ");
view.submit();
view.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
let event = rx.try_recv().expect("submit feedback event");
assert!(matches!(
@@ -128,6 +258,7 @@ fn submit_feedback_omits_empty_note() {
/*turn_id*/ None,
tx,
/*include_logs*/ false,
FeedbackAudience::External,
);
view.submit();
@@ -143,3 +274,21 @@ fn submit_feedback_omits_empty_note() {
}
));
}
#[test]
fn feedback_note_cancel_does_not_submit() {
let (tx_raw, mut rx) = tokio::sync::mpsc::unbounded_channel::<AppEvent>();
let mut view = FeedbackNoteView::new(
FeedbackCategory::Bug,
/*turn_id*/ None,
AppEventSender::new(tx_raw),
/*include_logs*/ false,
FeedbackAudience::OpenAiEmployee,
);
view.textarea.insert_str("unfinished feedback");
view.handle_key_event(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE));
assert_eq!(view.is_complete(), true);
assert!(rx.try_recv().is_err());
}
+13 -12
View File
@@ -19,10 +19,9 @@ const BASE_CLI_BUG_ISSUE_URL: &str =
/// Internal routing link for employee feedback follow-ups. This must not be shown to external users.
const CODEX_FEEDBACK_INTERNAL_URL: &str = "http://go/codex-feedback-internal";
/// The target audience for feedback follow-up instructions.
/// The target audience for feedback disclosure and follow-up instructions.
///
/// This is used strictly for messaging/links after feedback upload completes. It
/// must not change feedback upload behavior itself.
/// This controls displayed copy and links, not feedback upload behavior.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum FeedbackAudience {
OpenAiEmployee,
@@ -60,7 +59,7 @@ pub(crate) fn feedback_success_cell(
let issue_url = issue_url_for_category(category, thread_id, feedback_audience);
let mut lines = vec![Line::from(match issue_url.as_ref() {
Some(_) if feedback_audience == FeedbackAudience::OpenAiEmployee => {
format!("{prefix} Please report this in #codex-feedback:")
format!("{prefix} You can share this in #codex-feedback:")
}
Some(_) => format!("{prefix} Please open an issue using the following URL:"),
None => format!("{prefix} Thanks for the feedback!"),
@@ -71,10 +70,15 @@ pub(crate) fn feedback_success_cell(
"".into(),
Line::from(vec![" ".into(), url.cyan().underlined()]),
"".into(),
Line::from(" Share this and add some info about your problem:"),
Line::from(vec![
" ".into(),
format!("https://go/codex-feedback/{thread_id}").bold(),
" Sentry Feedback ID: ".into(),
thread_id.to_string().bold(),
]),
Line::from(vec![
" Sentry URL: ".into(),
format!("https://go/codex-feedback/{thread_id}")
.cyan()
.underlined(),
]),
]);
}
@@ -352,7 +356,7 @@ mod tests {
if symbol.is_empty() {
line.push(' ');
} else {
line.push_str(symbol);
line.push_str(&crate::terminal_hyperlinks::strip_osc8(symbol));
}
}
line.trim_end().to_string()
@@ -520,10 +524,7 @@ mod tests {
),
/*width*/ 120,
);
assert_eq!(
rendered,
"• Feedback uploaded. Please report this in #codex-feedback:\n\n http://go/codex-feedback-internal\n\n Share this and add some info about your problem:\n https://go/codex-feedback/thread-2"
);
insta::assert_snapshot!("feedback_success_employee", rendered);
}
#[test]
@@ -0,0 +1,13 @@
---
source: tui/src/bottom_pane/feedback_note_view_tests.rs
expression: "format!(\"{initial}\\n\\n{scrolled}\")"
---
▌ Your data may be used to improve our models and products
▌
▌ A visible note.
Press enter to submit or esc to cancel
▌ sensitive personal information.
▌
▌ A visible note.
Press enter to submit or esc to cancel
@@ -0,0 +1,75 @@
---
source: tui/src/bottom_pane/feedback_note_view_tests.rs
expression: "rendered.join(\"\\n\\n\")"
---
40x12 initial
▌ Tell us more (bug)
▌
▌ Your data may be used to improve our
▌ models and products
▌ By submitting feedback, you agree that
▌ OpenAI can use your feedback for
▌ safety purposes and internal model
▌ training, as explained in more detail
▌
▌ A visible note.
pgup/pgdn scroll disclosure
Press enter to submit or esc to cancel
40x12 scrolled
▌ By submitting feedback, you agree that
▌ OpenAI can use your feedback for
▌ safety purposes and internal model
▌ training, as explained in more detail
▌ here. Please avoid sharing tented
▌ information under NDA, or privileged,
▌ HR-related, or sensitive personal
▌ information.
▌
▌ A visible note.
pgup/pgdn scroll disclosure
Press enter to submit or esc to cancel
40x12 restored
▌ Tell us more (bug)
▌
▌ Your data may be used to improve our
▌ models and products
▌ By submitting feedback, you agree that
▌ OpenAI can use your feedback for
▌ safety purposes and internal model
▌ training, as explained in more detail
▌
▌ A visible note.
pgup/pgdn scroll disclosure
Press enter to submit or esc to cancel
80x8 initial
▌ Tell us more (bug)
▌
▌ Your data may be used to improve our models and products
▌ By submitting feedback, you agree that OpenAI can use your feedback for safety
▌
▌ A visible note.
pgup/pgdn scroll disclosure
Press enter to submit or esc to cancel
80x8 scrolled
▌ By submitting feedback, you agree that OpenAI can use your feedback for safety
▌ purposes and internal model training, as explained in more detail here. Please
▌ avoid sharing tented information under NDA, or privileged, HR-related, or
▌ sensitive personal information.
▌
▌ A visible note.
pgup/pgdn scroll disclosure
Press enter to submit or esc to cancel
80x8 restored
▌ Tell us more (bug)
▌
▌ Your data may be used to improve our models and products
▌ By submitting feedback, you agree that OpenAI can use your feedback for safety
▌
▌ A visible note.
pgup/pgdn scroll disclosure
Press enter to submit or esc to cancel
@@ -0,0 +1,53 @@
---
source: tui/src/bottom_pane/feedback_note_view_tests.rs
expression: "rendered.join(\"\\n\\n\")"
---
external (80 columns)
▌ Tell us more (bug)
▌
▌ Your feedback can be used to improve ChatGPT. Learn more.
▌
▌ (optional) Write a short description to help us further
Press enter to submit or esc to cancel
external (40 columns)
▌ Tell us more (bug)
▌
▌ Your feedback can be used to improve
▌ ChatGPT. Learn more.
▌
▌ (optional) Write a short description t
Press enter to submit or esc to cancel
employee (80 columns)
▌ Tell us more (bug)
▌
▌ Your data may be used to improve our models and products
▌ By submitting feedback, you agree that OpenAI can use your feedback for safety
▌ purposes and internal model training, as explained in more detail here. Please
▌ avoid sharing tented information under NDA, or privileged, HR-related, or
▌ sensitive personal information.
▌
▌ (optional) Write a short description to help us further
Press enter to submit or esc to cancel
employee (40 columns)
▌ Tell us more (bug)
▌
▌ Your data may be used to improve our
▌ models and products
▌ By submitting feedback, you agree that
▌ OpenAI can use your feedback for
▌ safety purposes and internal model
▌ training, as explained in more detail
▌ here. Please avoid sharing tented
▌ information under NDA, or privileged,
▌ HR-related, or sensitive personal
▌ information.
▌
▌ (optional) Write a short description t
Press enter to submit or esc to cancel
@@ -4,6 +4,8 @@ expression: rendered
---
▌ Tell us more (bad result)
▌
▌ Your feedback can be used to improve ChatGPT. Learn more.
▌
▌ (optional) Write a short description to help us further
Press enter to confirm or esc to go back
Press enter to submit or esc to cancel
@@ -4,6 +4,8 @@ expression: rendered
---
▌ Tell us more (bug)
▌
▌ Your feedback can be used to improve ChatGPT. Learn more.
▌
▌ (optional) Write a short description to help us further
Press enter to confirm or esc to go back
Press enter to submit or esc to cancel
@@ -4,6 +4,8 @@ expression: rendered
---
▌ Tell us more (good result)
▌
▌ Your feedback can be used to improve ChatGPT. Learn more.
▌
▌ (optional) Write a short description to help us further
Press enter to confirm or esc to go back
Press enter to submit or esc to cancel
@@ -4,6 +4,8 @@ expression: rendered
---
▌ Tell us more (other)
▌
▌ Your feedback can be used to improve ChatGPT. Learn more.
▌
▌ (optional) Write a short description to help us further
Press enter to confirm or esc to go back
Press enter to submit or esc to cancel
@@ -4,6 +4,8 @@ expression: rendered
---
▌ Tell us more (safety check)
▌
▌ Your feedback can be used to improve ChatGPT. Learn more.
▌
▌ (optional) Share what was refused and why it should have b
Press enter to confirm or esc to go back
Press enter to submit or esc to cancel
@@ -4,6 +4,8 @@ expression: rendered
---
▌ Tell us more (bug)
▌
▌ Your feedback can be used to improve ChatGPT. Learn more.
▌
▌ (optional) Write a short description to help us further
Press enter to confirm or esc to go back
Press enter to submit or esc to cancel
@@ -0,0 +1,10 @@
---
source: tui/src/bottom_pane/feedback_view.rs
expression: rendered
---
• Feedback uploaded. You can share this in #codex-feedback:
http://go/codex-feedback-internal
Sentry Feedback ID: thread-2
Sentry URL: https://go/codex-feedback/thread-2
+2 -8
View File
@@ -997,20 +997,14 @@ impl ChatWidget {
&mut self,
category: crate::app_event::FeedbackCategory,
include_logs: bool,
) {
self.show_feedback_note(category, include_logs);
}
fn show_feedback_note(
&mut self,
category: crate::app_event::FeedbackCategory,
include_logs: bool,
feedback_audience: crate::bottom_pane::FeedbackAudience,
) {
let view = crate::bottom_pane::FeedbackNoteView::new(
category,
self.turn_lifecycle.last_turn_id.clone(),
self.app_event_tx.clone(),
include_logs,
feedback_audience,
);
self.bottom_pane.show_view(Box::new(view));
self.request_redraw();
@@ -813,6 +813,7 @@ async fn live_app_server_turn_started_sets_feedback_turn_id() {
chat.open_feedback_note(
crate::app_event::FeedbackCategory::Bug,
/*include_logs*/ false,
crate::bottom_pane::FeedbackAudience::External,
);
chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));