桌面 GUI 外链修复 + 主题顺序调整(含结构扁平化进行中的工作区基线)

- 集成 tauri-plugin-opener:Cargo/lib.rs/capabilities/npm 四处接线,新增 src/openExternal.ts(桌面走 opener,浏览器回退 window.open,相对路径绝对化)
- 关于页 4 个链接、活动栏官网菜单、代理错误横幅 2 处健康检查链接全部改走 openExternal,桌面端可正常跳转
- 设置→外观 主题顺序调整为 深色/浅色/跟随系统
- release 二进制重编并验证(浏览器实测关于页链接可开文档页)
- 附带工作区中未提交的扁平化重构现状(另一会话进行中)
- homepage 落地页与文档终检通过(8787 / 与 /docs 双 200)
This commit is contained in:
logan committed 2026-10-16 03:03:36 +08:00
1 parent 6452026fd6
commit 2bb4ef7a95
299 files changed
+6059 -3322

No files matched your search

+74
View File
@@ -0,0 +1,74 @@
import { useEffect, useRef, useState, type ReactNode } from "react";
const DEFAULT_WIDTH = 256;
const MIN_WIDTH = 200;
const MAX_WIDTH = 420;
const MAIN_MIN_WIDTH = 660;
const STORAGE_KEY = "opsxapi.sidebar.width";
function savedWidth() {
try {
const value = Number(localStorage.getItem(STORAGE_KEY));
return Number.isFinite(value) && value >= MIN_WIDTH && value <= MAX_WIDTH ? value : DEFAULT_WIDTH;
} catch { return DEFAULT_WIDTH; }
}
/** All workspace views share the same sidebar width and resize affordance. */
export function WorkspaceSidebar({ label, className = "", children }: { label: string; className?: string; children: ReactNode }) {
const divider = useRef<HTMLDivElement>(null);
const [width, setWidth] = useState(savedWidth);
const [available, setAvailable] = useState(Infinity);
const visibleWidth = Math.min(width, Math.max(MIN_WIDTH, available - MAIN_MIN_WIDTH - 6));
useEffect(() => {
const parent = divider.current?.parentElement;
if (!parent) return;
const observer = new ResizeObserver(() => setAvailable(parent.clientWidth));
observer.observe(parent);
setAvailable(parent.clientWidth);
return () => {
observer.disconnect();
document.body.style.cursor = "";
document.body.style.userSelect = "";
};
}, []);
const setAndSave = (value: number) => {
const next = Math.round(Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, available - MAIN_MIN_WIDTH - 6, value)));
setWidth(next);
try { localStorage.setItem(STORAGE_KEY, String(next)); } catch { /* optional preference */ }
};
const resize = (clientX: number) => {
const parent = divider.current?.parentElement;
if (parent) setAndSave(clientX - parent.getBoundingClientRect().left);
};
const stop = (element: HTMLElement, pointerId: number) => {
if (element.hasPointerCapture(pointerId)) element.releasePointerCapture(pointerId);
document.body.style.cursor = "";
document.body.style.userSelect = "";
};
return <>
<aside className={`ops-panel flex min-w-0 shrink-0 flex-col ${className}`} style={{ width: visibleWidth }}>{children}</aside>
<div ref={divider} role="separator" tabIndex={0} aria-label={`调整${label}侧栏宽度`}
aria-orientation="vertical" aria-valuemin={MIN_WIDTH}
aria-valuemax={Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, available - MAIN_MIN_WIDTH - 6))}
aria-valuenow={visibleWidth} title="左右拖动调整侧栏;双击恢复默认宽度"
className="sidebar-resizer"
onPointerDown={(event) => {
event.currentTarget.setPointerCapture(event.pointerId);
document.body.style.cursor = "col-resize";
document.body.style.userSelect = "none";
}}
onPointerMove={(event) => { if (event.currentTarget.hasPointerCapture(event.pointerId)) resize(event.clientX); }}
onPointerUp={(event) => stop(event.currentTarget, event.pointerId)}
onPointerCancel={(event) => stop(event.currentTarget, event.pointerId)}
onDoubleClick={() => setAndSave(DEFAULT_WIDTH)}
onKeyDown={(event) => {
if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
event.preventDefault();
setAndSave(visibleWidth + (event.key === "ArrowRight" ? 16 : -16));
}
}} />
</>;
}