Skip to content

Commit 8a7ab0f

Browse files
committed
feat: add about_scroll state for improved navigation in About modal
1 parent 7ab11be commit 8a7ab0f

File tree

2 files changed

+62
-10
lines changed

2 files changed

+62
-10
lines changed

crates/agentic-tui/src/ui/app.rs

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ pub struct App {
161161
final_prompt: String,
162162
cloud_response: Option<AtomicNote>,
163163
synthesis_scroll: u16,
164+
about_scroll: u16,
164165
coaching_tip: (String, String),
165166
local_tokens_used: u32, // Token count for current local request
166167
cloud_tokens_used: u32, // Token count for current cloud request
@@ -196,6 +197,7 @@ impl App {
196197
final_prompt: String::new(),
197198
cloud_response: None,
198199
synthesis_scroll: 0,
200+
about_scroll: 0,
199201
coaching_tip: (String::new(), String::new()),
200202
local_tokens_used: 0,
201203
cloud_tokens_used: 0,
@@ -266,7 +268,7 @@ impl App {
266268
if body_length > 800 && has_insights && tag_count > 3 {
267269
RuixenState::Celebrating // 💎🚀🎯 - Excellent synthesis, celebration!
268270
} else if body_length > 400 && (has_insights || has_technical_terms) {
269-
RuixenState::Celebrating // 💎🚀🎯 - Good synthesis, satisfied
271+
RuixenState::Resting // 😴💤🌙 - Good synthesis, satisfied
270272
} else if body_length < 200 {
271273
RuixenState::Confused // 😅🤦‍♂️📝 - Short response, maybe didn't work well
272274
} else {
@@ -380,28 +382,36 @@ impl App {
380382
let inner_area = block.inner(area);
381383
frame.render_widget(block, area);
382384

383-
// Split area: message + tips
385+
// Split area: message + navigation footer
384386
let chunks = Layout::default()
385387
.direction(Direction::Vertical)
386388
.constraints([
387389
Constraint::Min(5), // Main message (flexible)
388-
Constraint::Length(3), // Tips footer
390+
Constraint::Length(1), // Navigation footer - single line like settings
389391
])
390392
.split(inner_area);
391393

392-
let message = Paragraph::new(message.as_str())
393-
.alignment(Alignment::Center)
394+
let mut message = Paragraph::new(message.as_str())
395+
.alignment(Alignment::Left) // Use Left alignment for better scrolling readability
394396
.style(self.theme.ratatui_style(Element::Text))
395397
.wrap(Wrap { trim: true });
396398

399+
// Apply scrolling only for About pages
400+
if title.contains("About RuixenOS") {
401+
message = message.scroll((self.about_scroll, 0));
402+
}
403+
397404
frame.render_widget(message, chunks[0]);
398405

399-
// Navigation footer
400-
let footer_text = "Press [ESC] to return.";
406+
// Navigation footer - show scroll controls for About page
407+
let footer_text = if title.contains("About RuixenOS") {
408+
"[←] [→] Scroll | [ESC] Return"
409+
} else {
410+
"Press [ESC] to return."
411+
};
401412
let footer = Paragraph::new(footer_text)
402413
.alignment(Alignment::Center)
403-
.style(self.theme.ratatui_style(Element::Inactive))
404-
.wrap(Wrap { trim: true });
414+
.style(self.theme.ratatui_style(Element::Inactive));
405415

406416
frame.render_widget(footer, chunks[1]);
407417
}
@@ -841,7 +851,7 @@ impl App {
841851
// Show About modal - same as /about command
842852
self.coaching_tip = (
843853
"About RuixenOS v0.1.0".to_string(),
844-
"🎯 The Curiosity Machine\nTransforming queries into thoughtful Ruixen inquiries since 2025.\nBuilt with Rust, ratatui, and endless wonder.".to_string(),
854+
"🎯 The Curiosity Machine\nTransforming queries into thoughtful Ruixen inquiries since 2025.\nBuilt with Rust, ratatui, and endless wonder.\n\n💝 Builder's Note:\nThis app was crafted with constitutional Rust patterns, following the RuixenOS workspace architecture. Every emoji expression, every token counted, every error handled gracefully. It's been an absolute joy building something that turns simple questions into profound explorations. The curiosity machine doesn't just process queries - it awakens wonder.\n\n🤝 Co-built with love by humans and AI agents working in harmony.".to_string(),
845855
);
846856
self.mode = AppMode::CoachingTip;
847857
}
@@ -1167,7 +1177,48 @@ impl App {
11671177
_ => {}
11681178
},
11691179
AppMode::CoachingTip => match key.code {
1180+
KeyCode::Left => {
1181+
// Scroll up through About content (only for About page)
1182+
if self.coaching_tip.0.contains("About RuixenOS")
1183+
&& self.about_scroll > 0
1184+
{
1185+
self.about_scroll -= 1;
1186+
}
1187+
}
1188+
KeyCode::Right => {
1189+
// Scroll down through About content (only for About page)
1190+
if self.coaching_tip.0.contains("About RuixenOS") {
1191+
// Calculate max scroll based on content length
1192+
let content = &self.coaching_tip.1;
1193+
let approx_usable_width = 50u16; // Conservative estimate for modal width
1194+
let approx_display_height = 8u16; // Conservative estimate (modal height - borders)
1195+
1196+
let lines: Vec<&str> = content.lines().collect();
1197+
let total_wrapped_lines: u16 = lines
1198+
.iter()
1199+
.map(|line| {
1200+
if line.is_empty() {
1201+
1 // Empty lines still take space
1202+
} else {
1203+
((line.len() as f32 / approx_usable_width as f32)
1204+
.ceil()
1205+
as u16)
1206+
.max(1)
1207+
}
1208+
})
1209+
.sum();
1210+
1211+
let max_scroll =
1212+
total_wrapped_lines.saturating_sub(approx_display_height);
1213+
1214+
if max_scroll > 0 && self.about_scroll < max_scroll {
1215+
self.about_scroll += 1;
1216+
}
1217+
}
1218+
}
11701219
KeyCode::Enter | KeyCode::Esc => {
1220+
// Reset scroll when closing and return to appropriate mode
1221+
self.about_scroll = 0;
11711222
// About modal should return to main menu, errors return to chat
11721223
if self.coaching_tip.0.contains("About RuixenOS") {
11731224
self.mode = AppMode::Normal;

crates/agentic-tui/src/ui/chat.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const GAP_HEIGHT: u16 = 1;
6363
const MAIN_TOTAL_HEIGHT: u16 = MAIN_LOGO_HEIGHT + GAP_HEIGHT + TEXT_HEIGHT;
6464
const SPIRAL_TOTAL_HEIGHT: u16 = SPIRAL_GALAXY_HEIGHT;
6565

66+
#[allow(clippy::too_many_arguments)]
6667
pub fn render_chat(
6768
frame: &mut Frame,
6869
area: Rect,

0 commit comments

Comments
 (0)