Skip to content

Commit 9780a6a

Browse files
committed
feat(component): 路由雏形完善,状态条功能基本完善
1 parent 025f96f commit 9780a6a

11 files changed

Lines changed: 357 additions & 21 deletions

File tree

console/console-ui/src/components/HelloWorld.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const count = ref(0)
1616
</div>
1717
<div>
1818
<h1>Get started</h1>
19-
<p>Edit <code>src/App.vue</code> and save to test <code>HMR</code></p>
19+
<p>Edit <code>src/app.vue</code> and save to test <code>HMR</code></p>
2020
</div>
2121
<button type="button" class="counter" @click="count++">
2222
Count is {{ count }}
File renamed without changes.

src/components/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
pub mod welcome;
2-
pub(crate) mod input_bar;
3-
mod status_bar;
2+
pub mod input_bar;
3+
pub mod status_bar;

src/components/status_bar.rs

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
use ratatui_kit::{
2+
Component,
3+
ComponentDrawer,
4+
ComponentUpdater,
5+
Hooks,
6+
Props,
7+
UseState,
8+
};
9+
10+
use ratatui_kit::ratatui::{
11+
style::{Color, Style, Stylize},
12+
text::{Line, Span},
13+
widgets::{Block, Borders, Paragraph, Widget},
14+
layout::{Alignment, Rect},
15+
buffer::Buffer,
16+
};
17+
18+
#[derive(Props)]
19+
pub struct StatusBarProps {
20+
pub project_path: Option<String>,
21+
pub context_length: usize,
22+
pub max_context_length: usize,
23+
pub style: Style,
24+
pub progress_style: Style,
25+
pub border_style: Style,
26+
}
27+
28+
impl Default for StatusBarProps {
29+
fn default() -> Self {
30+
Self {
31+
project_path: None,
32+
context_length: 0,
33+
max_context_length: 100,
34+
style: Style::default(),
35+
progress_style: Style::default().fg(Color::Green),
36+
border_style: Style::default(),
37+
}
38+
}
39+
}
40+
41+
pub struct StatusBar {
42+
project_path: Option<String>,
43+
context_length: usize,
44+
max_context_length: usize,
45+
style: Style,
46+
progress_style: Style,
47+
border_style: Style,
48+
}
49+
50+
impl Component for StatusBar {
51+
type Props<'a> = StatusBarProps;
52+
53+
fn new(props: &Self::Props<'_>) -> Self {
54+
Self {
55+
project_path: props.project_path.clone(),
56+
context_length: props.context_length,
57+
max_context_length: props.max_context_length,
58+
style: props.style,
59+
progress_style: props.progress_style,
60+
border_style: props.border_style,
61+
}
62+
}
63+
64+
fn update(
65+
&mut self,
66+
props: &mut Self::Props<'_>,
67+
_hooks: Hooks,
68+
_updater: &mut ComponentUpdater,
69+
) {
70+
// 同步props
71+
self.project_path = props.project_path.clone();
72+
self.context_length = props.context_length;
73+
self.max_context_length = props.max_context_length;
74+
self.style = props.style;
75+
self.progress_style = props.progress_style;
76+
self.border_style = props.border_style;
77+
}
78+
79+
fn draw(&mut self, drawer: &mut ComponentDrawer<'_, '_>) {
80+
let area = drawer.area;
81+
let buf = drawer.buffer_mut();
82+
83+
// 绘制顶部边框线
84+
self.render_border_top(buf, area);
85+
86+
// 绘制内容区域(减去顶部边框占用的一行)
87+
let content_area = Rect {
88+
x: area.x,
89+
y: area.y + 1,
90+
width: area.width,
91+
height: area.height.saturating_sub(1),
92+
};
93+
94+
self.render_content(buf, content_area);
95+
}
96+
}
97+
98+
impl StatusBar {
99+
/// 渲染顶部边框线
100+
fn render_border_top(&self, buf: &mut Buffer, area: Rect) {
101+
if area.height > 0 {
102+
for x in area.left()..area.right() {
103+
buf.get_mut(x, area.top())
104+
.set_char('─')
105+
.set_style(self.border_style);
106+
}
107+
}
108+
}
109+
110+
/// 渲染内容区域
111+
fn render_content(&self, buf: &mut Buffer, area: Rect) {
112+
if area.height == 0 {
113+
return;
114+
}
115+
116+
// 创建主段落容器
117+
let content_line = self.create_content_line(area.width as usize);
118+
119+
// 使用Paragraph渲染内容
120+
Paragraph::new(content_line)
121+
.alignment(Alignment::Left)
122+
.render(area, buf);
123+
}
124+
125+
/// 创建内容行,包含左侧路径和右侧进度条
126+
fn create_content_line(&self, total_width: usize) -> Line {
127+
let mut spans = Vec::new();
128+
129+
// 计算左右两部分的最大宽度
130+
let min_left_width = 20; // 最小左侧宽度,确保路径能显示一些内容
131+
let min_right_width = 20; // 最小右侧宽度,确保进度条能显示
132+
133+
let left_max_width = std::cmp::max(total_width.saturating_sub(min_right_width), min_left_width);
134+
let right_max_width = std::cmp::max(total_width.saturating_sub(left_max_width), min_right_width);
135+
136+
// 添加左侧项目路径
137+
if let Some(ref path) = self.project_path {
138+
let truncated_path = self.truncate_path_for_display(path, left_max_width);
139+
spans.push(Span::styled(truncated_path, self.style));
140+
} else {
141+
spans.push(Span::styled("No project", self.style.dim()));
142+
}
143+
144+
// 计算中间空白区域
145+
let current_path_width = self.project_path.as_ref().map(|p| {
146+
let truncated = self.truncate_path_for_display(p, left_max_width);
147+
truncated.len()
148+
}).unwrap_or(7); // "No project" 长度为9,这里用7是为了安全
149+
150+
// 为右侧进度条预留空间
151+
let progress_bar_width = std::cmp::min(right_max_width, 20); // 最大20字符宽的进度条
152+
let required_space = progress_bar_width + 3; // 加上一些间隔
153+
let available_middle_space = total_width.saturating_sub(current_path_width + progress_bar_width);
154+
155+
// 添加中间空白
156+
if available_middle_space > 0 {
157+
spans.push(Span::raw(" ".repeat(available_middle_space)));
158+
}
159+
160+
// 添加右侧上下文长度进度条
161+
let progress_spans = self.create_progress_bar(progress_bar_width);
162+
spans.extend(progress_spans);
163+
164+
Line::from(spans)
165+
}
166+
167+
/// 将路径截断以适应显示宽度
168+
fn truncate_path_for_display(&self, path: &str, max_width: usize) -> String {
169+
if path.len() <= max_width {
170+
return path.to_string();
171+
}
172+
173+
if max_width <= 5 {
174+
return if max_width >= 3 {
175+
"..".to_string()
176+
} else {
177+
"".to_string()
178+
};
179+
}
180+
181+
let part_len = (max_width - 3) / 2;
182+
let start_part = &path[..std::cmp::min(part_len, path.len())];
183+
let end_start = std::cmp::max(path.len() - part_len, part_len);
184+
let end_part = &path[end_start..];
185+
186+
format!("{}...{}", start_part, end_part)
187+
}
188+
189+
/// 创建上下文长度进度条
190+
fn create_progress_bar(&self, max_width: usize) -> Vec<Span> {
191+
let mut spans = Vec::new();
192+
193+
if self.max_context_length == 0 {
194+
// 如果最大长度为0,则显示简单的文本
195+
spans.push(Span::styled(
196+
format!("Ctx: {}", self.context_length),
197+
self.style,
198+
));
199+
return spans;
200+
}
201+
202+
let progress_ratio = self.context_length as f64 / self.max_context_length as f64;
203+
let filled_count = ((max_width as f64 * progress_ratio).round() as usize)
204+
.min(max_width.saturating_sub(2)); // 保留至少2个字符用于显示百分比
205+
206+
// 计算百分比
207+
let percentage = (progress_ratio * 100.0).round() as usize;
208+
209+
// 根据百分比选择颜色
210+
let bar_color = if percentage < 50 {
211+
Color::Green
212+
} else if percentage < 80 {
213+
Color::Yellow
214+
} else {
215+
Color::Red
216+
};
217+
218+
// 构建进度条字符串
219+
let mut progress_str = String::new();
220+
for i in 0..max_width {
221+
if i < filled_count {
222+
progress_str.push('█');
223+
} else if i == filled_count && filled_count < max_width.saturating_sub(2) {
224+
// 在进度条末尾添加百分比,如果还有空间的话
225+
break;
226+
} else {
227+
progress_str.push('░');
228+
}
229+
}
230+
231+
// 添加进度条
232+
spans.push(Span::styled(
233+
format!("{}", progress_str),
234+
self.progress_style.fg(bar_color),
235+
));
236+
237+
// 如果有足够的空间,添加百分比和数值信息
238+
if max_width > progress_str.len() + 5 { // 需要额外空间显示百分比
239+
spans.push(Span::styled(
240+
format!(" {}% ({}/{})",
241+
percentage,
242+
self.context_length,
243+
self.max_context_length),
244+
self.style,
245+
));
246+
} else if max_width > progress_str.len() + 2 { // 至少显示数值
247+
spans.push(Span::styled(
248+
format!(" {}/{}", self.context_length, self.max_context_length),
249+
self.style,
250+
));
251+
}
252+
253+
spans
254+
}
255+
}

