feat(canvas): 沉浸专注与短剧流程 - 支持专注模式与流程条并入顶栏体系 (#157)
- 沉浸专注:进入后隐藏顶部栏、Dock、缩放控件、小地图与素材托盘,保留专注胶囊可唤出工具与智能体;小屏默认进入,⇧⌘F 切换,Esc 在无选中与浮层时退出 - 智能体面板改贴边停靠并按顶栏让位,作为沉浸专注的配套依赖 - 短剧流程条并入顶栏:折叠入口移入顶栏按钮组,展开条锚定顶栏下缘,与工作模式开关互斥 - 流程步骤改为里程碑节点与状态轨道,新增 --flow-step-* Design Token Co-authored-by: sikongyue <3155157671@qq.com>
This commit is contained in:
@@ -1,45 +1,45 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
const FOCUS_MODE_KEY = "canvas-focus-mode-v1";
|
||||
const FORCED_FOCUS_BREAKPOINT = 1024;
|
||||
const FOCUS_MODE_KEY = "canvas-focus-mode-v2";
|
||||
const SMALL_SCREEN_BREAKPOINT = 1024;
|
||||
|
||||
// 默认策略:小屏自动沉浸(不可在顶栏手动切换),宽屏由用户偏好决定。
|
||||
// 用户手动切换后以持久化偏好为准,窗口缩放不再覆盖用户选择。
|
||||
function readInitialPreference(): boolean {
|
||||
if (typeof window === "undefined") return true;
|
||||
const stored = window.localStorage.getItem(FOCUS_MODE_KEY);
|
||||
if (stored === "1") return true;
|
||||
if (stored === "0") return false;
|
||||
return true;
|
||||
if (stored !== null) return stored === "true";
|
||||
return window.innerWidth < SMALL_SCREEN_BREAKPOINT;
|
||||
}
|
||||
|
||||
export function useFocusMode() {
|
||||
const [userPreference, setUserPreference] = useState<boolean>(readInitialPreference);
|
||||
const [viewportWidth, setViewportWidth] = useState<number>(() => (typeof window !== "undefined" ? window.innerWidth : 1024));
|
||||
const [smallScreen, setSmallScreen] = useState<boolean>(() => window.innerWidth < SMALL_SCREEN_BREAKPOINT);
|
||||
|
||||
const focusMode = smallScreen || userPreference;
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined") return;
|
||||
const handleResize = () => setViewportWidth(window.innerWidth);
|
||||
handleResize();
|
||||
const handleResize = () => setSmallScreen(window.innerWidth < SMALL_SCREEN_BREAKPOINT);
|
||||
window.addEventListener("resize", handleResize);
|
||||
return () => window.removeEventListener("resize", handleResize);
|
||||
}, []);
|
||||
|
||||
const forcedOn = viewportWidth < FORCED_FOCUS_BREAKPOINT;
|
||||
const focusMode = forcedOn || userPreference;
|
||||
|
||||
const toggleFocusMode = useCallback(() => {
|
||||
setUserPreference((prev) => {
|
||||
const next = !prev;
|
||||
if (typeof window !== "undefined") {
|
||||
window.localStorage.setItem(FOCUS_MODE_KEY, next ? "1" : "0");
|
||||
}
|
||||
return next;
|
||||
});
|
||||
const persist = useCallback((next: boolean) => {
|
||||
setUserPreference(next);
|
||||
try {
|
||||
window.localStorage.setItem(FOCUS_MODE_KEY, String(next));
|
||||
} catch {
|
||||
// 忽略 localStorage 不可用场景,专注模式仍可在本次会话生效。
|
||||
}
|
||||
}, []);
|
||||
|
||||
const enterFocusMode = useCallback(() => persist(true), [persist]);
|
||||
const exitFocusMode = useCallback(() => persist(false), [persist]);
|
||||
const toggleFocusMode = useCallback(() => persist(!(smallScreen || userPreference)), [persist, smallScreen, userPreference]);
|
||||
|
||||
return {
|
||||
focusMode,
|
||||
userPreference,
|
||||
forcedOn,
|
||||
enterFocusMode,
|
||||
exitFocusMode,
|
||||
toggleFocusMode,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user