feat(canvas): 沉浸专注与短剧流程 - 支持专注模式与流程条并入顶栏体系 (#157)

- 沉浸专注:进入后隐藏顶部栏、Dock、缩放控件、小地图与素材托盘,保留专注胶囊可唤出工具与智能体;小屏默认进入,⇧⌘F 切换,Esc 在无选中与浮层时退出
- 智能体面板改贴边停靠并按顶栏让位,作为沉浸专注的配套依赖
- 短剧流程条并入顶栏:折叠入口移入顶栏按钮组,展开条锚定顶栏下缘,与工作模式开关互斥
- 流程步骤改为里程碑节点与状态轨道,新增 --flow-step-* Design Token

Co-authored-by: sikongyue <3155157671@qq.com>
This commit is contained in:
爱笑的毛毛虫
2026-08-06 08:32:38 +08:00
committed by GitHub
parent 6671c331e3
commit 971489a336
12 changed files with 519 additions and 255 deletions

View File

@@ -146,12 +146,10 @@ type CanvasAssistantPanelProps = {
onCollapse: () => void;
cinematicEntry?: boolean;
onCinematicEntryConsumed?: () => void;
width: number;
onWidthChange: (width: number) => void;
focusMode: boolean;
resizing?: boolean;
};
export function CanvasAssistantPanel({ nodes, selectedNodeIds, snapshot, projectId, sessions, activeSessionId, onSelectNodeIds, onSessionsChange, onApplyOps, canUndoOps, undoOpsCount, onUndoOps, onPasteImage, agentMode, onAgentModeChange, autoConnectLocal, closing, onCollapse, cinematicEntry = false, onCinematicEntryConsumed, width, onWidthChange, focusMode }: CanvasAssistantPanelProps) {
export function CanvasAssistantPanel({ nodes, selectedNodeIds, snapshot, projectId, sessions, activeSessionId, onSelectNodeIds, onSessionsChange, onApplyOps, canUndoOps, undoOpsCount, onUndoOps, onPasteImage, agentMode, onAgentModeChange, autoConnectLocal, closing, onCollapse, cinematicEntry = false, onCinematicEntryConsumed, resizing = false }: CanvasAssistantPanelProps) {
const theme = canvasThemes[useThemeStore((state) => state.theme)];
const user = useUserStore((state) => state.user);
const effectiveConfig = useEffectiveConfig();
@@ -166,7 +164,6 @@ export function CanvasAssistantPanel({ nodes, selectedNodeIds, snapshot, project
const [isRunning, setIsRunning] = useState(false);
const [deleteChatIds, setDeleteChatIds] = useState<string[]>([]);
const [onlineLogs, setOnlineLogs] = useState<OnlineAgentLog[]>([]);
const [resizing, setResizing] = useState(false);
const [removedReferenceIds, setRemovedReferenceIds] = useState<Set<string>>(new Set());
const [localSessions, setLocalSessions] = useState<CanvasAssistantSession[]>(() => (sessions.length ? sessions : [createSession()]));
const [localActiveSessionId, setLocalActiveSessionId] = useState<string | null>(activeSessionId);
@@ -684,22 +681,6 @@ export function CanvasAssistantPanel({ nodes, selectedNodeIds, snapshot, project
if (file) onPasteImage(file);
};
const startResize = () => {
const move = (event: MouseEvent) => onWidthChange(Math.min(760, Math.max(320, window.innerWidth - event.clientX)));
const stop = () => {
setResizing(false);
document.body.style.cursor = "";
document.body.style.userSelect = "";
document.removeEventListener("mousemove", move);
document.removeEventListener("mouseup", stop);
};
setResizing(true);
document.body.style.cursor = "col-resize";
document.body.style.userSelect = "none";
document.addEventListener("mousemove", move);
document.addEventListener("mouseup", stop);
};
const collapse = () => {
onCollapse();
};
@@ -834,42 +815,20 @@ export function CanvasAssistantPanel({ nodes, selectedNodeIds, snapshot, project
</>
);
// 面板顶部偏移:顶部栏高度 + 间距,活动任务面板为 absolute 定位无需额外预留
const PANEL_SLOT_TOP_OFFSET = 80;
const panelOffsetRight = focusMode ? 0 : 12;
const panelOffsetBottom = focusMode ? 0 : 16;
return (
<motion.div
className="pointer-events-none absolute z-[var(--z-panel-floating)]"
initial={{ opacity: 0 }}
animate={{ opacity: closing ? 0 : 1 }}
<motion.aside
className="pointer-events-auto relative flex h-full w-full flex-col overflow-hidden rounded-[var(--panel-radius)] border"
initial={{ x: 48, opacity: 0 }}
animate={{ x: closing ? 28 : 0, opacity: closing ? 0 : 1 }}
exit={{ opacity: 0 }}
transition={{ duration: resizing ? 0 : PANEL_MOTION_SECONDS, ease: [0.22, 1, 0.36, 1] }}
style={{
top: PANEL_SLOT_TOP_OFFSET,
right: panelOffsetRight,
bottom: panelOffsetBottom,
width: closing ? 0 : width,
overflow: "clip",
pointerEvents: closing ? "none" : undefined,
borderColor: theme.toolbar.border,
background: theme.spatial.elevated,
color: theme.node.text,
boxShadow: `0 24px 72px ${theme.spatial.shadow}`,
}}
>
<motion.aside
className="pointer-events-auto relative flex shrink-0 flex-col overflow-hidden rounded-[var(--panel-radius)] border"
initial={{ x: 48, opacity: 0 }}
animate={{ x: closing ? 28 : 0, opacity: closing ? 0 : 1 }}
transition={{ duration: resizing ? 0 : PANEL_MOTION_SECONDS, ease: [0.22, 1, 0.36, 1] }}
style={{
width,
height: "100%",
borderColor: theme.toolbar.border,
background: theme.spatial.elevated,
color: theme.node.text,
boxShadow: focusMode ? "none" : `0 24px 72px ${theme.spatial.shadow}`,
}}
>
<button type="button" className="absolute inset-y-0 left-0 z-[var(--node-z-overlay)] w-4 -translate-x-1/2 cursor-col-resize" onMouseDown={startResize} aria-label="调整右侧面板宽度" />
<AgentPanelChrome
theme={theme}
mode={agentMode}
@@ -896,8 +855,7 @@ export function CanvasAssistantPanel({ nodes, selectedNodeIds, snapshot, project
) : (
onlineContent
)}
</motion.aside>
</motion.div>
</motion.aside>
);
}