Skip to content

Commit 35afcf8

Browse files
authored
refactor(tui): extract shared setting edit overlay (#1776)
The draw_edit_overlay function in sandbox_settings and global_settings was nearly identical — the only difference was which settings slice the entry was resolved from (app.sandbox_settings vs app.global_settings). Extract the common rendering logic into draw_setting_edit_overlay in ui/mod.rs, taking key/kind/edit/area/theme as parameters. Each pane now resolves its own entry and delegates to the single implementation. Net: -75 lines, +61 lines across the three files.
1 parent 97986d9 commit 35afcf8

3 files changed

Lines changed: 61 additions & 75 deletions

File tree

crates/openshell-tui/src/ui/global_settings.rs

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -120,46 +120,10 @@ fn draw_edit_overlay(
120120
edit: &crate::app::SettingEditState,
121121
area: Rect,
122122
) {
123-
let t = &app.theme;
124123
let Some(entry) = app.global_settings.get(edit.index) else {
125124
return;
126125
};
127-
128-
let title = format!(" Edit: {} ({}) ", entry.key, entry.kind.as_str());
129-
let mut lines = vec![
130-
Line::from(Span::styled(&title, t.heading)),
131-
Line::from(""),
132-
Line::from(vec![
133-
Span::styled("Value: ", t.muted),
134-
Span::styled(&edit.input, t.text),
135-
Span::styled("_", t.accent),
136-
]),
137-
];
138-
139-
if let Some(ref err) = edit.error {
140-
lines.push(Line::from(""));
141-
lines.push(Line::from(Span::styled(err, t.status_err)));
142-
}
143-
144-
lines.push(Line::from(""));
145-
lines.push(Line::from(vec![
146-
Span::styled("[Enter]", t.key_hint),
147-
Span::styled(" Confirm ", t.muted),
148-
Span::styled("[Esc]", t.key_hint),
149-
Span::styled(" Cancel", t.muted),
150-
]));
151-
152-
// content lines + 2 for border
153-
let popup_height = u16::try_from(lines.len() + 2).unwrap_or(u16::MAX);
154-
let popup = centered_rect(50, popup_height, area);
155-
frame.render_widget(Clear, popup);
156-
157-
let block = Block::default()
158-
.borders(Borders::ALL)
159-
.border_style(t.border_focused)
160-
.padding(Padding::horizontal(1));
161-
162-
frame.render_widget(Paragraph::new(lines).block(block), popup);
126+
super::draw_setting_edit_overlay(frame, &entry.key, entry.kind, edit, area, &app.theme);
163127
}
164128

165129
fn draw_confirm_set(frame: &mut Frame<'_>, app: &App, idx: usize, area: Rect) {

crates/openshell-tui/src/ui/mod.rs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ use ratatui::Frame;
1818
use ratatui::layout::{Constraint, Direction, Layout, Rect};
1919
use ratatui::style::Style;
2020
use ratatui::text::{Line, Span};
21-
use ratatui::widgets::{Block, Borders, Paragraph};
21+
use ratatui::widgets::{Block, Borders, Clear, Padding, Paragraph};
2222

23-
use crate::app::{self, App, Focus, InputMode, Screen};
23+
use crate::app::{self, App, Focus, InputMode, Screen, SettingEditState};
24+
use crate::theme::Theme;
2425

2526
pub fn draw(frame: &mut Frame<'_>, app: &mut App) {
2627
// Splash screen is a full-screen takeover — no chrome.
@@ -512,6 +513,62 @@ pub fn centered_rect(width: u16, height: u16, area: Rect) -> Rect {
512513
horiz[1]
513514
}
514515

516+
// ---------------------------------------------------------------------------
517+
// Shared setting-edit overlay (used by both global and sandbox settings panes)
518+
// ---------------------------------------------------------------------------
519+
520+
/// Render the generic setting-edit text-input overlay.
521+
///
522+
/// Both the global-settings and sandbox-settings panes show the same editor
523+
/// overlay — an input box with the setting key/type in the title, an error
524+
/// line when validation fails, and `[Enter]`/`[Esc]` hints. This function
525+
/// is the single implementation; each pane resolves its own entry and then
526+
/// delegates here.
527+
fn draw_setting_edit_overlay(
528+
frame: &mut Frame<'_>,
529+
key: &str,
530+
kind: openshell_core::settings::SettingValueKind,
531+
edit: &SettingEditState,
532+
area: Rect,
533+
theme: &Theme,
534+
) {
535+
let t = theme;
536+
let title = format!(" Edit: {} ({}) ", key, kind.as_str());
537+
let mut lines = vec![
538+
Line::from(Span::styled(&title, t.heading)),
539+
Line::from(""),
540+
Line::from(vec![
541+
Span::styled("Value: ", t.muted),
542+
Span::styled(&edit.input, t.text),
543+
Span::styled("_", t.accent),
544+
]),
545+
];
546+
547+
if let Some(ref err) = edit.error {
548+
lines.push(Line::from(""));
549+
lines.push(Line::from(Span::styled(err.as_str(), t.status_err)));
550+
}
551+
552+
lines.push(Line::from(""));
553+
lines.push(Line::from(vec![
554+
Span::styled("[Enter]", t.key_hint),
555+
Span::styled(" Confirm ", t.muted),
556+
Span::styled("[Esc]", t.key_hint),
557+
Span::styled(" Cancel", t.muted),
558+
]));
559+
560+
let popup_height = u16::try_from(lines.len() + 2).unwrap_or(u16::MAX);
561+
let popup = centered_popup(50, popup_height, area);
562+
frame.render_widget(Clear, popup);
563+
564+
let block = Block::default()
565+
.borders(Borders::ALL)
566+
.border_style(t.border_focused)
567+
.padding(Padding::horizontal(1));
568+
569+
frame.render_widget(Paragraph::new(lines).block(block), popup);
570+
}
571+
515572
/// Center a popup rectangle within `area` using percentage-based width and
516573
/// an absolute height (in rows).
517574
pub fn centered_popup(percent_x: u16, height: u16, area: Rect) -> Rect {

crates/openshell-tui/src/ui/sandbox_settings.rs

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -128,45 +128,10 @@ fn draw_edit_overlay(
128128
edit: &crate::app::SettingEditState,
129129
area: Rect,
130130
) {
131-
let t = &app.theme;
132131
let Some(entry) = app.sandbox_settings.get(edit.index) else {
133132
return;
134133
};
135-
136-
let title = format!(" Edit: {} ({}) ", entry.key, entry.kind.as_str());
137-
let mut lines = vec![
138-
Line::from(Span::styled(&title, t.heading)),
139-
Line::from(""),
140-
Line::from(vec![
141-
Span::styled("Value: ", t.muted),
142-
Span::styled(&edit.input, t.text),
143-
Span::styled("_", t.accent),
144-
]),
145-
];
146-
147-
if let Some(ref err) = edit.error {
148-
lines.push(Line::from(""));
149-
lines.push(Line::from(Span::styled(err, t.status_err)));
150-
}
151-
152-
lines.push(Line::from(""));
153-
lines.push(Line::from(vec![
154-
Span::styled("[Enter]", t.key_hint),
155-
Span::styled(" Confirm ", t.muted),
156-
Span::styled("[Esc]", t.key_hint),
157-
Span::styled(" Cancel", t.muted),
158-
]));
159-
160-
let popup_height = u16::try_from(lines.len() + 2).unwrap_or(u16::MAX);
161-
let popup = centered_rect(50, popup_height, area);
162-
frame.render_widget(Clear, popup);
163-
164-
let block = Block::default()
165-
.borders(Borders::ALL)
166-
.border_style(t.border_focused)
167-
.padding(Padding::horizontal(1));
168-
169-
frame.render_widget(Paragraph::new(lines).block(block), popup);
134+
super::draw_setting_edit_overlay(frame, &entry.key, entry.kind, edit, area, &app.theme);
170135
}
171136

172137
fn draw_confirm_set(frame: &mut Frame<'_>, app: &App, idx: usize, area: Rect) {

0 commit comments

Comments
 (0)