src/lib.rs

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
#[macro_use]
22
extern crate rust_i18n;
33

4-
use std::io;
4+
use crate::core::db::DatabaseManager;
5+
use crate::pages::layout::App;
56
use log::info;
6-
use ratatui_kit::{element, ElementExt};
7+
use ratatui_kit::crossterm::event::{Event, KeyCode, KeyEventKind};
78
use ratatui_kit::prelude::RouterProvider;
9+
use ratatui_kit::{AnyElement, ElementExt, Hooks, UseEvents, UseRouter, component, element};
810
use rust_i18n::t;
9-
use crate::core::db::DatabaseManager;
10-
use crate::pages::layout::App;
11+
use std::io;
1112

12-
pub mod core;
13-
pub mod runtime;
14-
pub mod services;
15-
pub mod pages;
16-
pub mod utils;
1713
pub mod components;
14+
pub mod core;
15+
pub mod db;
1816
pub mod health;
17+
pub mod pages;
1918
pub mod platform;
20-
pub mod db;
21-
pub mod state;
2219
pub mod router;
20+
pub mod runtime;
21+
pub mod services;
22+
pub mod state;
23+
pub mod utils;
2324

24-
// use crate::pages::chat::chat_page;
25-
use crate::pages::welcome::WelcomePage;
25+
use crate::router::app_routes;
2626

