feat: 构建系统重构 + VCHSM 架构迁移 + 右键菜单修复 + 尺寸参数统一
## 构建、打包与分发系统重构 (build/) - 构建系统收敛至 build/ 目录:build.mjs + electron-builder.js + 6 个脚本 - electron-builder 配置从 package.json 提取为 build/electron-builder.js - 新增 --edition (personal/enterprise) 和 --channel (stable/beta/alpha) 参数 - OSS 路径按渠道隔离 - 新增 pre-build-check.mjs:Git/CHANGELOG/版本/环境变量校验 - 新增 bump-version.mjs:版本号管理 + CHANGELOG 自动生成 - updater.ts 发送 channel/edition/deviceFingerprint - 新增 src/shared/utils/rollout.ts 灰度测试工具 - 新增 10 条 NPM 脚本 ## VCHSM 五层架构迁移 - 7 个业务模块 + ~500 处导入路径同步 ## 修复 - MediaCardContextMenu 改用共享 ContextMenu 组件 - ComboBox 尺寸参数显示统一 (size-format.ts) ## 文档 - UpdateA.md / build/README.md / README.md / .env.example 更新 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
59
src/modules/canvas/hooks/useSidecarSync.ts
Normal file
59
src/modules/canvas/hooks/useSidecarSync.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
// ============================================================
|
||||
// useSidecarSync — Sidecar 元数据防抖同步 Hook
|
||||
//
|
||||
// QuickEditPanel 编辑 → 250ms 防抖 → dataSource.writeSidecar() → 保存到 JSON。
|
||||
// Phase 4 扩展:写入后通过事件总线通知宿主窗口刷新。
|
||||
// ============================================================
|
||||
|
||||
import { useRef, useCallback } from 'react';
|
||||
import { useCanvasStore, getCanvasStore } from '../stores/useCanvasStore';
|
||||
import type { SidecarMetadata } from '../utils/ICanvasDataSource';
|
||||
|
||||
export function useSidecarSync() {
|
||||
const updateItemMetadata = useCanvasStore((s) => s.updateItemMetadata);
|
||||
const getDataSource = useCanvasStore((s) => s.getDataSource);
|
||||
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const pendingRef = useRef<Map<number, SidecarMetadata>>(new Map());
|
||||
|
||||
/**
|
||||
* 提交元数据更新(250ms 防抖)。
|
||||
* 更新 Store → 延迟写入 sidecar JSON(失败时保留 Store 中的副本)。
|
||||
*/
|
||||
const commitMetadata = useCallback(
|
||||
(itemId: number, filePath: string, partial: Partial<SidecarMetadata>) => {
|
||||
const current =
|
||||
pendingRef.current.get(itemId) ||
|
||||
({} as SidecarMetadata);
|
||||
const merged = { ...current, ...partial };
|
||||
pendingRef.current.set(itemId, merged);
|
||||
|
||||
// 立即更新 Store(乐观更新)
|
||||
updateItemMetadata(itemId, partial);
|
||||
|
||||
// 防抖写入 sidecar
|
||||
if (timerRef.current) clearTimeout(timerRef.current);
|
||||
timerRef.current = setTimeout(async () => {
|
||||
const batch = new Map(pendingRef.current);
|
||||
pendingRef.current.clear();
|
||||
|
||||
const dataSource = getDataSource();
|
||||
const store = getCanvasStore();
|
||||
for (const [id, meta] of batch) {
|
||||
try {
|
||||
const item = store.getState().items.find(
|
||||
(i) => i.id === id,
|
||||
);
|
||||
if (item) {
|
||||
await dataSource.writeSidecar(item.path, meta);
|
||||
}
|
||||
} catch {
|
||||
console.warn(`[useSidecarSync] 写入 sidecar 失败: ${filePath}`);
|
||||
}
|
||||
}
|
||||
}, 250);
|
||||
},
|
||||
[updateItemMetadata, getDataSource],
|
||||
);
|
||||
|
||||
return { commitMetadata };
|
||||
}
|
||||
Reference in New Issue
Block a user