# 目录工程协议 v2

仓库标识 `story.repository.json`，所有章节在 `chapters/` 下。

```text
story-chapters/
  .git/
  .gitattributes
  .gitignore
  story.repository.json
  chapters/
    chapter-001/
      story.project.json
      variables.json
      story/
        chapter.json
        nodes/
          start.json
          choice-a.json
          .gitkeep
      editor/
        layout.json
      assets/
        videos/opening.mp4
        videos/opening.mp4.meta
        .gitkeep
  .studio/                 # 本机派生数据，不跟踪
```

`story.project.json` 必须包含 `format: "story-studio/chapter"`、`schemaVersion: 2`、稳定 `projectId`、`name`、唯一 `chapterId`、`chapterName`、`chapterEntry`。

固定入口、story/chapter.json、story/nodes、editor/layout.json、variables.json、assets 均为必需结构。空目录用 .gitkeep 保留，以便 Git 和浏览器目录选择器搬运。项目路径由目录本身决定，不保存机器绝对路径。

`story/chapter.json` 保存章节入口、版本、附加原始字段以及各节点数组的 `nodeOrder`。`story/nodes/<nodeId>.json` 保存 `{ "type": "videoNodes", "node": {...} }`。不适合做文件名的节点 ID 使用 ID 的 SHA-256 文件名，但内部 ID 保持不变。后继列表和节点顺序均保留语义，不自动排序。

`editor/layout.json` 保存画布位置等编辑状态。节点位置不会混入剧情节点文件；导出时可重组位置。未知原配置字段继续保留。JSON 键稳定排序、两空格缩进、UTF-8/LF，未变化文件不重写。

素材旁置文件，例如 `opening.mp4.meta`：

```json
{
  "guid": "32位小写十六进制UUID",
  "kind": "video",
  "schemaVersion": 1,
  "sha256": "素材内容指纹"
}
```

节点的媒体字段，例如 `videoClipPath`，在编辑工程中保存 `asset://<guid>`。GUID 是身份；内容哈希仅用于章内重复导入检查。移动文件与 meta 不变更 GUID；替换文件也保留 GUID。解析时扫描本章 assets；GUID 的作用域是工程。缺失文件可保留 meta 与引用，检查显示缺失，不伪造媒体。

运行包将 GUID 编译为 `GameProduceFiles/EditoData/Chapters/<chapterId>/<guid>.<扩展名>`，避免章节素材名字相同导致冲突。运行包不作为编辑工程导入，需另行导出工程目录包。

保存的 revision 是基于配置内容与媒体文件状态计算的乐观版本号，不写入 Git 文件。浏览器与可选 Python CLI 的 revision 算法独立，不能跨工具直接复用 inspect / 草稿的 revision；应在对应工具重新加载后合并修改。

浏览器通过 File System Access 读写授权目录，或用 IndexedDB 事务保存浏览器副本。多标签页通过 Web Locks 串行写入，浏览器存储另有事务内 revision 比较。外部程序 / Git 不受 Web Locks 约束，仍需避免同时写入；保存前检查到外部变更会拒绝覆盖。

本机多文件写入前将受影响文件的前后版本写入 IndexedDB journal，异常 / 关闭后通过“草稿与写入恢复”下载变更文件包并修复，修复前阻止继续写入。普通草稿保存编辑模型，不含媒体。服务器不保存这些内容。

## 本地操作层（0.4）

`src/web/local-repository.js` 负责浏览器存储，`project-format.js` 负责目录序列化与运行包配置，`binary.js` 负责分块 SHA256 与 ZIP 输出。app 内部保留 `/api/...` 字符串作为本地分发名称，不执行网络请求；HTTP 服务器不提供这些路由。

可选 Python CLI 仅用于本地目录操作，其 `.studio/history`、`.studio/exports` 与写入锁为本地辅助文件，不属于静态服务器。无编辑器登录会话，也没有服务器端章节库。

## 编辑器 0.3 的扩展（目录格式仍为 v2）

`editor/layout.json` 增加 `behaviors`（每个宿主的动作/奖励向导配置）和 `memories`（回忆名称、解锁变量、回放跳转点）。运行逻辑始终保存在普通节点的 conditions、effects、nextNodeIds 等字段中；客户端不依赖这些编辑元数据。可以通过属性微调生成节点；再次应用向导会重建其管理的路由与变量规则，保留图片布局和时机（动作时机由动作向导管理）。

本地 catalog 操作返回章节、回忆登记与跳转点列表。运行导出递归收集跨章跳转依赖，生成多章节列表、去重变量定义与带章节路径的素材；相同名称但定义不同的变量会阻止导出。章节间共享变量名称是显式协议，复制工程后如需隔离奖励/回忆状态，应修改相应变量名。

预览存档使用当前网站浏览器的 `story-studio.preview.v3`，不写入 Git 工程、不访问线上游戏存档。清空仅影响编辑器预览。

0.3.2 封面兼容：已绑定的章节封面导出到 `GameProduceFiles/GameResources/stories/ChapterImages/ChapterThumbnails/<chapterId>_chapterthumbnails.<原扩展名>`；同一素材在剧情中的引用同步重映射，仅输出一份文件，工程中的 GUID 与源素材不变。当前客户端优先尝试 PNG，建议正式封面使用 PNG；其他图片格式保留原始编码，客户端仍可能探测其他扩展名。

目录名 `stories` 必须小写，与现有 Linux 内容服务目录一致。显式 thumbnailPath 指向此目录可避免客户端追加其他目录的默认候选；部署到已上线章节时，媒体与章节列表的 thumbnailPath 需一致更新。
