新增本地服务器管理面板
新增 egui 服务器管理面板并支持 SSH alias 多服务器巡检 接入硬件状态、服务状态、HTTP 探测和生产巡检状态展示 增加受控 systemd 启动关闭重启操作和中文字体注入 补充本地服务器面板技术方案与团队共享记忆
This commit is contained in:
128
server-rs/crates/server-manager-panel/src/fonts.rs
Normal file
128
server-rs/crates/server-manager-panel/src/fonts.rs
Normal file
@@ -0,0 +1,128 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::sync::Arc;
|
||||
|
||||
use eframe::egui::{FontData, FontDefinitions, FontFamily};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CjkFontCandidate {
|
||||
pub path: PathBuf,
|
||||
pub index: u32,
|
||||
}
|
||||
|
||||
pub fn install_cjk_font(ctx: &eframe::egui::Context) -> Option<CjkFontCandidate> {
|
||||
let candidate = find_cjk_font_candidate()?;
|
||||
let bytes = std::fs::read(&candidate.path).ok()?;
|
||||
let mut font_data = FontData::from_owned(bytes);
|
||||
font_data.index = candidate.index;
|
||||
|
||||
let mut definitions = FontDefinitions::default();
|
||||
definitions
|
||||
.font_data
|
||||
.insert("genarrative-cjk".to_owned(), Arc::new(font_data));
|
||||
|
||||
// 中文注释:作为 fallback 注入,保留 egui 默认拉丁/图标字体,同时补齐中文 glyph。
|
||||
for family in [FontFamily::Proportional, FontFamily::Monospace] {
|
||||
definitions
|
||||
.families
|
||||
.entry(family)
|
||||
.or_default()
|
||||
.push("genarrative-cjk".to_owned());
|
||||
}
|
||||
|
||||
ctx.set_fonts(definitions);
|
||||
Some(candidate)
|
||||
}
|
||||
|
||||
pub fn find_cjk_font_candidate() -> Option<CjkFontCandidate> {
|
||||
if let Ok(path) = std::env::var("GENARRATIVE_SERVER_PANEL_CJK_FONT") {
|
||||
if let Some(candidate) = parse_font_spec(&path) {
|
||||
return Some(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
const KNOWN_PATHS: &[(&str, u32)] = &[
|
||||
("/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", 2),
|
||||
("/usr/share/fonts/opentype/noto/NotoSansCJK-Medium.ttc", 2),
|
||||
("/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", 0),
|
||||
("/usr/share/fonts/truetype/wqy/wqy-microhei.ttc", 0),
|
||||
(
|
||||
"/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf",
|
||||
0,
|
||||
),
|
||||
(
|
||||
"/home/dsk/.local/share/fonts/genarrative-cjk/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc",
|
||||
0,
|
||||
),
|
||||
];
|
||||
|
||||
for (path, index) in KNOWN_PATHS {
|
||||
if Path::new(path).is_file() {
|
||||
return Some(CjkFontCandidate {
|
||||
path: PathBuf::from(path),
|
||||
index: *index,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for family in [
|
||||
"Noto Sans CJK SC",
|
||||
"WenQuanYi Zen Hei",
|
||||
"Droid Sans Fallback",
|
||||
] {
|
||||
if let Some(candidate) = find_with_fc_match(family) {
|
||||
return Some(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn parse_font_spec(raw: &str) -> Option<CjkFontCandidate> {
|
||||
let trimmed = raw.trim();
|
||||
if trimmed.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let (path, index) = trimmed
|
||||
.rsplit_once('|')
|
||||
.and_then(|(path, index)| Some((path, index.parse().ok()?)))
|
||||
.unwrap_or((trimmed, 0));
|
||||
let path = PathBuf::from(path);
|
||||
path.is_file().then_some(CjkFontCandidate { path, index })
|
||||
}
|
||||
|
||||
fn find_with_fc_match(family: &str) -> Option<CjkFontCandidate> {
|
||||
let output = Command::new("fc-match")
|
||||
.arg("-f")
|
||||
.arg("%{file}|%{index}\n")
|
||||
.arg(family)
|
||||
.output()
|
||||
.ok()?;
|
||||
if !output.status.success() {
|
||||
return None;
|
||||
}
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
stdout.lines().find_map(parse_font_spec)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parses_font_path_with_index() {
|
||||
let candidate = parse_font_spec("/tmp/missing-font.ttc|2");
|
||||
assert_eq!(candidate, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finds_existing_system_cjk_font() {
|
||||
let candidate = find_cjk_font_candidate();
|
||||
assert!(
|
||||
candidate
|
||||
.as_ref()
|
||||
.is_some_and(|candidate| candidate.path.is_file()),
|
||||
"expected at least one CJK font on this development host"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user