Skip to content

fix(adapt): sync edits from /editor back into the result panel - #61

Merged
shanchuann merged 2 commits into
mainfrom
feat/yaml-back-sync
Jun 7, 2026
Merged

shanchuann merged 2 commits into
mainfrom
feat/yaml-back-sync

Conversation

@shanchuann

Copy link
Copy Markdown
Owner

功能描述

修隐藏的数据双源 bug:从 Editor 改完回到 Adapt,之前你看到的 YAML 是旧的——LLM 第一次生成的版本,作者所有的手动修改都不反映。

后果两个:

  1. PR feat(export): screenplay export to Fountain / txt / Markdown / PDF #60 加的导出剧本下拉用的是 Adapt state 里的 result.screenplay_yaml,会导出改之前的版本
  2. 顶部显示的 schema_validation / quality_metrics 也是旧状态,作者以为搞定了,其实还是 LLM 那版

实现思路

Adapt mount + tab focus / visibilitychange 三个时机各跑一次同步:

```

  1. 读 sessionStorage["story2script:draft"](Editor 实时写)
  2. 跟 result.screenplay_yaml 比对
  3. 不同就:
    • 调 /api/v1/validate 拿最新 schema_validation
    • setResult({ ...result, screenplay_yaml: draft, schema_validation })
      ```

quality_metrics 故意不更新——它描述的是 LLM 怎么生成的草稿(章节数、AI stage 应用情况、置信度均值),跟手动改无关,刷它会误导。

validate 调用失败时回退到只换 YAML,导出按钮还是反映当前 YAML;后续 /export 端点会重新报 schema 错误。

监听 `window.focus` 和 `document.visibilitychange`——React Router 切换路由不会重 mount 已渲染的页面,所以光靠 mount effect 不够;focus 事件覆盖"切 tab 回来",visibilitychange 覆盖"标签页隐藏后切回"。

测试方式

  • 后端 `pytest`: 314 passed(没碰 Python)
  • 前端 `tsc -b`: clean
  • `eslint`: 0 新增(1 条 main 已有历史告警)

人眼验证:

  1. 上传/加载示例 → 改编 → 进 Editor
  2. 改任意 beat 的文本(或 speaker)
  3. 点 `改编工作台` 回 Adapt
  4. 应该立刻看到 result 面板里的 YAML 反映新内容("源代码" tab 可查)
  5. `导出剧本` 下拉随便选一个格式 → 下载的文件应包含改后内容

依赖与复用声明

无新增依赖。复用:

  • 现有 `validate()` API 客户端
  • 现有 `STORAGE_KEY_DRAFT = "story2script:draft"`(Editor 的 patchScreenplayYaml 实时写这个 key)

依赖 PR #60(导出剧本)——没那个的话双源 bug 只是显示问题,有了导出就成了真问题。所以两个 PR 同期推。

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

shanchuann and others added 2 commits June 7, 2026 17:01
The editor's only download was the schema YAML — useful for the
pipeline contract, useless for directors and actors. Adds an
end-to-end export path so the author leaves with a file the rest
of the team can read.

Backend: new story2script.export package with three pure renderers
(to_fountain, to_txt, to_markdown), wired to POST /api/v1/export.
The endpoint runs the same schema validator the YAML download
relies on, so a broken draft surfaces as 422 with field-level errors
instead of a half-rendered file. PDF is intentionally NOT a server
format — generating CJK-safe PDFs needs either weasyprint+GTK or a
5 MB bundled font; the frontend opens the Markdown variant in a
print-friendly window and lets the browser's native Print → Save
as PDF do the conversion. Zero new server dependencies.

Frontend: the toolbar's 导出 YAML button becomes a 导出剧本
dropdown with five options (Fountain / txt / md / PDF / raw YAML).
PDF reuses the markdown response, rewraps it in an inline HTML
template with CJK system fonts, and triggers window.print() after
load. Error messaging surfaces 422 schema details so the author
knows which field to fix in the editor.

Tests: 25 new (8 fountain, 4 txt, 7 markdown, 6 endpoint),
covering character cue casing, parenthetical split, transition
normalization, mode block presence, invalid YAML / invalid
screenplay / unknown format error paths. Full suite 314 passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Returning to /app after editing in /editor showed a stale result —
the screenplay_yaml in the React state still reflected the original
LLM output even though the editor had patched sessionStorage with
every keystroke. Two real consequences:

- Exporting via PR #60's new dropdown would have produced the
  PRE-edit Fountain / Markdown / etc., silently discarding all the
  author's manual fixes.
- Schema validation and quality metrics displayed the LLM's draft
  state, not what the author was about to ship.

Strategy: on mount AND on tab focus / visibility change, diff the
editor's draft against the result.screenplay_yaml. When they differ,
swap the YAML in and re-run /api/v1/validate so schema_validation
reflects current state. Quality metrics intentionally stay frozen
since they describe how the LLM produced the draft, not whether
the author's hand-edits are still valid.

The validate call's failure path falls through to a YAML-only swap
so the export button still reflects current edits when the backend
is down; the eventual /export call will surface the same errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@shanchuann
shanchuann merged commit e93bf9f into main Jun 7, 2026
2 checks passed
@shanchuann
shanchuann deleted the feat/yaml-back-sync branch June 7, 2026 09:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant