桌面 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:
1 parent
6452026fd6
commit
2bb4ef7a95
299 files changed
+6059
-3322
No files matched your search
@@ -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));
|
||||
}
|
||||
}} />
|
||||
</>;
|
||||
}
|
||||
Reference in new issue
Block a user