修复运行页刷新按钮不真正重载 #640

Merged
suzmii merged 4 commits from fix/run-preview-refresh-reload into master 2026-10-06 10:59:24 +08:00
Member

现象

运行页右下角的「刷新运行画面」不是真正重载:游戏页面出现异常(白屏 / 报错)之后点它不会恢复,
必须切到「资源管理」再切回「运行」页才行。

根因(两条)

  1. apps/ai-game-creator-shell/src/features/project-workspace/LocalGamePreviewFrame.tsx(改前 :452-456)
    刷新只换 iframe 的 React key,src 一字不变 —— 同一地址的导航在请求层面没有任何变化,
    可能直接吃 HTTP 缓存;改了代码、或上一版崩过,都拿不到新页面。
  2. 同文件(改前 :375-385)量到的内容尺寸只在 embeddedUrl 变化时清空,刷新时不清,
    于是新页面先被上一版的尺寸渲染、要等它自己上报才纠正;切页会把整个运行分支卸载
    (尺寸归零)→ 这就是「必须切出再进才恢复」的来源。

修复

  • 刷新时给 iframe 地址加一枚只随 reloadNonce 递增的非业务参数 __agc_reload:
    首次加载(nonce=0)地址逐字不动;既有 query / hash 用 URL API 原样保留;
    resolveEmbeddedPreviewUrl 的 http: + 127.0.0.1 约束与 postMessage 的 origin 校验都不变。
  • 重新载入(换 key 或换地址)时丢弃上一版量到的内容尺寸:新页面先在容器尺寸上适配,等它自己上报。
  • 保留既有的 key={reloadNonce} 元素身份替换,以及点选模式在 onLoad 里重新 enable。

前后对照

改前 改后
地址 不变(同地址导航) 追加 __agc_reload=<nonce>(首次加载不变)
请求 可能命中缓存 一定是一次新请求
尺寸状态 沿用上一版测量值 清零,按容器适配后等新页面上报
恢复方式 只能切出再进「运行」页 点刷新即恢复

验证

  • npx vitest run 五文件(localGamePreviewFrame / previewActivation / runPreviewFullscreen /
    runPreviewRefresh / appSurface)→ 5 passed,224 passed | 9 skipped (233)
  • npm run agc:typecheck → EXIT=0
  • npm run check:encoding → EXIT=0(5268 files);git diff --check 空;git status --porcelain 空
  • 用例:
    • tests/localGamePreviewFrame.test.ts 新增「刷新运行画面:换新节点、地址带一次性参数,并丢掉上一版的
      测量结果按容器重新适配」(先上报 835px → 刷新后新节点宽高回到容器 800×500)
    • tests/runPreviewRefresh.test.tsx 新增「连点两次各自都是一次新的载入:参数逐次递增」(1 → 2);
      既有那条的 src 断言按新契约更新为「origin + pathname 仍是原地址 + 带 __agc_reload=1」,
      节点替换与 onPlay 调用一次的断言未动

边界

  • 不动点选与桥脚本(resources/preview/local-preview-fit.js、runtimeInspectResourceMatch.ts)。
  • 刷新仍不卸载整个运行页(只换 iframe 元素身份):全屏预览、点选模式、错误态与既有文案都不变。

未验证

  • 真机点击刷新未做:需要从本分支源码编译客户端后人工点一次(崩溃态点刷新应当场恢复)。
