Skip to content

Commit

Permalink
initial workspace abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
aprxi committed Sep 9, 2024
1 parent 62c797b commit e326dff
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 52 deletions.
3 changes: 3 additions & 0 deletions lumni/src/apps/builtin/llm/prompt/src/chat/db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ impl ModelServerName {
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct WorkspaceId(pub i64);

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ConversationId(pub i64);

Expand Down
1 change: 1 addition & 0 deletions lumni/src/apps/builtin/llm/prompt/src/chat/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use super::tui::{
ContentDisplayMode, ConversationEvent, Conversations, KeyEventHandler,
ModalEvent, ModalWindowType, PromptAction, SimpleString, TextLine,
TextSegment, TextWindowTrait, UserEvent, WindowKind, WindowMode,
Workspaces,
};

// gets PERSONAS from the generated code
Expand Down
5 changes: 3 additions & 2 deletions lumni/src/apps/builtin/llm/prompt/src/chat/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use super::{
CompletionResponse, ContentDisplayMode, ConversationEvent, Conversations,
KeyEventHandler, ModalEvent, ModelServer, PromptAction, PromptError,
PromptInstruction, PromptNotReadyReason, ServerManager, TextWindowTrait,
UserEvent, WindowKind, WindowMode,
UserEvent, WindowKind, WindowMode, Workspaces,
};
pub use crate::external as lumni;

Expand Down Expand Up @@ -56,7 +56,8 @@ impl App<'_> {
let conversations =
Conversations::new(handler.fetch_conversation_list(100).await?);

let mut ui = AppUi::new(conversations, conversation_text).await;
let workspaces = Workspaces::new_as_default(conversations);
let mut ui = AppUi::new(workspaces, conversation_text).await;
ui.init();

Ok(App {
Expand Down
4 changes: 2 additions & 2 deletions lumni/src/apps/builtin/llm/prompt/src/tui/conversations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use ratatui::Frame;
use super::widgets::{ListWidget, ListWidgetState};
use super::{
Conversation, ConversationDbHandler, ConversationEvent, ConversationId,
ConversationSelectEvent, ConversationStatus, KeyTrack, ModalEvent,
PromptInstruction, ThreadedChatSession, UserEvent, WindowKind, WindowMode,
ConversationSelectEvent, ConversationStatus, KeyTrack, PromptInstruction,
ThreadedChatSession, WindowMode,
};
pub use crate::external as lumni;

Expand Down
89 changes: 64 additions & 25 deletions lumni/src/apps/builtin/llm/prompt/src/tui/draw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ use ratatui::{Frame, Terminal};

use super::ui::{ContentDisplayMode, ConversationUi};
use super::widgets::FileBrowser;
use super::{App, TextWindowTrait, WindowMode};
use crate::apps::builtin::llm::prompt::src::tui::ConversationEvent;
use super::{App, TextWindowTrait, WindowMode, Workspaces};

pub async fn draw_ui<B: Backend>(
terminal: &mut Terminal<B>,
Expand All @@ -18,39 +17,51 @@ pub async fn draw_ui<B: Backend>(
) -> Result<(), io::Error> {
terminal.draw(|frame| {
let terminal_area = frame.size();
const NAV_PANE_WIDTH: u16 = 32;
const NAV_TAB_HEIGHT: u16 = 2;
const LIST_PANE_WIDTH: u16 = 32;
const LIST_TAB_HEIGHT: u16 = 2;
const WORKSPACE_NAV_HEIGHT: u16 = 2;

// Default background
frame.render_widget(
Block::default().style(Style::default().bg(Color::Rgb(16, 24, 32))),
terminal_area,
);

match window_mode {
WindowMode::Conversation(Some(ConversationEvent::PromptInsert)) => {
app.ui.conversation_ui.set_prompt_window(true);
}
_ => {}
}

// Main layout
// Main layout with workspace navigation
let main_layout = Layout::default()
.direction(Direction::Horizontal)
.direction(Direction::Vertical)
.constraints([
Constraint::Length(NAV_PANE_WIDTH),
Constraint::Length(WORKSPACE_NAV_HEIGHT),
Constraint::Min(0),
])
.split(terminal_area);

let nav_pane = main_layout[0];
let content_pane = main_layout[1];
let workspace_nav_area = main_layout[0];
let content_area = main_layout[1];

render_workspace_nav::<B>(
frame,
workspace_nav_area,
&app.ui.workspaces,
);

// Sub-layout for list pane and content pane
let sub_layout = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Length(LIST_PANE_WIDTH),
Constraint::Min(0),
])
.split(content_area);

// Navigation pane styling
let nav_block = Block::default()
let list_pane = sub_layout[0];
let content_pane = sub_layout[1];
// List pane styling
let list_block = Block::default()
.borders(Borders::NONE)
.style(Style::default().bg(Color::Rgb(0, 0, 0)))
.style(Style::default().bg(Color::Rgb(16, 24, 32)));
frame.render_widget(nav_block, nav_pane);
frame.render_widget(list_block, list_pane);

// Content pane styling
let content_block = Block::default()
Expand All @@ -62,21 +73,25 @@ pub async fn draw_ui<B: Backend>(
let nav_layout = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(NAV_TAB_HEIGHT),
Constraint::Length(LIST_TAB_HEIGHT),
Constraint::Min(0),
])
.split(nav_pane);
.split(list_pane);

let nav_tab_area = nav_layout[0];
let list_tab_area = nav_layout[0];
let nav_content_area = nav_layout[1];

// Render navigation tabs
render_nav_tabs::<B>(frame, nav_tab_area, &app.ui.selected_mode);
render_list_tabs::<B>(frame, list_tab_area, &app.ui.selected_mode);

// Render navigation pane content
match &mut app.ui.selected_mode {
ContentDisplayMode::Conversation(_) => {
app.ui.conversations.render(frame, nav_content_area);
if let Some(conversations) =
app.ui.workspaces.current_conversations_mut()
{
conversations.render(frame, nav_content_area);
}
}
ContentDisplayMode::FileBrowser(filebrowser) => {
render_file_nav::<B>(frame, nav_content_area, filebrowser);
Expand All @@ -103,7 +118,31 @@ pub async fn draw_ui<B: Backend>(
Ok(())
}

fn render_nav_tabs<B: Backend>(
fn render_workspace_nav<B: Backend>(
frame: &mut Frame,
area: Rect,
workspaces: &Workspaces,
) {
let workspace_names: Vec<&str> = workspaces
.workspaces
.iter()
.map(|w| w.name.as_str())
.collect();

let tabs = Tabs::new(workspace_names)
.block(Block::default().borders(Borders::BOTTOM))
.select(workspaces.current_workspace_index)
.style(Style::default().fg(Color::White))
.highlight_style(
Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD),
);

frame.render_widget(tabs, area);
}

fn render_list_tabs<B: Backend>(
frame: &mut Frame,
area: Rect,
selected_mode: &ContentDisplayMode,
Expand Down
62 changes: 46 additions & 16 deletions lumni/src/apps/builtin/llm/prompt/src/tui/events/key_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ impl KeyEventHandler {
}
}

eprintln!(
"Modifier={:?}, Code={:?}",
current_key.modifiers, current_key.code
);

if current_key.modifiers == KeyModifiers::SHIFT {
// Catch Shift + (Back)Tab, Left or Right to switch main ui tabs
match current_key.code {
Expand Down Expand Up @@ -227,14 +232,25 @@ impl KeyEventHandler {
Some(ConversationEvent::Select(_)) => {
match &mut app_ui.selected_mode {
ContentDisplayMode::Conversation(_) => {
*current_mode = app_ui
.conversations
.handle_key_event(
&mut self.key_track,
tab_chat,
handler,
)
.await?;
if let Some(current_conversations) = app_ui
.workspaces
.current_conversations_mut()
{
*current_mode = current_conversations
.handle_key_event(
&mut self.key_track,
tab_chat,
handler,
)
.await?;
} else {
// Handle the case where there is no current workspace or conversations
log::warn!(
"No current workspace or \
conversations available"
);
// Optionally, you could set an error state or display a message to the user
}
}
_ => {
unreachable!(
Expand All @@ -245,6 +261,7 @@ impl KeyEventHandler {
}
return Ok(());
}

_ => return Ok(()),
};
}
Expand Down Expand Up @@ -323,14 +340,27 @@ impl KeyEventHandler {
// Forward event to the selected mode
match &mut app_ui.selected_mode {
ContentDisplayMode::Conversation(_) => {
*current_mode = app_ui
.conversations
.handle_key_event(
&mut self.key_track,
tab_chat,
handler,
)
.await?;
if let Some(current_conversations) =
app_ui.workspaces.current_conversations_mut()
{
*current_mode = current_conversations
.handle_key_event(
&mut self.key_track,
tab_chat,
handler,
)
.await?;
} else {
log::warn!(
"No current workspace or conversations \
available"
);
return Err(ApplicationError::NotReady(
"No current workspace or conversations \
available"
.to_string(),
));
}
}
ContentDisplayMode::FileBrowser(filebrowser) => {
match filebrowser.handle_key_event(&mut self.key_track)
Expand Down
4 changes: 3 additions & 1 deletion lumni/src/apps/builtin/llm/prompt/src/tui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod modals;
mod ui;
pub mod widgets;
mod window;
mod workspaces;

pub use colorscheme::{ColorScheme, ColorSchemeType};
pub use conversations::Conversations;
Expand All @@ -24,11 +25,12 @@ pub use window::{
SimpleString, TextBuffer, TextDocumentTrait, TextLine, TextSegment,
TextWindowTrait, WindowKind,
};
pub use workspaces::{Workspace, Workspaces};

use super::chat::db::{
Conversation, ConversationDatabase, ConversationDbHandler, ConversationId,
ConversationStatus, MaskMode, ModelSpec, ProviderConfig,
ProviderConfigOptions, UserProfile, UserProfileDbHandler,
ProviderConfigOptions, UserProfile, UserProfileDbHandler, WorkspaceId,
};
use super::chat::{
App, ChatSessionManager, NewConversation, PromptInstruction,
Expand Down
14 changes: 8 additions & 6 deletions lumni/src/apps/builtin/llm/prompt/src/tui/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ use ratatui::widgets::Borders;
use super::conversations::Conversations;
use super::modals::{FileBrowserModal, SettingsModal};
use super::widgets::FileBrowser;
use super::workspaces::Workspaces;
use super::{
CommandLine, ConversationDatabase, ConversationEvent, ConversationId,
ModalEvent, ModalWindowTrait, ModalWindowType, PromptWindow,
ResponseWindow, TextLine, TextWindowTrait, WindowKind, WindowMode,
workspaces, CommandLine, ConversationDatabase, ConversationEvent,
ConversationId, ModalEvent, ModalWindowTrait, ModalWindowType,
PromptWindow, ResponseWindow, TextLine, TextWindowTrait, WindowKind,
WindowMode,
};

#[derive(Debug)]
Expand Down Expand Up @@ -82,20 +84,20 @@ pub struct AppUi<'a> {
pub command_line: CommandLine<'a>,
pub modal: Option<Box<dyn ModalWindowTrait>>,
pub selected_mode: ContentDisplayMode,
pub conversations: Conversations,
pub workspaces: Workspaces,
pub conversation_ui: ConversationUi<'a>,
}

impl AppUi<'_> {
pub async fn new(
conversations: Conversations,
workspaces: Workspaces,
conversation_text: Option<Vec<TextLine>>,
) -> Self {
Self {
command_line: CommandLine::new(),
modal: None,
selected_mode: ContentDisplayMode::Conversation(None),
conversations,
workspaces,
conversation_ui: ConversationUi::new(conversation_text),
}
}
Expand Down
44 changes: 44 additions & 0 deletions lumni/src/apps/builtin/llm/prompt/src/tui/workspaces.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use std::path::PathBuf;

use super::conversations::Conversations;
use super::WorkspaceId;

pub struct Workspaces {
pub workspaces: Vec<Workspace>,
pub current_workspace_index: usize,
}

pub struct Workspace {
pub id: WorkspaceId,
pub name: String,
pub directory_path: Option<PathBuf>,
pub conversations: Conversations,
}

impl Workspaces {
pub fn new_as_default(conversations: Conversations) -> Self {
let default_workspace = Workspace {
id: WorkspaceId(1),
name: "Default".to_string(),
directory_path: None,
conversations,
};

Self {
workspaces: vec![default_workspace],
current_workspace_index: 0,
}
}

pub fn current_workspace(&self) -> Option<&Workspace> {
self.workspaces.get(self.current_workspace_index)
}

pub fn current_workspace_mut(&mut self) -> Option<&mut Workspace> {
self.workspaces.get_mut(self.current_workspace_index)
}

pub fn current_conversations_mut(&mut self) -> Option<&mut Conversations> {
self.current_workspace_mut().map(|w| &mut w.conversations)
}
}

0 comments on commit e326dff

Please sign in to comment.