feat: 添加 README,更新 .gitignore,移除 JE-Skin/eskin-finger-sdk

- 添加 README.md 项目文档
- 更新 .gitignore 排除 JE-Skin/、eskin-finger-sdk/ 及构建产物
- 从 git 跟踪中移除 JE-Skin 和 eskin-finger-sdk

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
lennlouisgeek
2026-05-20 03:15:16 +08:00
parent d2c9fad556
commit 5f1c217853
19 changed files with 628 additions and 116 deletions

306
src/ui.rs
View File

@@ -10,6 +10,9 @@ pub struct FloatingPanelState {
pub visible: bool,
default_pos: egui::Pos2,
tag_pos: egui::Pos2,
center_anim: f32,
center_anim_target: bool,
center_anim_last_time: Option<f64>,
}
pub struct ConfigPanelState {
@@ -63,6 +66,9 @@ impl FloatingPanelState {
visible: true,
default_pos: egui::pos2(default_pos[0], default_pos[1]),
tag_pos: egui::pos2(tag_pos[0], tag_pos[1]),
center_anim: 1.0,
center_anim_target: true,
center_anim_last_time: None,
}
}
}
@@ -131,7 +137,7 @@ pub fn draw_connect_panel(
ConnectionState::Connected | ConnectionState::Streaming
);
draw_center_floating_panel(ctx, panel, "connect_center_panel", 64.0, |ui| {
draw_center_floating_panel(ctx, panel, "connect_center_panel", 42.0, "连接", |ui| {
ui.set_min_width(320.0);
ui.vertical(|ui| {
@@ -654,18 +660,308 @@ fn draw_center_floating_panel(
panel: &mut FloatingPanelState,
id: &'static str,
top_offset: f32,
collapsed_label: &'static str,
add_contents: impl FnOnce(&mut egui::Ui),
) {
if !panel.visible {
const PANEL_WIDTH: f32 = 430.0;
const SLIDE_DISTANCE: f32 = 18.0;
let anim = advance_center_panel_anim(ctx, panel);
let eased = ease_in_out(anim);
if !panel.visible || anim < 0.28 {
let handle_y = top_offset - (1.0 - eased) * 2.0;
egui::Area::new(egui::Id::new(format!("{id}_handle")))
.anchor(egui::Align2::CENTER_TOP, egui::vec2(0.0, handle_y))
.order(egui::Order::Tooltip)
.show(ctx, |ui| {
let handle_rect = ui
.allocate_exact_size(egui::vec2(112.0, 24.0), egui::Sense::hover())
.0;
if integrated_handle(ui, handle_rect, collapsed_label, false, "打开连接面板")
.clicked()
{
panel.visible = true;
}
});
}
if anim <= 0.02 {
return;
}
egui::Area::new(egui::Id::new(id))
.anchor(egui::Align2::CENTER_TOP, egui::vec2(0.0, top_offset))
.order(egui::Order::Foreground)
.anchor(
egui::Align2::CENTER_TOP,
egui::vec2(0.0, top_offset - (1.0 - eased) * SLIDE_DISTANCE),
)
.order(egui::Order::Tooltip)
.show(ctx, |ui| {
panel_frame(ctx).show(ui, |ui| {
let response = center_panel_shell(ui, PANEL_WIDTH, |ui| {
ui.set_width(PANEL_WIDTH);
add_contents(ui);
ui.add_space(6.0);
let handle_rect = allocate_center_panel_handle(ui);
if integrated_handle(ui, handle_rect, "", true, "收起连接面板").clicked() {
panel.visible = false;
}
handle_rect
});
paint_integrated_center_panel(ui, response.response.rect, response.inner, true);
});
}
fn advance_center_panel_anim(ctx: &egui::Context, panel: &mut FloatingPanelState) -> f32 {
let target = if panel.visible { 1.0 } else { 0.0 };
let now = ctx.input(|input| input.time);
let delta_time = panel
.center_anim_last_time
.map(|last_time| (now - last_time) as f32)
.unwrap_or(1.0 / 60.0)
.clamp(0.0, 0.05);
panel.center_anim_last_time = Some(now);
panel.center_anim_target = panel.visible;
let speed = 7.5;
let step = speed * delta_time;
if panel.center_anim < target {
panel.center_anim = (panel.center_anim + step).min(target);
} else if panel.center_anim > target {
panel.center_anim = (panel.center_anim - step).max(target);
}
if (panel.center_anim - target).abs() > 0.001 {
ctx.request_repaint();
}
panel.center_anim
}
fn ease_in_out(t: f32) -> f32 {
let t = t.clamp(0.0, 1.0);
t * t * (3.0 - 2.0 * t)
}
fn center_panel_shell<R>(
ui: &mut egui::Ui,
width: f32,
add_contents: impl FnOnce(&mut egui::Ui) -> R,
) -> egui::InnerResponse<R> {
egui::Frame::new()
.fill(ENGINEERING_DARK.panel)
.corner_radius(egui::CornerRadius::same(ENGINEERING_DARK.radius))
.inner_margin(egui::Margin::symmetric(10, 8))
.show(ui, |ui| {
ui.set_width(width);
add_contents(ui)
})
}
fn allocate_center_panel_handle(ui: &mut egui::Ui) -> egui::Rect {
let handle_width = 112.0;
let handle_height = 24.0;
let left_space = ((ui.available_width() - handle_width) * 0.5).max(0.0);
let mut handle_rect = egui::Rect::NOTHING;
ui.horizontal(|ui| {
ui.add_space(left_space);
let (rect, _) = ui.allocate_exact_size(
egui::vec2(handle_width, handle_height),
egui::Sense::hover(),
);
handle_rect = rect;
});
handle_rect
}
fn integrated_handle(
ui: &mut egui::Ui,
rect: egui::Rect,
label: &str,
expanded: bool,
tooltip: &'static str,
) -> egui::Response {
let response = ui
.interact(
rect.expand2(egui::vec2(12.0, 5.0)),
ui.id().with(("center_panel_handle", expanded)),
egui::Sense::click(),
)
.on_hover_text(tooltip);
paint_integrated_handle(ui, rect, label, expanded, response.hovered());
response
}
fn paint_integrated_handle(
ui: &mut egui::Ui,
rect: egui::Rect,
label: &str,
expanded: bool,
hovered: bool,
) {
let center = rect.center();
if !expanded {
let fill = if hovered {
ENGINEERING_DARK.panel_strong
} else {
egui::Color32::from_rgb(31, 41, 52)
};
let points = vec![
egui::pos2(rect.left() - 12.0, rect.top() - 1.0),
egui::pos2(rect.right() + 12.0, rect.top() - 1.0),
egui::pos2(rect.right() + 3.0, rect.bottom() + 8.0),
egui::pos2(rect.left() - 3.0, rect.bottom() + 8.0),
];
ui.painter().add(egui::Shape::convex_polygon(
points.clone(),
fill,
egui::Stroke::new(1.0, ENGINEERING_DARK.border),
));
ui.painter().line_segment(
[points[2], points[3]],
egui::Stroke::new(1.0, ENGINEERING_DARK.border_soft),
);
}
let arrow = if expanded { -1.0 } else { 1.0 };
let arrow_center_x = if label.is_empty() {
center.x
} else {
rect.right() - 18.0
};
let arrow_center = egui::pos2(arrow_center_x, center.y);
let arrow_color = if hovered {
ENGINEERING_DARK.accent_hot
} else {
ENGINEERING_DARK.text_dim
};
ui.painter().line_segment(
[
arrow_center + egui::vec2(-6.0, -2.0 * arrow),
arrow_center + egui::vec2(0.0, 4.0 * arrow),
],
egui::Stroke::new(1.7, arrow_color),
);
ui.painter().line_segment(
[
arrow_center + egui::vec2(6.0, -2.0 * arrow),
arrow_center + egui::vec2(0.0, 4.0 * arrow),
],
egui::Stroke::new(1.7, arrow_color),
);
if !label.is_empty() {
let galley = ui.fonts_mut(|fonts| {
fonts.layout_no_wrap(
label.to_owned(),
egui::FontId::proportional(12.0),
ENGINEERING_DARK.text,
)
});
ui.painter().galley(
egui::pos2(rect.left() + 14.0, center.y - galley.size().y * 0.5 - 1.0),
galley,
ENGINEERING_DARK.text,
);
}
}
fn paint_integrated_center_panel(
ui: &egui::Ui,
rect: egui::Rect,
handle_rect: egui::Rect,
handle_on_bottom: bool,
) {
let painter = ui.painter();
let stroke = egui::Stroke::new(1.0, ENGINEERING_DARK.border);
let accent_stroke = egui::Stroke::new(1.2, ENGINEERING_DARK.border_soft);
let top = rect.top();
let left = rect.left();
let right = rect.right();
let bottom = rect.bottom();
let radius = ENGINEERING_DARK.radius as f32;
let tab_left = handle_rect.left() - 18.0;
let tab_right = handle_rect.right() + 18.0;
let tab_recess = if handle_on_bottom {
handle_rect.top() - 2.0
} else {
handle_rect.bottom() + 2.0
};
if handle_on_bottom {
painter.line_segment(
[
egui::pos2(left + radius, top),
egui::pos2(right - radius, top),
],
stroke,
);
} else {
painter.line_segment(
[egui::pos2(left + radius, top), egui::pos2(tab_left, top)],
stroke,
);
painter.line_segment(
[egui::pos2(tab_right, top), egui::pos2(right - radius, top)],
stroke,
);
}
painter.line_segment(
[
egui::pos2(left, top + radius),
egui::pos2(left, bottom - radius),
],
stroke,
);
painter.line_segment(
[
egui::pos2(right, top + radius),
egui::pos2(right, bottom - radius),
],
stroke,
);
if handle_on_bottom {
painter.line_segment(
[
egui::pos2(left + radius, bottom),
egui::pos2(tab_left, bottom),
],
stroke,
);
painter.line_segment(
[
egui::pos2(tab_right, bottom),
egui::pos2(right - radius, bottom),
],
stroke,
);
} else {
painter.line_segment(
[
egui::pos2(left + radius, bottom),
egui::pos2(right - radius, bottom),
],
stroke,
);
}
let tab_points = if handle_on_bottom {
vec![
egui::pos2(tab_left, bottom),
egui::pos2(handle_rect.left() - 8.0, tab_recess),
egui::pos2(handle_rect.right() + 8.0, tab_recess),
egui::pos2(tab_right, bottom),
]
} else {
vec![
egui::pos2(tab_left, top),
egui::pos2(handle_rect.left() - 8.0, tab_recess),
egui::pos2(handle_rect.right() + 8.0, tab_recess),
egui::pos2(tab_right, top),
]
};
painter.add(egui::Shape::line(tab_points, accent_stroke));
}