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:
2026-06-29 18:41:07 +08:00
parent e0d91335c7
commit 337d6c1205
187 changed files with 5252 additions and 1702 deletions

View 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 };
}