## 现象 运行页右下角的「刷新运行画面」不是真正重载:游戏页面出现异常(白屏 / 报错)之后点它不会恢复, 必须切到「资源管理」再切回「运行」页才行。 ## 根因(两条) 1. `apps/ai-game-creator-shell/src/features/project-workspace/LocalGamePreviewFrame.tsx`(改前 :452-456) 刷新只换 iframe 的 React `key`,`src` 一字不变 —— 同一地址的导航在请求层面没有任何变化, 可能直接吃 HTTP 缓存;改了代码、或上一版崩过,都拿不到新页面。 2. 同文件(改前 :375-385)量到的内容尺寸只在 `embeddedUrl` 变化时清空,刷新时不清, 于是新页面先被上一版的尺寸渲染、要等它自己上报才纠正;切页会把整个运行分支卸载 (尺寸归零)→ 这就是「必须切出再进才恢复」的来源。 ## 修复 - 刷新时给 iframe 地址加一枚只随 `reloadNonce` 递增的非业务参数 `__agc_reload`: 首次加载(nonce=0)**地址逐字不动**;既有 query / hash 用 URL API 原样保留; `resolveEmbeddedPreviewUrl` 的 `http:` + `127.0.0.1` 约束与 postMessage 的 origin 校验都不变。 - 重新载入(换 key 或换地址)时丢弃上一版量到的内容尺寸:新页面先在容器尺寸上适配,等它自己上报。 - 保留既有的 `key={reloadNonce}` 元素身份替换,以及点选模式在 `onLoad` 里重新 enable。 ## 前后对照 | | 改前 | 改后 | | --- | --- | --- | | 地址 | 不变(同地址导航) | 追加 `__agc_reload=<nonce>`(首次加载不变) | | 请求 | 可能命中缓存 | 一定是一次新请求 | | 尺寸状态 | 沿用上一版测量值 | 清零,按容器适配后等新页面上报 | | 恢复方式 | 只能切出再进「运行」页 | 点刷新即恢复 | ## 验证 - `npx vitest run` 五文件(localGamePreviewFrame / previewActivation / runPreviewFullscreen / runPreviewRefresh / appSurface)→ **5 passed,224 passed | 9 skipped (233)** - `npm run agc:typecheck` → EXIT=0 - `npm run check:encoding` → EXIT=0(5268 files);`git diff --check` 空;`git status --porcelain` 空 - 用例: - `tests/localGamePreviewFrame.test.ts` 新增「刷新运行画面:换新节点、地址带一次性参数,并丢掉上一版的 测量结果按容器重新适配」(先上报 835px → 刷新后新节点宽高回到容器 800×500) - `tests/runPreviewRefresh.test.tsx` 新增「连点两次各自都是一次新的载入:参数逐次递增」(1 → 2); 既有那条的 `src` 断言按新契约更新为「origin + pathname 仍是原地址 + 带 `__agc_reload=1`」, 节点替换与 `onPlay` 调用一次的断言未动 ## 边界 - 不动点选与桥脚本(`resources/preview/local-preview-fit.js`、`runtimeInspectResourceMatch.ts`)。 - 刷新仍**不卸载**整个运行页(只换 iframe 元素身份):全屏预览、点选模式、错误态与既有文案都不变。 ## 未验证 - 真机点击刷新未做:需要从本分支源码编译客户端后人工点一次(崩溃态点刷新应当场恢复)。
suzmii added 1 commit 2026-10-05 18:45:05 +08:00
修复运行页「刷新运行画面」不是真正重载
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
14283db943
- 刷新时给 iframe 地址加一枚随 reloadNonce 递增的 __agc_reload 参数:同一地址的导航可能命中缓存,只换元素身份不足以保证重新请求
- 重新载入时丢掉上一版量到的内容尺寸,新页面先在容器尺寸上适配,不再被上一版的适配结果渲染
- tests/localGamePreviewFrame.test.ts 补刷新回归:换新节点 + 地址带一次性参数 + 丢弃旧测量
- tests/runPreviewRefresh.test.tsx 断言改为新契约(原地址逐字保留、参数逐次递增、节点每次都是新的)
suzmii added 1 commit 2026-10-05 19:19:55 +08:00
Merge remote-tracking branch 'origin/master' into fix/run-preview-refresh-reload
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m10s
Project CI / Native shell tests (pull_request) Failing after 1m6s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 5m1s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m21s
Project CI / Frontend tests (pull_request) Successful in 2m21s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m32s
Project CI / Backend tests (pull_request) Successful in 7m35s
Project CI / Repository checks (pull_request) Successful in 5m52s
386fe4b64f
Author
Member

