- 数据库升到第 13 版:主机的登录、连接方式、代理和备注合进 hosts 一行,只有一对多的跳板、书签和端口转发单独成表;全部表改为 STRICT,并用 CHECK 写明约束;用凭据的主机不再冗余存用户名,凭据外键不再级联,在用的凭据删不掉 - 表名 sessions → hosts、session_jumps → host_jumps,session_id → host_id,地址列 host → address;schema 比对去掉排版,以后加字段可以直接 ADD COLUMN - 删除开发期的历史迁移代码:版本 1–12 的库直接拒绝打开、不动文件 - 代码里 Session / SessionId / SessionStore 等改为 Host*,模块 session/ → host/,Host.host → Host.address,菜单命令与界面元素 id 同步改名 - 外部 CLI 的参数、JSON 字段和错误码改为 host(host_not_found),协议版本升到 2 - CLAUDE.md、AGENTS.md、README 与 Agent skill 同步更新
124 lines
3.9 KiB
Rust
124 lines
3.9 KiB
Rust
use gpui_kit::component::{
|
|
ActiveTheme as _, Icon, Sizable, Size, ThemeStyled as _, h_flex, tooltip::Tooltip,
|
|
};
|
|
use gpui_kit::prelude::FluentBuilder as _;
|
|
use gpui_kit::*;
|
|
|
|
use crate::host::HostOs;
|
|
|
|
/// The mark that stands for a host: the operating system found on its host,
|
|
/// drawn on a badge in that project's own colour, or the first character of
|
|
/// the host name until a probe succeeds.
|
|
///
|
|
/// Shared so the host tree and the start page show one identity for the
|
|
/// same host. The badge borrows `Avatar`'s treatment, a circle at the theme's
|
|
/// radius, so identity marks look alike wherever the product shows one.
|
|
#[derive(IntoElement)]
|
|
pub struct HostMark {
|
|
id: ElementId,
|
|
name: SharedString,
|
|
os: Option<HostOs>,
|
|
size: Size,
|
|
tooltip: bool,
|
|
}
|
|
|
|
impl HostMark {
|
|
pub fn new(
|
|
id: impl Into<ElementId>,
|
|
name: impl Into<SharedString>,
|
|
os: Option<HostOs>,
|
|
) -> Self {
|
|
Self {
|
|
id: id.into(),
|
|
name: name.into(),
|
|
os,
|
|
size: Size::Medium,
|
|
tooltip: true,
|
|
}
|
|
}
|
|
|
|
/// No tooltip of its own, for a row that shows one for the whole row, as
|
|
/// the host tree does, so two tooltips never stack.
|
|
pub fn without_tooltip(mut self) -> Self {
|
|
self.tooltip = false;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl Sizable for HostMark {
|
|
fn with_size(mut self, size: impl Into<Size>) -> Self {
|
|
self.size = size.into();
|
|
self
|
|
}
|
|
}
|
|
|
|
impl RenderOnce for HostMark {
|
|
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
|
|
let compact = matches!(self.size, Size::XSmall | Size::Small);
|
|
let (background, ink, glyph, description) = match self.os {
|
|
Some(os) => {
|
|
let (background, ink) = match (os.brand_color(), os.brand_foreground()) {
|
|
(Some(background), Some(ink)) => (background.into(), ink.into()),
|
|
// A monochrome mark follows the theme, which is also how
|
|
// Apple's own guidance draws it.
|
|
_ => (cx.theme().foreground, cx.theme().background),
|
|
};
|
|
let glyph = Icon::default().path(os.icon_path());
|
|
(
|
|
background,
|
|
ink,
|
|
if compact {
|
|
glyph.size_3()
|
|
} else {
|
|
glyph.size_5()
|
|
}
|
|
.into_any_element(),
|
|
SharedString::from(os.label()),
|
|
)
|
|
}
|
|
None => (
|
|
cx.theme().muted,
|
|
cx.theme().muted_foreground,
|
|
div()
|
|
.map(|text| {
|
|
if compact {
|
|
text.text_xs()
|
|
} else {
|
|
text.text_base()
|
|
}
|
|
})
|
|
.child(
|
|
self.name
|
|
.chars()
|
|
.next()
|
|
.map(|character| character.to_uppercase().to_string())
|
|
.unwrap_or_default(),
|
|
)
|
|
.into_any_element(),
|
|
SharedString::from("未探测到系统"),
|
|
),
|
|
};
|
|
let tooltip = description.clone();
|
|
h_flex()
|
|
.id(self.id)
|
|
.test_support()
|
|
.aria_label(description)
|
|
.when(self.tooltip, |badge| {
|
|
badge.tooltip(move |window, cx| Tooltip::new(tooltip.clone()).build(window, cx))
|
|
})
|
|
.flex_shrink_0()
|
|
.map(|badge| {
|
|
if compact {
|
|
badge.size_5()
|
|
} else {
|
|
badge.size_8()
|
|
}
|
|
})
|
|
.justify_center()
|
|
.rounded_full_style(cx)
|
|
.bg(background)
|
|
.text_color(ink)
|
|
.child(glyph)
|
|
}
|
|
}
|