2727
i18n!("locales", fallback = "en");
2828

@@ -33,6 +33,25 @@ pub async fn run() -> anyhow::Result<()> {
3333
info!("{}", t!("current_locale", locale_name = "en"));
3434
let db = DatabaseManager::new()?;
3535
db.health_check()?;
36-
element!(WelcomePage()).fullscreen().await.expect("Failed to render welcome page");
36+
37+
element!(Root)
38+
.into_any()
39+
.fullscreen()
40+
.await
41+
.expect("Failed to run the application");
42+
3743
Ok(())
3844
}
45+
46+
#[component]
47+
pub fn Root(_hooks: Hooks) -> impl Into<AnyElement<'static>> {
48+
// 严格按照官方文档:RouterProvider 放最顶层
49+
element!(
50+
RouterProvider(
51+
routes: app_routes(),
52+
index_path: "/router",
53+
)
54+
)
55+
}
56+
57+

src/pages/chat.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,29 @@
11
use ratatui_kit::{component, element, AnyElement, ElementExt, Hooks};
22
use ratatui_kit::components::View;
33
use ratatui_kit::ratatui::{TerminalOptions, Viewport};
4-
use ratatui_kit::ratatui::layout::Direction;
4+
use ratatui_kit::ratatui::layout::{Direction, Constraint};
5+
use ratatui_kit::ratatui::prelude::Color;
6+
use ratatui_kit::ratatui::style::Style;
57
use crate::components::input_bar::InputBar;
8+
use crate::components::status_bar::StatusBar;
69

710
#[component]
811
pub fn ChatPage(mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
9-
element!(ratatui_kit::components::textarea::TextArea)
12+
element!(
13+
View(flex_direction: Direction::Vertical,) {
14+
View(height: Constraint::Length(10),) {
15+
InputBar()
16+
}
17+
View(height: Constraint::Length(4),) {
18+
StatusBar(
19+
project_path: Some("/home/project/OmegaProject/".into()),
20+
context_length: 123987usize,
21+
max_context_length: 1000000usize,
22+
style: Style::default(),
23+
progress_style: Style::default().fg(Color::Green),
24+
border_style: Style::default(),
25+
)
26+
}
27+
}
28+
)
1029
}

src/pages/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ pub mod layout;
22
pub mod welcome;
33
pub mod chat;
44
pub mod context;
5+
pub mod router;

0 commit comments

Comments
 (0)