diff --git a/headless/README.md b/headless/README.md index 771424f..a3c3169 100644 --- a/headless/README.md +++ b/headless/README.md @@ -333,7 +333,7 @@ claim of constant-time full-frame updates or complete region-based drawing. array, with byte cursors, bounded record payloads, structural depth limits and abort/yield checks. It uses parserVersion 3 and can feed a host-managed generic ImportRunner. It does not yet interpret the exported document envelope, and -ProjectEngine now accepts native record arrays through `imports.startJsonArray(bytes, canvasId)`; the file picker routes `.json` to it. Exported document envelopes are still unsupported. +ProjectEngine now accepts native record arrays through `imports.startJsonArray(bytes, canvasId)`; the file picker routes `.json` to it. Exported document envelopes use the separate `imports.startDocument` path described below. A malformed structural tail ends with an explicit failure; previously emitted records remain available. Empty source bytes reject, while `[]` is a valid empty array. Array record errors are distinct from document schema/reference failures. @@ -344,3 +344,14 @@ and commit boundary as NDJSON. Such roots use recovery envelope 14; readers acce 1–14 and reject 15. Downgrading an array-bearing image to 13 rejects. This changes neither IndexedDB schema v4 nor the server wire protocol. Native array items have the same `{kind,key,record}` schema as NDJSON; `$target` denotes the chosen canvas. + +`imports.startDocument(bytes, canvasId)` accepts the toolbar's exported document +format v1. The JSON picker distinguishes object envelopes from record arrays. +Source canvas ownership and references map to fresh IDs; its root maps to the +explicit target, and tombstones are not copied into the target. The original +file/digest and parserVersion 4 persist; the ordinal cursor resumes against the +same indexed bytes. Document jobs use recovery v15 (readers accept 1–15, reject +16), without changing DB v4 or the wire protocol. Current resource-bearing records +report `import_resource_authorization_required`; other records can import with an +explicit partial result. This is not complete asset round-trip support. Preparation +still scans the envelope before progressive payload decoding and insertion. diff --git a/src/components/canvas/headless-imports.tsx b/src/components/canvas/headless-imports.tsx index 3b3ede3..7fcb2f5 100644 --- a/src/components/canvas/headless-imports.tsx +++ b/src/components/canvas/headless-imports.tsx @@ -7,7 +7,7 @@ import { IconButton } from "@/components/ui/icon-button"; import { errorText } from "@/lib/api"; import styles from "./canvas.module.css"; -const reason = (code: string) => ({ invalid_json_array: "JSON 记录数组格式错误", incomplete_json_array: "JSON 数组尾部不完整", json_nesting_limit: "JSON 嵌套层数过多", invalid_json: "记录格式错误", invalid_json_value: "记录包含不支持的数据", invalid_utf8: "文件编码不是 UTF-8", record_too_large: "单条记录过大", unresolved_dependency: "引用的记录未加入", invalid_native_import_record: "不是画布记录", import_component_unavailable: "元素类型暂不支持", invalid_geometry: "元素尺寸不合法", invalid_endpoint: "连线端点已不存在", duplicate_import_source_key: "记录标识重复", import_source_unavailable: "导入源文件不可用", import_source_changed: "源文件内容与保存时不一致,任务已暂停", import_source_integrity_unavailable: "旧任务缺少源内容校验,无法安全继续;已加入内容保留", import_source_integrity_missing: "源校验记录缺失,任务已暂停", import_source_integrity_interpreter_unavailable: "源校验版本暂不支持", import_target_missing: "目标分区已不存在", import_target_changed: "目标分区已改变", import_dependency_changed: "引用对象已删除或恢复为新版本", dependency_capacity: "等待引用过多,请调整文件顺序", copy_external_changed: "原图引用已删除、移动或恢复为新版本", invalid_duplicate_source: "复制源数据不可用", invalid_copy_snapshot: "复制快照不可用", invalid_copy_preparation: "复制准备记录不可用", copy_interpreter_changed: "复制规则已变化,需要重新复制", invalid_import_source: "文件过大或格式不支持" } as Record)[code] ?? "这条记录暂时无法加入"; +const reason = (code: string) => ({ import_resource_authorization_required: "资源需要重新授权,此记录尚未加入", invalid_document_import: "导出文档结构不合法", invalid_json_array: "JSON 记录数组格式错误", incomplete_json_array: "JSON 数组尾部不完整", json_nesting_limit: "JSON 嵌套层数过多", invalid_json: "记录格式错误", invalid_json_value: "记录包含不支持的数据", invalid_utf8: "文件编码不是 UTF-8", record_too_large: "单条记录过大", unresolved_dependency: "引用的记录未加入", invalid_native_import_record: "不是画布记录", import_component_unavailable: "元素类型暂不支持", invalid_geometry: "元素尺寸不合法", invalid_endpoint: "连线端点已不存在", duplicate_import_source_key: "记录标识重复", import_source_unavailable: "导入源文件不可用", import_source_changed: "源文件内容与保存时不一致,任务已暂停", import_source_integrity_unavailable: "旧任务缺少源内容校验,无法安全继续;已加入内容保留", import_source_integrity_missing: "源校验记录缺失,任务已暂停", import_source_integrity_interpreter_unavailable: "源校验版本暂不支持", import_target_missing: "目标分区已不存在", import_target_changed: "目标分区已改变", import_dependency_changed: "引用对象已删除或恢复为新版本", dependency_capacity: "等待引用过多,请调整文件顺序", copy_external_changed: "原图引用已删除、移动或恢复为新版本", invalid_duplicate_source: "复制源数据不可用", invalid_copy_snapshot: "复制快照不可用", invalid_copy_preparation: "复制准备记录不可用", copy_interpreter_changed: "复制规则已变化,需要重新复制", invalid_import_source: "文件过大或格式不支持" } as Record)[code] ?? "这条记录暂时无法加入"; export function HeadlessImports({ engine, canvasId }: { engine: ProjectEngine; canvasId: string }) { const subscribe = useCallback((notify: () => void) => engine.editor.subscribe(notify), [engine]); @@ -18,9 +18,9 @@ export function HeadlessImports({ engine, canvasId }: { engine: ProjectEngine; c const control = (work: Promise) => { void work.then(() => { if (scope.current.engine === engine && scope.current.canvasId === canvasId) setError(""); }).catch(error => { if (scope.current.engine === engine && scope.current.canvasId === canvasId) setError(errorText(error)); }); }; return