diff --git a/docs/README.md b/docs/README.md index a2501862..78a839aa 100644 --- a/docs/README.md +++ b/docs/README.md @@ -23,6 +23,8 @@ `/editor/agent` 浏览器内 AI Web 工程编辑器的静态 SPA 沙箱预览 MVP,采用“平台编辑器壳 + api-server 控制面 + 独立 runner worker + 独立预览域”四层结构;技术方案、威胁模型和验收清单见 [【技术方案】浏览器内AIWeb工程沙箱预览方案-2026-06-13.md](./technical/【技术方案】浏览器内AIWeb工程沙箱预览方案-2026-06-13.md)、[【安全模型】AIWeb工程Runner与预览隔离威胁模型-2026-06-13.md](./technical/【安全模型】AIWeb工程Runner与预览隔离威胁模型-2026-06-13.md) 和 [【测试用例】AIWeb工程静态预览MVP验收清单-2026-06-13.md](./technical/【测试用例】AIWeb工程静态预览MVP验收清单-2026-06-13.md)。 +`/editor/canvas` 图片画布编辑器的画布素材 ZIP 导出能力,入口放在右上角标题栏下载图标内,第一版采用前端 JSZip 打包画布中有效图层引用的上传图、生成图和修改结果,方案见 [【前端架构】图片画布素材导出方案-2026-06-15.md](./technical/【前端架构】图片画布素材导出方案-2026-06-15.md)。 + 本地通过 SSH alias 管理多台服务器、查看硬件 / systemd / HTTP 健康状态并执行受控服务启停的 egui 桌面工具见 [【开发运维】本地SSH服务器管理面板技术方案-2026-06-11.md](./technical/【开发运维】本地SSH服务器管理面板技术方案-2026-06-11.md)。 生产部署切换到 systemd + Nginx + SpacetimeDB 自托管的总方案见 [PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md](./technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md),该文档也是当前生产 Jenkinsfile 的唯一入口。SpacetimeDB 表结构变更、自动迁移边界和保留旧数据的分阶段迁移流程见 [SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md](./technical/SPACETIMEDB_SCHEMA_CHANGE_CONSTRAINTS.md);private 表迁移 JSON 导入导出、HTTP 413 分片导入和旧数据库迁移流水线经验见 [SPACETIMEDB_JSON_STRING_MIGRATION_PROCEDURE_2026-04-27.md](./technical/SPACETIMEDB_JSON_STRING_MIGRATION_PROCEDURE_2026-04-27.md) 与 [JENKINS_SPACETIMEDB_DATABASE_MIGRATION_PIPELINES_2026-04-29.md](./technical/JENKINS_SPACETIMEDB_DATABASE_MIGRATION_PIPELINES_2026-04-29.md);后台管理独立前端工程技术方案见 [ADMIN_WEB_CONSOLE_TECHNICAL_SOLUTION_2026-04-30.md](./technical/ADMIN_WEB_CONSOLE_TECHNICAL_SOLUTION_2026-04-30.md)。 diff --git a/docs/technical/【前端架构】图片画布素材导出方案-2026-06-15.md b/docs/technical/【前端架构】图片画布素材导出方案-2026-06-15.md new file mode 100644 index 00000000..f3503144 --- /dev/null +++ b/docs/technical/【前端架构】图片画布素材导出方案-2026-06-15.md @@ -0,0 +1,158 @@ +# 图片画布素材导出方案 + +日期:2026-06-15 + +## 背景 + +`/editor/canvas` 已承载项目画布、账号级素材库、生成图片、图层、分组、隐藏、锁定、翻转、缩放和右键菜单等编辑能力。用户需要一次性下载当前画布中使用到的全部素材,用于本地归档、外部编辑或跨工具交接。 + +素材导出应作为画布级能力,而不是单个图层的“导出为”。单图导出仍保留在目标右键菜单;全部素材导出放在画布标题栏,入口稳定、可发现且不打断画布操作。 + +## 入口设计 + +- 在画布右上角标题栏内增加下载图标按钮。 +- 图标使用 `lucide-react` 的 `Download`。 +- 按钮 `aria-label` 使用 `下载画布素材`。 +- 入口与返回项目页、项目名称同属标题栏,但视觉上靠右,不进入左下角画布工具组,也不进入底部 AI 工具栏。 +- 标题栏空间不足时,下载图标保持固定尺寸,项目名称使用省略号收缩。 + +## 第一版范围 + +第一版实现“导出画布素材 ZIP”: + +- 导出当前画布中有效图层引用的图片。 +- 包含上传图、生成图、修改生成结果。 +- 默认包含隐藏图层。 +- 跳过已被素材库删除且已判定无效的上传图层。 +- 锁定、分组、翻转等状态不影响图片文件导出,但写入元数据。 +- 空画布时按钮置灰,或点击后显示轻提示。 + +暂不实现: + +- 画布整体截图 PNG。 +- PSD / Figma / 工程包导出。 +- 后端异步打包任务。 +- 导入导出包恢复画布。 + +## ZIP 结构 + +导出文件名: + +```text +项目名-画布素材-YYYYMMDD.zip +``` + +包内结构: + +```text +项目名-画布素材/ +├─ images/ +│ ├─ 001-拼图素材.png +│ ├─ 002-生成图片.png +│ └─ 003-修改结果.png +├─ metadata.json +└─ manifest.txt +``` + +## 元数据结构 + +`metadata.json` 记录项目、导出时间、图层和图片文件映射: + +```json +{ + "projectId": "editor-project-default", + "projectTitle": "默认项目", + "exportedAt": "2026-06-15T00:00:00.000Z", + "layers": [ + { + "layerId": "layer-generated-1", + "title": "生成图片 1", + "file": "images/002-生成图片.png", + "width": 1024, + "height": 1024, + "canvas": { + "x": 120, + "y": 80, + "width": 420, + "height": 420, + "zIndex": 12, + "hidden": false, + "locked": false, + "flipX": false, + "flipY": false, + "groupId": null + }, + "sourceType": "generated", + "prompt": "一张明亮的拼图主视觉", + "actualPrompt": "一张明亮的拼图主视觉", + "model": "gpt-image-2", + "provider": "VectorEngine", + "taskId": "editor-real-task-1" + } + ] +} +``` + +`manifest.txt` 用于人工快速查看: + +```text +项目:默认项目 +导出时间:2026-06-15T00:00:00.000Z +素材数量:3 +图层数量:5 +``` + +## 去重规则 + +同一张图片被多个图层引用时,只导出一份图片,所有图层在 `metadata.json` 中指向同一个 `file`。 + +图片去重 key 优先级: + +```text +assetObjectId > objectKey > sourceAssetId > src +``` + +文件命名按图层 zIndex 从低到高排序,遇到重复名称追加序号。 + +## 前端实现 + +第一版优先使用客户端 ZIP: + +1. 从当前 `layers` 取目标图层。 +2. 过滤无效图层,保留隐藏图层。 +3. 按去重 key 合并图片源。 +4. 对每个图片源读取 Blob: + - `data:image/...` 直接转换为 Blob。 + - 同源或可访问 URL 使用 `fetch` 拉取 Blob。 + - 拉取失败时记录失败项,不中断整个导出。 +5. 使用 `JSZip` 写入 `images/`、`metadata.json` 和 `manifest.txt`。 +6. `zip.generateAsync({ type: 'blob' })` 生成文件。 +7. 用临时 `` 触发下载。 + +若当前仓库未安装 `jszip`,新增依赖时应只引入 `jszip`,不引入额外下载库。 + +## 错误处理 + +- 空画布:按钮置灰或显示短提示。 +- 部分图片下载失败:ZIP 仍生成,`metadata.json` 记录失败图片,UI 显示“部分素材未能导出”。 +- 全部图片失败:不下载 ZIP,显示失败提示。 +- 浏览器不支持 Blob 下载:显示失败提示。 + +## 测试计划 + +- 标题栏右上角显示 `下载画布素材` 图标入口。 +- 空画布时入口不可执行或提示为空。 +- 上传图和生成图都写入 ZIP。 +- 多图层引用同一素材时,图片文件只写一份。 +- 隐藏图层默认写入 `metadata.json`。 +- 图层的锁定、翻转、分组状态写入元数据。 +- `data:image` 图片不经过网络请求即可导出。 +- URL 图片 fetch 失败时不中断其他素材导出。 + +## 后续扩展 + +- 导出当前选中素材。 +- 导出画布快照 PNG。 +- 导出可恢复工程包。 +- 导入工程包恢复画布。 +- 后端异步打包大项目,前端只轮询下载结果。