diff --git a/src/components/input_bar.rs b/src/components/input_bar.rs index 647ab51..3b16f9a 100644 --- a/src/components/input_bar.rs +++ b/src/components/input_bar.rs @@ -44,10 +44,10 @@ pub fn InputBar(mut hooks: Hooks) -> impl Into> { }); element!(Border( - height: Constraint::Length(4), + height: Constraint::Length(6), style: Style::default().green(), - bottom_title: Line::styled( - "Press 'Enter' to submit, 'Esc' to exit", + top_title: Line::styled( + "Composer", Style::default().yellow(), ).centered(), ) { diff --git a/src/components/main_scroll.rs b/src/components/main_scroll.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/components/mod.rs b/src/components/mod.rs index cd96e7e..19919f9 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,3 +1,5 @@ pub mod welcome; pub mod input_bar; pub mod status_bar; +mod main_scroll; +mod progress_bar; diff --git a/src/components/progress_bar.rs b/src/components/progress_bar.rs new file mode 100644 index 0000000..af3a07c --- /dev/null +++ b/src/components/progress_bar.rs @@ -0,0 +1,81 @@ +use ratatui_kit::prelude::*; +use ratatui_kit::ratatui::{ + style::{Modifier, Style}, + text::{Line, Span}, +}; +use ratatui_kit::ratatui::layout::Direction; + +#[derive(Debug, Clone, Props)] +pub struct ProgressBarProps { + pub style: Style, + pub menu_style: Style, + pub install_message: String, + pub stage: String, + pub percent: f64, + pub downloaded: u64, + pub total: u64, + pub speed: f64, +} + +impl Default for ProgressBarProps { + fn default() -> Self { + Self { + style: Style::default(), + menu_style: Style::default(), + install_message: String::new(), + stage: String::new(), + percent: 0.0, + downloaded: 0, + total: 0, + speed: 0.0, + } + } +} + +#[component] +pub fn ProgressBar( + _hooks: Hooks, + props: &ProgressBarProps, +) -> impl Into> { + // 计算进度条 + let percent = props.percent.clamp(0.0, 100.0); + const BAR_WIDTH: usize = 30; + let fill = ((percent / 100.0 * BAR_WIDTH as f64) as usize).min(BAR_WIDTH); + let bar = format!("[{}{}]", "█".repeat(fill), "░".repeat(BAR_WIDTH - fill)); + + let mut spans = vec![ + Span::styled(bar, props.menu_style), + Span::raw(format!(" {:.2}%", percent)), + ]; + + if props.stage.contains("Downloading") { + let mb = props.downloaded as f64 / 1024.0 / 1024.0; + let total_mb = props.total as f64 / 1024.0 / 1024.0; + let speed_mb = props.speed / 1024.0 / 1024.0; + spans.push(Span::raw(format!(" ({:.2} MB/{:.2} MB)", mb, total_mb))); + spans.push(Span::raw(format!(" {:.2} MB/s", speed_mb))); + } else { + spans.push(Span::raw(format!(" {} / {}", props.downloaded, props.total))); + } + + // 官方标准 element! 语法(零错误) + element! { + View(flex_direction: Direction::Vertical) { + // 必须传 String,不能传 &str + Text(text: props.install_message.clone(), style: props.style.add_modifier(Modifier::BOLD)) {} + Text(text: String::new()) {} + + // 官方条件渲染:#(if ...) + #(if !props.stage.is_empty() { + Some(element! { + View { + Text(text: props.stage.clone(), style: props.style.add_modifier(Modifier::BOLD)) {} + // Text(spans: spans) {} + } + }) + } else { + None + }) + } + } +} \ No newline at end of file diff --git a/src/db/store/mod.rs b/src/db/store/mod.rs index e69de29..ec023ed 100644 --- a/src/db/store/mod.rs +++ b/src/db/store/mod.rs @@ -0,0 +1,58 @@ +use ratatui_kit::Store; + +pub struct TodoItem { + pub id: u32, + pub title: String, + pub completed: bool, +} + +pub struct TodoList { + pub items: Vec, +} + +pub struct TaskItem { + pub id: u32, + pub title: String, + pub completed: bool, +} + +pub struct TaskList { + pub items: Vec, +} + +#[derive(Store)] +pub struct GlobalStore { + pub llm_name: String, + pub current_chat: String, + pub cache_size: f64, + pub context_size: f64, + pub model_context_window: f64, + pub input_tokens: u32, + pub output_tokens: u32, + pub current_chat_count: u32, + pub todo_list: TodoList, + pub task_list: TaskList, + pub input_buffer: String, +} + +impl Default for GlobalStore { + fn default() -> Self { + Self { + llm_name: String::new(), + current_chat: String::new(), + cache_size: 0.0, + context_size: 0.0, + model_context_window: 0.0, + input_tokens: 0, + output_tokens: 0, + current_chat_count: 0, + todo_list: TodoList { + items: Vec::new(), + }, + task_list: TaskList { + items: Vec::new(), + }, + input_buffer: String::new(), + } + } +} \ No newline at end of file diff --git a/src/pages/router.rs b/src/pages/router.rs index eac98a2..07c8fbb 100644 --- a/src/pages/router.rs +++ b/src/pages/router.rs @@ -24,6 +24,8 @@ pub fn RouterPage(mut hooks: Hooks) -> impl Into> { }); //todo 这里的逻辑是通过数据库查询是否初始化,从而确定要路由到 Welcome 还是 Chat + //todo 为了开发方便,这里的逻辑还需要判断是否是开发环境,如果是开发环境,默认路由到路由页面,路由页面在生产环境不对外暴露 + //todo 同时路由函数禁止在生成环境中调用,避免在生产环境中路由到路由页面 element!( View(height: Constraint::Length(10),) {