diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/mod.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/mod.rs index 75136c6..0ea1590 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/mod.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/mod.rs @@ -17,13 +17,14 @@ use lumni::api::error::ApplicationError; pub use modals::{ModalAction, ModalWindowTrait, ModalWindowType}; pub use ui::AppUi; pub use window::{ - CommandLine, ResponseWindow, SimpleString, TextArea, TextLine, TextSegment, + CommandLine, ReadDocument, ReadWriteDocument, ResponseWindow, SimpleString, + TextArea, TextBuffer, TextDocumentTrait, TextLine, TextSegment, TextWindowTrait, WindowKind, }; use super::chat::db::{ Conversation, ConversationDatabase, ConversationDbHandler, ConversationId, - ConversationStatus, MaskMode, ModelIdentifier, ModelSpec, ProviderConfig, + ConversationStatus, MaskMode, ModelSpec, ProviderConfig, ProviderConfigOptions, UserProfile, UserProfileDbHandler, }; use super::chat::{ diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/modals/filebrowser/mod.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/modals/filebrowser/mod.rs index 23e5176..22994d1 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/modals/filebrowser/mod.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/modals/filebrowser/mod.rs @@ -169,7 +169,6 @@ impl ModalWindowTrait for FileBrowserModal { ]) .split(chunks[0]); - //self.file_browser.render(frame, main_chunks[0]); frame.render_stateful_widget( &self.file_browser, main_chunks[0], diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/modals/mod.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/modals/mod.rs index b2f0959..21809ca 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/modals/mod.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/modals/mod.rs @@ -14,10 +14,9 @@ pub use super::widgets; use super::{ ApplicationError, Conversation, ConversationDbHandler, ConversationStatus, KeyTrack, MaskMode, ModelServer, ModelSpec, PromptInstruction, - ProviderConfig, ProviderConfigOptions, ResponseWindow, ServerTrait, - SimpleString, TextArea, TextLine, TextWindowTrait, ThreadedChatSession, - UserEvent, UserProfile, UserProfileDbHandler, WindowEvent, - SUPPORTED_MODEL_ENDPOINTS, + ProviderConfig, ProviderConfigOptions, ServerTrait, SimpleString, TextArea, + TextLine, TextWindowTrait, ThreadedChatSession, UserEvent, UserProfile, + UserProfileDbHandler, WindowEvent, SUPPORTED_MODEL_ENDPOINTS, }; #[derive(Debug, Clone, PartialEq)] diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/modals/settings/mod.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/modals/settings/mod.rs index 3889e3d..241b79c 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/modals/settings/mod.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/modals/settings/mod.rs @@ -15,12 +15,13 @@ use ratatui::Frame; use renderer::SettingsRenderer; use settings_editor::{SettingsAction, SettingsEditor}; +use super::widgets::{TextAreaState, TextAreaWidget}; use super::{ ApplicationError, ConversationDbHandler, KeyTrack, MaskMode, ModalAction, ModalWindowTrait, ModalWindowType, ModelServer, ModelSpec, ProviderConfig, - ProviderConfigOptions, ResponseWindow, ServerTrait, SimpleString, TextLine, - TextWindowTrait, ThreadedChatSession, UserProfile, UserProfileDbHandler, - WindowEvent, SUPPORTED_MODEL_ENDPOINTS, + ProviderConfigOptions, ServerTrait, SimpleString, TextLine, + ThreadedChatSession, UserProfile, UserProfileDbHandler, WindowEvent, + SUPPORTED_MODEL_ENDPOINTS, }; #[derive(Debug)] diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/modals/settings/profile/creator.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/modals/settings/profile/creator.rs index cb46520..9a5dc11 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/modals/settings/profile/creator.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/modals/settings/profile/creator.rs @@ -1,6 +1,6 @@ use std::time::Instant; -use ratatui::layout::{Alignment, Constraint, Direction, Layout}; +use ratatui::layout::{Alignment, Constraint, Direction, Layout, Margin}; use ratatui::style::{Color, Modifier, Style}; use ratatui::widgets::{Block, Borders, List, ListItem, ListState, Paragraph}; use serde_json::json; @@ -338,19 +338,26 @@ impl ProfileCreator { let content_area = Layout::default() .direction(Direction::Horizontal) .constraints( - [Constraint::Min(1), Constraint::Length(1)], // reserve space for scrollbar ( to be implemented ) + [Constraint::Min(1), Constraint::Length(1)], // reserve space for scrollbar (to be implemented) ) .split(chunks[0]); let text_lines = self.create_confirm_details(); - let mut text_area = ResponseWindow::new(Some(text_lines)); + let text_area_widget = TextAreaWidget::new(); + let mut text_area_state = + TextAreaState::with_read_document(Some(text_lines)); let text_area_block = Block::default() .borders(Borders::ALL) .title("Profile Details"); - let text_area_widget = - text_area.widget(&content_area[0]).block(text_area_block); - f.render_widget(text_area_widget, content_area[0]); + + f.render_stateful_widget( + &text_area_widget, + content_area[0].inner(Margin::new(1, 1)), + &mut text_area_state, + ); + + f.render_widget(text_area_block, content_area[0]); // Render buttons let button_constraints = diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/modals/settings/provider/creator.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/modals/settings/provider/creator.rs index b7a0c05..417bfee 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/modals/settings/provider/creator.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/modals/settings/provider/creator.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use ratatui::layout::{Alignment, Constraint, Direction, Layout}; +use ratatui::layout::{Alignment, Constraint, Direction, Layout, Margin}; use ratatui::style::{Color, Modifier, Style}; use ratatui::text::{Line, Span, Text}; use ratatui::widgets::{Block, Borders, List, ListItem, ListState, Paragraph}; @@ -87,14 +87,21 @@ impl ProviderCreator { .split(chunks[0]); let text_lines = self.create_confirm_details(); - let mut text_area = ResponseWindow::new(Some(text_lines)); + let text_area_widget = TextAreaWidget::new(); + let mut text_area_state = + TextAreaState::with_read_document(Some(text_lines)); let text_area_block = Block::default() .borders(Borders::ALL) .title("Provider Details"); - let text_area_widget = - text_area.widget(&content_area[0]).block(text_area_block); - f.render_widget(text_area_widget, content_area[0]); + + f.render_stateful_widget( + &text_area_widget, + content_area[0].inner(Margin::new(1, 1)), + &mut text_area_state, + ); + + f.render_widget(text_area_block, content_area[0]); // Render buttons let button_constraints = @@ -418,29 +425,6 @@ impl ProviderCreator { f.render_stateful_widget(list, area, &mut state); } - pub fn render_confirm(&self, f: &mut Frame, area: Rect) { - let mut items = vec![ - ListItem::new(format!("Name: {}", self.name)), - ListItem::new(format!("Provider Type: {}", self.provider_type)), - ]; - - if let Some(model) = &self.model_identifier { - items.push(ListItem::new(format!("Model: {}", model))); - } - - for (key, setting) in &self.additional_settings { - items.push(ListItem::new(format!("{}: {}", key, setting.value))); - } - - let list = List::new(items).block( - Block::default() - .borders(Borders::ALL) - .title("Confirm Provider Configuration"), - ); - - f.render_widget(list, area); - } - fn go_to_previous_step( &mut self, ) -> Result, ApplicationError> { @@ -673,11 +657,6 @@ impl ProviderCreator { } } - fn cancel_editing(&mut self) { - self.is_editing = false; - self.edit_buffer.clear(); - } - fn is_last_setting(&self) -> bool { if let Some(current_key) = &self.current_setting_key { let keys: Vec<_> = self.additional_settings.keys().collect(); diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/widgets/mod.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/widgets/mod.rs index e9f69aa..7c44475 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/widgets/mod.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/widgets/mod.rs @@ -1,5 +1,10 @@ mod filebrowser; +mod textarea; pub use filebrowser::{FileBrowserState, FileBrowserWidget}; +pub use textarea::{TextAreaState, TextAreaWidget}; -use super::{KeyTrack, ModalAction, TextArea, TextWindowTrait}; +use super::{ + KeyTrack, ModalAction, ReadDocument, ReadWriteDocument, TextArea, + TextBuffer, TextDocumentTrait, TextLine, TextWindowTrait, +}; diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/widgets/textarea.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/widgets/textarea.rs new file mode 100644 index 0000000..63de2d1 --- /dev/null +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/widgets/textarea.rs @@ -0,0 +1,99 @@ +use ratatui::buffer::Buffer; +use ratatui::layout::Rect; +use ratatui::style::Style; +use ratatui::widgets::{ + Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState, + StatefulWidget, StatefulWidgetRef, Widget, +}; + +use super::{ + KeyTrack, ReadDocument, ReadWriteDocument, TextBuffer, TextDocumentTrait, + TextLine, +}; + +pub struct TextAreaWidget(std::marker::PhantomData); + +pub struct TextAreaState<'a, T: TextDocumentTrait> { + text_buffer: TextBuffer<'a, T>, + scroll_offset: usize, +} + +impl TextAreaWidget { + pub fn new() -> Self { + Self(std::marker::PhantomData) + } +} + +impl<'a, T: TextDocumentTrait> TextAreaState<'a, T> { + pub fn new(document: T) -> Self { + Self { + text_buffer: TextBuffer::new(document), + scroll_offset: 0, + } + } + + pub fn text_buffer(&self) -> &TextBuffer<'a, T> { + &self.text_buffer + } + + pub fn text_buffer_mut(&mut self) -> &mut TextBuffer<'a, T> { + &mut self.text_buffer + } + + pub fn handle_key_event(&mut self, key_event: &KeyTrack) { + // For now, just print the received key events + eprintln!("Received key event: {:?}", key_event.current_key()); + } +} + +impl<'a> TextAreaState<'a, ReadDocument> { + pub fn with_read_document(text: Option>) -> Self { + let document = if let Some(text) = text { + ReadDocument::from_text(text) + } else { + ReadDocument::new() + }; + Self::new(document) + } +} + +impl<'a> TextAreaState<'a, ReadWriteDocument> { + pub fn with_read_write_document(text: Option>) -> Self { + let document = if let Some(text) = text { + ReadWriteDocument::from_text(text) + } else { + ReadWriteDocument::new() + }; + Self::new(document) + } +} + +impl StatefulWidget for &TextAreaWidget { + type State = TextAreaState<'static, T>; + + fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { + StatefulWidgetRef::render_ref(&self, area, buf, state) + } +} + +impl StatefulWidgetRef for &TextAreaWidget { + type State = TextAreaState<'static, T>; + + fn render_ref( + &self, + area: Rect, + buf: &mut Buffer, + state: &mut Self::State, + ) { + state.text_buffer.set_width(area.width as usize); + state.text_buffer.update_display_text(); + + let visible_text = state.text_buffer.display_window_lines( + state.scroll_offset, + state.scroll_offset + area.height as usize, + ); + + let paragraph = Paragraph::new(visible_text).style(Style::default()); + Widget::render(paragraph, area, buf); + } +} diff --git a/lumni/src/apps/builtin/llm/prompt/src/tui/window/mod.rs b/lumni/src/apps/builtin/llm/prompt/src/tui/window/mod.rs index 95e2f54..38e0779 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/tui/window/mod.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/tui/window/mod.rs @@ -15,7 +15,7 @@ pub use text_document::{ ReadDocument, ReadWriteDocument, SimpleString, TextDocumentTrait, TextLine, TextSegment, }; -pub use text_window::{TextWindow, TextWindowTrait}; +pub use text_window::{TextBuffer, TextWindow, TextWindowTrait}; pub use window_config::{ WindowConfig, WindowContent, WindowKind, WindowStatus, };