- 产品目标:基于节点画布进行图片上传、AI 生成/编辑、工具处理(裁剪/标注/分镜)。
- 前端:React + TypeScript + Zustand + @xyflow/react + TailwindCSS。
- 后端:Tauri 2 + Rust(命令式接口)+ SQLite(rusqlite,WAL)。
- 关键原则:解耦、可扩展、可回归验证、自动持久化、交互性能优先。
建议按以下顺序理解项目:
- 入口与全局状态
src/App.tsxsrc/stores/projectStore.tssrc/stores/canvasStore.ts
- 画布主流程
src/features/canvas/Canvas.tsxsrc/features/canvas/domain/canvasNodes.tssrc/features/canvas/domain/nodeRegistry.tssrc/features/canvas/NodeSelectionMenu.tsx
- 节点与覆盖层
src/features/canvas/nodes/*.tsxsrc/features/canvas/nodes/ImageEditNode.tsxsrc/features/canvas/nodes/GroupNode.tsxsrc/features/canvas/ui/SelectedNodeOverlay.tsxsrc/features/canvas/ui/NodeActionToolbar.tsxsrc/features/canvas/ui/NodeToolDialog.tsxsrc/features/canvas/ui/nodeControlStyles.tssrc/features/canvas/ui/nodeToolbarConfig.ts
- 工具体系(重点)
src/features/canvas/tools/types.tssrc/features/canvas/tools/builtInTools.tssrc/features/canvas/ui/tool-editors/*src/features/canvas/application/toolProcessor.ts
- 模型与供应商适配
src/features/canvas/models/types.tssrc/features/canvas/models/registry.tssrc/features/canvas/models/image/*src/features/canvas/models/providers/*
- Tauri 命令与持久化
src/commands/*.tssrc/commands/projectState.tssrc-tauri/src/commands/*.rssrc-tauri/src/commands/project_state.rssrc-tauri/src/lib.rs
- 明确变更范围
- 先界定是 UI 变更、节点行为变更、工具逻辑变更、模型适配变更,还是持久化/性能变更。
- 沿着数据流改动
- UI 输入 -> Store -> 应用服务 -> 基础设施(命令/API)-> 持久化。
- 禁止跨层“偷改”状态;尽量只在对应层处理对应职责。
- 小步提交与即时验证
- 每次改动后做轻量检查(见第 6 节),通过后再继续。
- 最后做一次完整构建
- 在功能收尾或大改合并前运行完整构建。
- 发布快捷口令
- 当用户明确说“推送更新”时,默认执行一次补丁版本发布:基于上一个 release/tag 自动递增 patch 版本号,汇总代码变动生成 Markdown 更新日志,完成版本同步、发布提交、annotated tag 与远端推送;如用户额外指定 minor/major 或自定义说明,则按用户要求覆盖默认行为。
- 自动生成的更新日志正文只保留
## 新增、## 优化、## 修复等二级标题分组与对应列表项;不要额外输出# vx.y.z标题、基于某个 tag 之后的若干提交整理说明或## 完整提交区块,空分组可省略。
- 模块间优先依赖接口/类型,不直接依赖具体实现细节。
- 跨模块通信优先使用事件总线或明确的 service/port。
- 展示层(UI)不直接耦合基础设施层(Tauri/API 调用);通过应用层中转。
- 一个文件只做一个业务概念;无法用三句话说清职责就应拆分。
- 工具 UI、工具数据结构、工具执行逻辑应分离(已采用:editor / annotation codec / processor)。
- 舒适区:类 <= 400 行,脚本 <= 300 行。
- 警戒线:800 行,必须评估拆分。
- 强制拆分:1000 行(纯数据定义除外)。
- 使用 DTO/纯数据对象,避免双向引用。
- Store 不应直接承担重业务逻辑;业务逻辑放应用层。
- 本文档定位为“技术开发规范文档”,优先记录稳定的架构约束、分层规则、扩展流程、验证标准。
- 不记录易变的具体 UI 操作步骤、临时交互文案或产品走查细节(这些应放在需求文档/设计稿/任务说明中)。
- 当实现变化仅影响交互细节而不影响技术约束时,可不更新本文档。
- 节点类型、默认数据、菜单展示、连线能力统一在
domain/nodeRegistry.ts声明,不在Canvas.tsx/canvasStore.ts重复硬编码。 connectivity为连线能力配置源:sourceHandle/targetHandle:是否具备输入输出端口。connectMenu.fromSource/connectMenu.fromTarget:从输出端或输入端拉线时,是否允许出现在“创建节点菜单”。
- 菜单候选节点必须由注册表函数统一推导(如
getConnectMenuNodeTypes),禁止在 UI 层手写类型白名单。 - 内部衍生节点(如切割结果
storyboardSplit、导出节点)默认connectMenu关闭,只能由应用流程自动创建。
- 复用统一 UI 组件:
src/components/ui/primitives.tsx。 - 风格统一使用设计变量和 token(
index.css),避免散落硬编码样式。 - 输入框、工具条、弹窗保持与节点对齐,交互动画保持一致。
- 节点底部控制条(模型/比例/生成/导出等)尺寸样式统一从
src/features/canvas/ui/nodeControlStyles.ts引用,禁止在各节点散落硬编码一套新尺寸。 - 节点工具条(NodeToolbar)位置、对齐、偏移统一从
src/features/canvas/ui/nodeToolbarConfig.ts引用;禁止通过left/translate等绝对定位覆盖跟随逻辑。 - 选中覆盖层
SelectedNodeOverlay只承载轻量通用覆盖能力(如工具条),节点核心业务输入区应内聚到节点组件本体(例如ImageEditNode)。 - 对话框支持“打开/关闭”过渡,避免突兀闪烁。
- 明暗主题要可读,避免高饱和蓝色抢占焦点(导航图已优化为灰黑系)。
- 快捷键应避开输入态(
input/textarea/contentEditable)避免误触。
# 前端开发
npm run dev
# Tauri 联调
npm run tauri dev
# 自动发布(默认建议配合 docs/releases/vx.y.z.md 使用)
npm run release -- patch --notes-file docs/releases/v0.1.12.md# TS 类型检查
npx tsc --noEmit
# Rust 快速检查
cd src-tauri && cargo check# 前端完整构建
npm run build
# 触发一次正式发布(会同步版本、提交、打 tag、推送)
npm run release -- patch --notes-file docs/releases/v0.1.12.md说明:
- 日常迭代不要求每次都完整打包,先走
tsc --noEmit+ 关键路径手测。 - 影响打包、依赖、入口、持久化、Tauri 命令时,再执行完整构建。
- 发布说明优先落到
docs/releases/vx.y.z.md,再通过npm run release或“推送更新”口令触发发布。 docs/releases/vx.y.z.md的默认格式同样只保留二级标题分组和列表正文,不写额外总标题、范围说明和完整提交清单。
- 禁止在拖拽每一帧执行重持久化或重计算。
- 节点拖拽中不要写盘;拖拽结束再保存(项目已按该策略优化)。
- 大图片场景避免重复
dataURL转换;节点渲染优先使用previewImageUrl,模型/工具处理使用原图imageUrl。 - 项目整量持久化(nodes/edges/history)使用防抖 + 空闲调度(idle callback)队列,避免与交互争用主线程。
- viewport 持久化走独立轻量队列与独立命令(
update_project_viewport_record),不要回退到整项目 upsert。 - 视口更新要做归一化与阈值比较(epsilon),过滤微小抖动写入。
- 优先使用
useMemo/useCallback控制重渲染;避免把大对象直接塞进依赖导致抖动。 - 画布交互优先“流畅”而非“实时全量持久化”,可使用短延迟合并保存。
- 一模型一文件,放到
src/features/canvas/models/image/<provider>/。 - 在模型定义中声明:
displayNameproviderId- 支持分辨率/比例
- 默认参数
- 请求映射函数
resolveRequest
- 在
tools/types.ts声明能力(如 editor kind)。 - 在
tools/builtInTools.ts注册插件。 - 在
ui/tool-editors/新增对应编辑器。 - 在
application/toolProcessor.ts接入执行逻辑。 - 保证产物仍走“处理后生成新节点”链路,不覆盖原节点。
- 在
domain/canvasNodes.ts增加类型与数据结构(必要时增加类型守卫)。 - 在
domain/nodeRegistry.ts注册定义:createDefaultData、capabilities、connectivity。 - 在
nodes/index.ts注册渲染组件。 - 明确手动创建策略:
- 可手动创建:配置
connectMenu.fromSource/fromTarget。 - 仅流程创建:关闭
connectMenu,由工具/应用服务触发。
- 可手动创建:配置
- 如新增分组/父子节点行为,必须同步验证删除、解组、连线清理与历史快照。
- 节点内控制条优先复用
nodeControlStyles.ts里的统一尺寸 token;若需特化,基于统一 token 小幅覆盖,不新建一整套尺寸体系。 - 节点工具条必须复用
nodeToolbarConfig.ts,并验证两点:- 拖拽节点时工具条随节点同步移动。
- 多种节点尺寸下工具条仍保持相对居中(不出现固定在画布某处的情况)。
- 项目数据通过
projectStore自动持久化,不要求手动保存。 - 重启默认进入项目页;进入项目时恢复上次 viewport。
- 当前持久化后端为 SQLite,库文件位于 Tauri
app_data_dir/projects.db。 projects表核心字段:nodes_json、edges_json、viewport_json、history_json、node_count。- 前端持久化采用双通道:
- 整项目快照:
upsert_project_record(防抖 + idle 调度)。 - 视口快照:
update_project_viewport_record(轻量更新、独立防抖)。
- 整项目快照:
- 图片字段通过
imagePool + __img_ref__做去重编码;新增图片字段(如previewImageUrl)需同步编码/解码映射。 - 变更 SQLite 表结构时:
- 必须在
ensure_projects_table中做自愈(PRAGMA table_info+ALTER TABLE)。 - 开发阶段可不兼容旧的临时草稿格式,但不能破坏当前
projects.db的基本可读性。
- 必须在
- 功能路径可用(至少手测 1 条主路径 + 1 条异常路径)。
- 无明显性能回退(拖拽、缩放、输入响应)。
- 轻量检查通过:
npx tsc --noEmit,Rust 改动则cargo check。 - 大改或发布前:
npm run build。 - 如为正式发布,确认
docs/releases/vx.y.z.md已更新,并与本次 tag/版本号一致。 - 新增约束/行为变化需同步更新文档。
- i18n 入口:
src/i18n/index.ts - 语言文件:
src/i18n/locales/zh.json、src/i18n/locales/en.json - 组件中统一使用
useTranslation()+t('key.path'),避免硬编码中英文文案。
- 使用模块化层级命名:
project.title、node.menu.uploadImage、common.save。 - 避免把中文句子直接作为 key;key 必须稳定、可复用、可检索。
- 通用文案优先放
common.*,页面专属文案放对应模块前缀。
- 先在
zh.json增加新 key。 - 同步在
en.json增加相同 key(不要缺语言键)。 - 代码里只引用 key,不写 fallback 字面量。
- 动态值用插值:
t('xxx', { count, name })。 - 数量相关场景使用 i18next 复数规则,不手写字符串拼接。
- 数字/时间等先格式化,再传给
t。
- 切换中英文后,不出现未翻译 key 泄露(例如直接显示
project.title)。 - 新增 key 在中英语言包均存在。
- 关键按钮、提示、错误文案在两种语言下都可读不截断。
如与用户明确要求冲突,以用户要求优先;如与运行时安全冲突,以安全优先。