refactor(ui): 拆分 app/connection_view/widgets 为子组件,对话框归拢 dialogs/

- ui/app/ 拆为门面、events(引擎事件泵)、actions(用户动作)、io(导入导出)、
  theme(主题)、titlebar/sidebar/main_area(渲染分区)
- ui/connection_view/ 拆为门面、subscribe、messages、publish、vars、util(纯函数)
- ui/widgets/ 拆为 select(可搜索下拉)、kv_editor、common(字段行/状态/时间)
- 五个对话框归拢 ui/dialogs/,linear-theme.json 随 theme.rs 迁入 ui/app/
- clippy 0 警告,全部路径经门面保持稳定
This commit is contained in:
2026-09-29 22:20:53 +08:00
parent b5089f5898
commit bc9f55c889
30 changed files with 4884 additions and 4652 deletions
+68
View File
@@ -0,0 +1,68 @@
//! 基础部件:字段行布局、连接状态指示、时间格式化。
use gpui_kit::component::{h_flex, v_flex, ActiveTheme as _};
use gpui_kit::{div, px, App, Hsla, IntoElement, ParentElement as _, SharedString, Styled as _};
use crate::model::ConnectionStatus;
/// 小号标签 + 控件的垂直字段容器(可选字段,无校验态)。
pub fn field(label: impl Into<SharedString>, control: impl IntoElement) -> impl IntoElement {
field_ex(label, control, false, None, Hsla::default())
}
/// 字段行变体:`required` 时标签后追加红色 `*`;
/// `error` 为 Some 时在控件下方渲染红字提示(`danger` 为主题错误色)。
pub fn field_ex(
label: impl Into<SharedString>,
control: impl IntoElement,
required: bool,
error: Option<SharedString>,
danger: Hsla,
) -> impl IntoElement {
v_flex()
.gap_1()
.min_w(px(0.))
.child(
h_flex()
.gap_0p5()
.items_baseline()
.child(div().text_xs().child(label.into()))
.children(required.then(|| div().text_xs().text_color(danger).child("*"))),
)
.child(control)
.children(error.map(|e| div().text_xs().text_color(danger).child(e)))
}
// ─── 状态颜色 ────────────────────────────────────────────────────────────────
pub fn status_color(status: ConnectionStatus, cx: &App) -> Hsla {
let t = cx.theme();
match status {
ConnectionStatus::Connected => t.success,
ConnectionStatus::Connecting => t.warning,
ConnectionStatus::Error => t.danger,
ConnectionStatus::Disconnected => t.muted_foreground,
}
}
pub fn status_dot(status: ConnectionStatus, cx: &App) -> impl IntoElement {
div()
.size(px(8.))
.rounded_full()
.bg(status_color(status, cx))
}
// ─── 键值对编辑器 ────────────────────────────────────────────────────────────
pub fn format_time(ts_ms: i64, show_millis: bool) -> String {
use chrono::TimeZone as _;
let Some(dt) = chrono::Local.timestamp_millis_opt(ts_ms).single() else {
return String::new();
};
if show_millis {
dt.format("%H:%M:%S%.3f").to_string()
} else {
dt.format("%H:%M:%S").to_string()
}
}