已同步最新 master(含点选功能 #624 的合并提交 96859666f),本分支新增 merge 提交 386fe4b64。

  • 同步方式:git merge --no-ff origin/master,无冲突(master 侧未改本 PR 的 3 个文件;
    LocalGamePreviewFrame.tsx 的刷新修复与两个用例原样保留)。
  • 门禁复跑(顶端 386fe4b64):
    • npx vitest run 7 文件(localGamePreviewFrame / previewActivation / runPreviewFullscreen /
      runPreviewRefresh / runtimeInspectEngines / localPreviewInspectSizeStability / appSurface)
      → 7 passed,262 passed | 9 skipped (271)
    • npm run agc:typecheck → EXIT=0
    • npm run check:encoding → EXIT=0(5278 files);git diff --check 空;git status --porcelain 空
  • 现在 head 已包含最新 base,CI 的 base-ancestor 校验不会再失败;8 项检查当前均为 pending
    (若是刚推完,等 CI 跑完再看结论)。
  • 仍待真机验证:从本分支源码编译客户端后人工点一次运行页右下角的刷新(崩溃态下点刷新应当场恢复)。
已同步最新 master(含点选功能 #624 的合并提交 `96859666f`),本分支新增 merge 提交 `386fe4b64`。 - 同步方式:`git merge --no-ff origin/master`,**无冲突**(master 侧未改本 PR 的 3 个文件; `LocalGamePreviewFrame.tsx` 的刷新修复与两个用例原样保留)。 - 门禁复跑(顶端 `386fe4b64`): - `npx vitest run` 7 文件(localGamePreviewFrame / previewActivation / runPreviewFullscreen / runPreviewRefresh / runtimeInspectEngines / localPreviewInspectSizeStability / appSurface) → **7 passed,262 passed | 9 skipped (271)** - `npm run agc:typecheck` → EXIT=0 - `npm run check:encoding` → EXIT=0(5278 files);`git diff --check` 空;`git status --porcelain` 空 - 现在 head 已包含最新 base,CI 的 base-ancestor 校验不会再失败;8 项检查当前均为 pending (若是刚推完,等 CI 跑完再看结论)。 - 仍待真机验证:从本分支源码编译客户端后人工点一次运行页右下角的刷新(崩溃态下点刷新应当场恢复)。
suzmii added 1 commit 2026-10-06 00:04:15 +08:00
修正原生壳预览边界守卫以接受刷新重载变量
Project CI / Backend tests (pull_request) Failing after 24s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 5m47s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m38s
Project CI / Repository checks (pull_request) Failing after 14s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m11s
Project CI / Frontend tests (pull_request) Successful in 2m14s
Project CI / Native shell tests (pull_request) Successful in 8m40s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m25s
430d983f65
- 预览框 src 断言改为两种形态都接受:旧的 `src={embeddedUrl}`,或 `src={iframeUrl ?? undefined}`
- 新形态额外要求 `iframeUrl` 由 `useMemo` 定义、且定义体内引用 `embeddedUrl`(只改名不算通过)
- 其余断言(`resolveEmbeddedPreviewUrl(` / loopback 校验 / sandbox / 单 iframe 与单挂载)原样保留
- 背景:14283db94 为「刷新运行画面」引入 iframeUrl,撞红了 Native shell tests 的
  Run native shell contract gates(missing src={embeddedUrl});只接受新形态会让 master 反过来红
Author
Member

CI 失败根因与修复(Native shell tests)

根因:Native shell tests 的 step「Run native shell contract gates」(npm run check:native-shells:contract)
唯一错误 AI game creator embedded preview boundary drifted: missing src={embeddedUrl}
(scripts/check-native-shells.mjs:2650,断言清单在 :2645 硬钉字面量 'src={embeddedUrl}')。
引入者就是本 PR 的提交 14283db94:为了「刷新运行画面」真正重载,把
apps/ai-game-creator-shell/src/features/project-workspace/LocalGamePreviewFrame.tsx:456
改成了 src={iframeUrl ?? undefined}。master(96859666f)跑同一命令 EXIT=0,本 head EXIT=1。

修法(本 PR 追加提交 430d983f6):把预览框 src 断言改成两种形态都接受——
旧的 src={embeddedUrl},或新的 src={iframeUrl ?? undefined} 且 iframeUrl 由
const iframeUrl = useMemo(...) 定义、定义体内引用了被校验过的 embeddedUrl
(只改名、丢掉派生关系仍按漂移处理)。其余断言(resolveEmbeddedPreviewUrl(、loopback/127.0.0.1
校验、sandbox=...、单 iframe 与单挂载)一条没删;断言旁加了中文注释说明这次为何允许中转变量。
刷新功能本身(__agc_reload、尺寸重置、iframeUrl 派生)一行未动。scripts/ 下没有该守卫的
独立测试或 fixture 钉这些字面量(已搜过,无需连带修改)。

验证(均为本地实跑):

  • 正向(本 head + 放宽后的守卫):node scripts/check-native-shells.mjs --groups=contract → EXIT=0
  • 负向对照(一次性 worktree 里把 src 改成与 embeddedUrl 无关的 src={someOtherUrl}):
    同一命令 → EXIT=1,报的正是 iframe src must derive from the validated embeddedUrl
    → 守卫仍有牙齿(worktree 已删,改动未提交)
  • 兼容性(一次性 worktree 取 96859666f 的旧源码 + 放宽后的守卫):
    --groups=contract → EXIT=0 → master 不会因为这次放宽反过来红(worktree 已删)
  • 本 PR 其它门禁:npx vitest run 3 文件(localGamePreviewFrame / runPreviewRefresh /
    previewActivation)→ 22 passed;npm run agc:typecheck → EXIT=0;git diff --check 空;
    git status --porcelain 空
  • 说明:本地 node scripts/check-native-shells.mjs(全量,非 --groups=contract)在本机报
    failed to start h5-host-bridge-tests: spawnSync npm.cmd EINVAL;同一现象在 master 上一样出现
    (已实测),是脚本在 Windows 上拉起 npm 子进程的既有环境限制,与本改动无关;
    CI(Linux)不受影响。

已推送 430d983f6,CI 会以新 head 重跑;Native shell tests 预计转绿。

CI 失败根因与修复(Native shell tests) **根因**:`Native shell tests` 的 step「Run native shell contract gates」(`npm run check:native-shells:contract`) 唯一错误 `AI game creator embedded preview boundary drifted: missing src={embeddedUrl}` (`scripts/check-native-shells.mjs:2650`,断言清单在 `:2645` 硬钉字面量 `'src={embeddedUrl}'`)。 引入者就是本 PR 的提交 `14283db94`:为了「刷新运行画面」真正重载,把 `apps/ai-game-creator-shell/src/features/project-workspace/LocalGamePreviewFrame.tsx:456` 改成了 `src={iframeUrl ?? undefined}`。master(`96859666f`)跑同一命令 EXIT=0,本 head EXIT=1。 **修法**(本 PR 追加提交 `430d983f6`):把预览框 src 断言改成**两种形态都接受**—— 旧的 `src={embeddedUrl}`,或新的 `src={iframeUrl ?? undefined}` **且** `iframeUrl` 由 `const iframeUrl = useMemo(...)` 定义、定义体内引用了被校验过的 `embeddedUrl` (只改名、丢掉派生关系仍按漂移处理)。其余断言(`resolveEmbeddedPreviewUrl(`、loopback/`127.0.0.1` 校验、`sandbox=...`、单 iframe 与单挂载)一条没删;断言旁加了中文注释说明这次为何允许中转变量。 刷新功能本身(`__agc_reload`、尺寸重置、`iframeUrl` 派生)一行未动。`scripts/` 下没有该守卫的 独立测试或 fixture 钉这些字面量(已搜过,无需连带修改)。 **验证**(均为本地实跑): - 正向(本 head + 放宽后的守卫):`node scripts/check-native-shells.mjs --groups=contract` → EXIT=0 - 负向对照(一次性 worktree 里把 src 改成与 `embeddedUrl` 无关的 `src={someOtherUrl}`): 同一命令 → **EXIT=1**,报的正是 `iframe src must derive from the validated embeddedUrl` → 守卫仍有牙齿(worktree 已删,改动未提交) - 兼容性(一次性 worktree 取 `96859666f` 的旧源码 + 放宽后的守卫): `--groups=contract` → **EXIT=0** → master 不会因为这次放宽反过来红(worktree 已删) - 本 PR 其它门禁:`npx vitest run` 3 文件(localGamePreviewFrame / runPreviewRefresh / previewActivation)→ 22 passed;`npm run agc:typecheck` → EXIT=0;`git diff --check` 空; `git status --porcelain` 空 - 说明:本地 `node scripts/check-native-shells.mjs`(全量,非 `--groups=contract`)在本机报 `failed to start h5-host-bridge-tests: spawnSync npm.cmd EINVAL`;**同一现象在 master 上一样出现** (已实测),是脚本在 Windows 上拉起 npm 子进程的既有环境限制,与本改动无关; CI(Linux)不受影响。 已推送 `430d983f6`,CI 会以新 head 重跑;Native shell tests 预计转绿。
suzmii added 1 commit 2026-10-06 00:23:34 +08:00
Merge remote-tracking branch 'origin/master' into fix/run-preview-refresh-reload
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 6m54s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m54s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m12s
Project CI / Frontend tests (pull_request) Successful in 2m54s
Project CI / Backend tests (pull_request) Successful in 7m34s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m7s
Project CI / Repository checks (pull_request) Successful in 6m58s
Project CI / Native shell tests (pull_request) Successful in 9m32s
df30fdeec7
Author
Member

再次同步 master 并复跑门禁

  • 合并:git merge --no-ff origin/master → 合入 master 顶端 51cb05f41(#638「补充创作者主页与关注粉丝工程文档」),
    无冲突;本分支新增 merge 提交 df30fdeec(父:430d983f6 + 51cb05f41)。刷新修复与守卫修复都原样保留。
  • 门禁(顶端 df30fdeec):
    • node scripts/check-native-shells.mjs --groups=contract → EXIT=0(守卫修复没被跑回去)
    • npx vitest run 7 文件(localGamePreviewFrame / previewActivation / runPreviewFullscreen / runPreviewRefresh /
      runtimeInspectEngines / localPreviewInspectSizeStability / appSurface)→ 7 passed,262 passed | 9 skipped (271)
    • npm run agc:typecheck → EXIT=0;npm run check:encoding → EXIT=0(5325 files);
      git diff --check 空;git status --porcelain 空
  • CI(新 head df30fdeec,run 已开始):Rust lane 1/2 与 Rust crates 已 success;此前失败的两个 job 已越过
    "Resolve comparison base"
    (Backend tests 现在是 "Has started running",而上一轮它在同一 step 24s 就失败)
    → base 漂移已由这次同步解决。Native shell tests / Frontend tests / Rust lane 2/2 正在跑,
    Repository checks 与 web tests 仍在排队;结论出炉后如需我再补一条以 CI 结果为准。
再次同步 master 并复跑门禁 - 合并:`git merge --no-ff origin/master` → 合入 master 顶端 **51cb05f41**(#638「补充创作者主页与关注粉丝工程文档」), 无冲突;本分支新增 merge 提交 **df30fdeec**(父:430d983f6 + 51cb05f41)。刷新修复与守卫修复都原样保留。 - 门禁(顶端 df30fdeec): - `node scripts/check-native-shells.mjs --groups=contract` → EXIT=0(守卫修复没被跑回去) - `npx vitest run` 7 文件(localGamePreviewFrame / previewActivation / runPreviewFullscreen / runPreviewRefresh / runtimeInspectEngines / localPreviewInspectSizeStability / appSurface)→ **7 passed,262 passed | 9 skipped (271)** - `npm run agc:typecheck` → EXIT=0;`npm run check:encoding` → EXIT=0(5325 files); `git diff --check` 空;`git status --porcelain` 空 - CI(新 head df30fdeec,run 已开始):Rust lane 1/2 与 Rust crates 已 success;**此前失败的两个 job 已越过 "Resolve comparison base"**(Backend tests 现在是 "Has started running",而上一轮它在同一 step 24s 就失败) → base 漂移已由这次同步解决。Native shell tests / Frontend tests / Rust lane 2/2 正在跑, Repository checks 与 web tests 仍在排队;结论出炉后如需我再补一条以 CI 结果为准。
Author
Member

CI 8/8 全绿,可合并

  • head:df30fdeec77f5019a9dabb8c45d5e4e8578f1bef(含 master 51cb05f41 的同步 merge)
  • 8 项全部 success:Rust lane 1/2(6m54s)、Rust lane 2/2(6m12s)、Rust crates(4m54s)、
    Frontend tests(2m54s)、Backend tests(7m34s)、web tests(3m7s)、Repository checks(6m58s)、
    Native shell tests(9m32s)
  • 说明:Backend tests 与 Repository checks 上一轮卡在 "Resolve comparison base"(master 前移导致的
    base 漂移),这次同步 51cb05f41 后都已真正执行并通过;Native shell tests 的
    Run native shell contract gates 在守卫修复(430d983f6)后连续两轮为 success。
CI 8/8 全绿,可合并 - head:`df30fdeec77f5019a9dabb8c45d5e4e8578f1bef`(含 master `51cb05f41` 的同步 merge) - 8 项全部 success:Rust lane 1/2(6m54s)、Rust lane 2/2(6m12s)、Rust crates(4m54s)、 Frontend tests(2m54s)、Backend tests(7m34s)、web tests(3m7s)、Repository checks(6m58s)、 Native shell tests(9m32s) - 说明:`Backend tests` 与 `Repository checks` 上一轮卡在 "Resolve comparison base"(master 前移导致的 base 漂移),这次同步 51cb05f41 后**都已真正执行并通过**;`Native shell tests` 的 Run native shell contract gates 在守卫修复(430d983f6)后连续两轮为 success。
suzmii merged commit 7d342856fa into master 2026-10-06 10:59:24 +08:00
suzmii deleted branch fix/run-preview-refresh-reload 2026-10-06 10:59:24 +08:00
Sign in to join this conversation.