feat: 改造云智能体桌面编辑与试用工作区
This commit is contained in:
1 parent
6b92591129
commit
dca6deab29
16 files changed
+623
-222
No files matched your search
@@ -0,0 +1,58 @@
|
||||
import { useRef, useState, type ReactNode } from 'react';
|
||||
import { Columns2, Maximize2 } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import './desktop-workspace.css';
|
||||
|
||||
const preferenceKey = 'makelore.cloud-agent-editor-ratio';
|
||||
|
||||
export function DesktopSplit({ configuration, preview }: { configuration: ReactNode; preview: (action: ReactNode) => ReactNode }) {
|
||||
const root = useRef<HTMLDivElement>(null);
|
||||
const [ratio, setRatio] = useState(() => {
|
||||
try { const saved = Number(localStorage.getItem(preferenceKey)); return saved >= 25 && saved <= 75 ? saved : 46; }
|
||||
catch { return 46; }
|
||||
});
|
||||
const [focus, setFocus] = useState<'configuration' | 'preview' | null>(null);
|
||||
const [compactPanel, setCompactPanel] = useState<'configuration' | 'preview'>('configuration');
|
||||
const resize = (next: number) => {
|
||||
const width = (root.current?.clientWidth ?? 0) - 8;
|
||||
if (width < 720) return;
|
||||
const clamped = Math.max(25, 340 / width * 100, Math.min(75, 100 - 380 / width * 100, next));
|
||||
setRatio(clamped);
|
||||
try { localStorage.setItem(preferenceKey, String(clamped)); } catch { /* Layout remains usable without persistence. */ }
|
||||
};
|
||||
const panelToggle = (panel: 'configuration' | 'preview') => <Button variant="ghost" size="icon" className="h-10 w-10"
|
||||
aria-label={focus === panel ? '恢复分栏' : panel === 'configuration' ? '展开配置' : '展开试用'}
|
||||
onClick={() => setFocus(focus === panel ? null : panel)}>{focus === panel ? <Columns2 className="h-4 w-4" /> : <Maximize2 className="h-4 w-4" />}</Button>;
|
||||
return <div className="agent-split-container" onKeyDown={e => {
|
||||
if (e.key === 'Escape') setFocus(null);
|
||||
if (e.key === 'F6' && root.current) {
|
||||
const panels = [...root.current.querySelectorAll<HTMLElement>('.agent-config-pane, .agent-preview-pane')].filter(panel => panel.offsetParent !== null);
|
||||
const current = panels.findIndex(panel => panel.contains(document.activeElement));
|
||||
const next = panels[(current + 1) % panels.length];
|
||||
const target = [...(next?.querySelectorAll<HTMLElement>('textarea:not(:disabled), input:not(:disabled), button:not(:disabled)') ?? [])].find(el => el.offsetParent !== null);
|
||||
if (target) { e.preventDefault(); target.focus(); }
|
||||
}
|
||||
}}>
|
||||
<div className="agent-compact-switch" aria-label="工作区面板">
|
||||
<Button variant={compactPanel === 'configuration' ? 'secondary' : 'ghost'} onClick={() => { setFocus(null); setCompactPanel('configuration'); }}>配置</Button>
|
||||
<Button variant={compactPanel === 'preview' ? 'secondary' : 'ghost'} onClick={() => { setFocus(null); setCompactPanel('preview'); }}>试用</Button>
|
||||
</div>
|
||||
<div ref={root} className="agent-split" data-focus={focus ?? ''} data-compact-panel={compactPanel}
|
||||
style={{ gridTemplateColumns: focus ? 'minmax(0, 1fr)' : `minmax(340px, ${ratio}fr) 8px minmax(380px, ${100 - ratio}fr)` }}>
|
||||
<section className="agent-config-pane" aria-label="草稿配置">
|
||||
<div className="agent-pane-heading"><span>配置智能体</span>{panelToggle('configuration')}</div>
|
||||
{configuration}
|
||||
</section>
|
||||
<div role="separator" aria-label="调整配置与试用宽度" aria-orientation="vertical" tabIndex={0}
|
||||
aria-valuemin={25} aria-valuemax={75} aria-valuenow={Math.round(ratio)} className="agent-divider"
|
||||
onDoubleClick={() => resize(46)}
|
||||
onPointerDown={e => { e.preventDefault(); e.currentTarget.focus(); e.currentTarget.setPointerCapture(e.pointerId); }}
|
||||
onPointerMove={e => { if (e.currentTarget.hasPointerCapture(e.pointerId) && root.current) resize((e.clientX - root.current.getBoundingClientRect().left) / (root.current.clientWidth - 8) * 100); }}
|
||||
onPointerUp={e => e.currentTarget.releasePointerCapture(e.pointerId)}
|
||||
onKeyDown={e => { if (['ArrowLeft', 'ArrowRight', 'Home'].includes(e.key)) { e.preventDefault(); resize(e.key === 'Home' ? 46 : ratio + (e.key === 'ArrowLeft' ? -2 : 2)); } }} />
|
||||
<section className="agent-preview-pane" aria-label="试用工作区">
|
||||
{preview(panelToggle('preview'))}
|
||||
</section>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
Reference in new issue
Block a user