feat(web-platform): release V0.9.3 全模块界面重构
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { FloatingLayerContext, useFloatingLayer } from '../../components/ui/floating';
|
||||
import './monacoSetup';
|
||||
import Editor from '@monaco-editor/react';
|
||||
import {
|
||||
@@ -42,6 +43,7 @@ export function SourceEditorDialog({
|
||||
const [code, setCode] = useState(sourceCode),
|
||||
[savedCode, setSavedCode] = useState(sourceCode),
|
||||
[saving, setSaving] = useState(false),
|
||||
[saveError, setSaveError] = useState<string>(),
|
||||
[copied, setCopied] = useState(false),
|
||||
[maximized, setMaximized] = useState(false),
|
||||
[discardOpen, setDiscardOpen] = useState(false),
|
||||
@@ -61,13 +63,17 @@ export function SourceEditorDialog({
|
||||
const save = useCallback(async () => {
|
||||
if (!dirty || problem) return;
|
||||
setSaving(true);
|
||||
setSaveError(undefined);
|
||||
try {
|
||||
await onSave(filePath, code);
|
||||
setSavedCode(code);
|
||||
} catch (value) {
|
||||
setSaveError(value instanceof Error ? value.message : String(value));
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}, [code, dirty, filePath, onSave, problem]);
|
||||
const layer = useFloatingLayer({ open, roots: () => [dialog.current], dismiss: requestClose });
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
previousFocus.current =
|
||||
@@ -80,7 +86,7 @@ export function SourceEditorDialog({
|
||||
}, [open]);
|
||||
useEffect(() => {
|
||||
const key = (event: KeyboardEvent) => {
|
||||
if (discardOpen) return;
|
||||
if (!open || discardOpen || !dialog.current?.contains(document.activeElement)) return;
|
||||
if (
|
||||
(event.ctrlKey || event.metaKey) &&
|
||||
event.key.toLowerCase() === 's' &&
|
||||
@@ -89,14 +95,11 @@ export function SourceEditorDialog({
|
||||
) {
|
||||
event.preventDefault();
|
||||
void save();
|
||||
} else if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
requestClose();
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', key);
|
||||
return () => window.removeEventListener('keydown', key);
|
||||
}, [dirty, discardOpen, problem, requestClose, save]);
|
||||
}, [open, dirty, discardOpen, problem, requestClose, save]);
|
||||
const copy = async () => {
|
||||
await navigator.clipboard.writeText(code);
|
||||
setCopied(true);
|
||||
@@ -124,7 +127,7 @@ export function SourceEditorDialog({
|
||||
};
|
||||
if (!open) return null;
|
||||
return (
|
||||
<>
|
||||
<FloatingLayerContext.Provider value={layer.context}>
|
||||
<div className="fixed inset-0 z-[390] pointer-events-none" role="presentation">
|
||||
<section
|
||||
ref={dialog}
|
||||
@@ -133,12 +136,20 @@ export function SourceEditorDialog({
|
||||
aria-modal="false"
|
||||
aria-label="转换后的 MJCF 编辑器"
|
||||
style={
|
||||
maximized ? undefined : { left: position.x, top: position.y, width: 900, height: 650 }
|
||||
maximized
|
||||
? { zIndex: layer.zIndex }
|
||||
: {
|
||||
zIndex: layer.zIndex,
|
||||
left: `clamp(16px, ${position.x}px, max(16px, calc(100vw - 916px)))`,
|
||||
top: `clamp(16px, ${position.y}px, max(16px, calc(100vh - 666px)))`,
|
||||
width: 'min(900px, calc(100vw - 32px))',
|
||||
height: 'min(650px, calc(100vh - 32px))',
|
||||
}
|
||||
}
|
||||
className={`source-editor-window pointer-events-auto fixed flex min-h-[360px] min-w-[520px] flex-col overflow-hidden border border-border-strong bg-panel shadow-2xl ${maximized ? 'inset-0 h-full w-full' : 'resize'}`}
|
||||
className={`source-editor-window pointer-events-auto fixed flex min-h-64 min-w-0 max-w-[calc(100vw-32px)] max-h-[calc(100vh-32px)] flex-col overflow-hidden border border-border-strong bg-panel shadow-2xl ${maximized ? 'inset-0 h-full w-full' : 'resize'}`}
|
||||
>
|
||||
<header
|
||||
className="flex h-11 shrink-0 cursor-move select-none items-center gap-3 border-b border-border bg-surface px-3"
|
||||
className="flex min-h-11 flex-wrap shrink-0 py-2 cursor-move select-none items-center gap-3 border-b border-border bg-surface px-3"
|
||||
onPointerDown={pointerDown}
|
||||
onPointerMove={pointerMove}
|
||||
onPointerUp={() => {
|
||||
@@ -151,16 +162,16 @@ export function SourceEditorDialog({
|
||||
<div className="truncate font-mono text-xs font-semibold text-text-primary">
|
||||
转换后的 MJCF
|
||||
</div>
|
||||
<div className="truncate font-mono text-[9px] text-text-tertiary" title={filePath}>
|
||||
<div className="break-all font-mono text-xs text-text-tertiary" title={filePath}>
|
||||
{filePath}
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-[10px] text-text-tertiary">{contentSize(code)}</span>
|
||||
<span className="rounded bg-accent-soft px-1.5 py-0.5 text-[9px] font-semibold text-accent">
|
||||
<span className="text-xs text-text-tertiary">{contentSize(code)}</span>
|
||||
<span className="rounded bg-accent-soft px-1.5 py-0.5 text-xs font-semibold text-accent">
|
||||
缓存文件 · 可编辑
|
||||
</span>
|
||||
{dirty && (
|
||||
<span className="rounded bg-warning-soft px-1.5 py-0.5 text-[9px] font-semibold text-warning">
|
||||
<span className="rounded bg-warning-soft px-1.5 py-0.5 text-xs font-semibold text-warning">
|
||||
已修改
|
||||
</span>
|
||||
)}
|
||||
@@ -193,6 +204,14 @@ export function SourceEditorDialog({
|
||||
<X className="h-4 w-4" />
|
||||
</IconButton>
|
||||
</header>
|
||||
<p className="border-b border-border px-3 py-2 text-xs text-warning">
|
||||
保存将重新编译并载入模型;未保存的源码关闭后无法恢复。
|
||||
</p>
|
||||
{saveError && (
|
||||
<p role="alert" className="px-3 py-2 text-xs text-danger">
|
||||
{saveError}
|
||||
</p>
|
||||
)}
|
||||
<div className="min-h-0 flex-1 bg-input">
|
||||
<Editor
|
||||
height="100%"
|
||||
@@ -218,8 +237,8 @@ export function SourceEditorDialog({
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<footer className="flex h-7 shrink-0 items-center justify-between gap-3 border-t border-border bg-surface px-3 text-[10px]">
|
||||
<div className={problem ? 'truncate text-warning' : 'text-success'}>
|
||||
<footer className="flex min-h-7 flex-wrap shrink-0 items-center justify-between gap-3 border-t border-border bg-surface px-3 text-xs">
|
||||
<div className={problem ? 'break-words text-warning' : 'text-success'}>
|
||||
{problem ? `XML 错误:${problem}` : '✓ XML 结构正常'}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 font-mono text-text-tertiary">
|
||||
@@ -243,6 +262,6 @@ export function SourceEditorDialog({
|
||||
当前 MJCF 源码包含未保存的修改。关闭后,这些修改将无法恢复。
|
||||
</p>
|
||||
</ConfirmDialog>
|
||||
</>
|
||||
</FloatingLayerContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user