Files
ttstd-infinite-canvas/web/src/lib/canvas/tool-registry/tool-persistence.ts
爱笑的毛毛虫 0c4c258a52 feat(canvas): 工具栏统一化 — 注册表驱动+上下文感知+自定义排序+危险隔离 #95 (#105)
将三个硬编码工具栏(主工具栏/多选工具栏/节点悬停工具栏)统一改造为注册表驱动体系:

- 新增 tool-registry 核心基础设施(ToolDefinition/ToolContext/ToolbarHandlers 类型、
  注册表解析器 resolveToolbarEntries/resolveToolbarTools、偏好持久化 readToolbarPrefs/persistToolbarPrefs)
- 提取四个工具定义文件(main/selection/node-hover/add-node-menu),通过 applicable 谓词实现上下文感知
- FloatingDock 新增 expands 字段(aria-expanded 语义修正)和 danger-group 分组渲染(危险操作视觉隔离)
- 主工具栏新增工具栏设置面板(ToolbarSettingsModal),支持拖拽排序+显隐控制(motion/react Reorder,零新增依赖)
- 偏好持久化到 scopedLocalStorage,按工具栏独立存储

Co-authored-by: sikongyue <3155157671@qq.com>
2026-08-03 23:22:44 +08:00

42 lines
1.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { scopedLocalStorage } from "@/lib/user-scope";
import type { ToolbarId, ToolbarPrefs } from "./tool-definition";
const STORAGE_VERSION = "v1";
const storageKey = (toolbar: ToolbarId) => `canvas-toolbar-prefs-${toolbar}-${STORAGE_VERSION}`;
/**
* 读取工具栏偏好。缺失或解析失败时返回 null(由调用方决定是否用默认值)
*/
export function readToolbarPrefs(toolbar: ToolbarId): ToolbarPrefs | null {
try {
const stored = scopedLocalStorage.getItem(storageKey(toolbar));
if (!stored) return null;
const parsed = JSON.parse(stored) as unknown;
if (!isPrefsShape(parsed)) return null;
return { order: Array.isArray(parsed.order) ? parsed.order.filter((id) => typeof id === "string") : [], hidden: Array.isArray(parsed.hidden) ? parsed.hidden.filter((id) => typeof id === "string") : [] };
} catch {
return null;
}
}
export function persistToolbarPrefs(toolbar: ToolbarId, prefs: ToolbarPrefs) {
try {
scopedLocalStorage.setItem(storageKey(toolbar), JSON.stringify(prefs));
} catch {
// 浏览器禁用本地存储时保留当前会话内的选择
}
}
export function clearToolbarPrefs(toolbar: ToolbarId) {
try {
scopedLocalStorage.removeItem(storageKey(toolbar));
} catch {
// 忽略
}
}
function isPrefsShape(value: unknown): value is Partial<ToolbarPrefs> {
return typeof value === "object" && value !== null;
}