Skip to content

fix(mobile): 稳定消息分享的原生 WebView 导出 - #3029

Merged
MagicLizi merged 7 commits into
mainfrom
cindy/sweet-babbage
Aug 19, 2026
Merged

fix(mobile): 稳定消息分享的原生 WebView 导出#3029
MagicLizi merged 7 commits into
mainfrom
cindy/sweet-babbage

Conversation

@DavidShenXD

Copy link
Copy Markdown
Collaborator

这次改了什么

摘要

消息分享图片在 iOS 上优先使用原生 WKWebView 渲染真实 HTML/CSS,并通过固定视口逐屏 takeSnapshot、Swift 原生拼接生成长图。原生路径异常或最终图片判定为空白时,继续使用现有 SVG 导出兜底。

同时修复了两个导致系统预览空白或加载失败的问题:

  • 将 WKWebView 挂载到可合成的独立窗口,滚动后等待 WebKit 完成绘制再截图;
  • 分享成功后不立即删除 cache PNG,避免 iOS 分享扩展稍后读取文件时失败。

变更类型

  • feat 新功能
  • fix 缺陷修复
  • refactor / perf 重构或性能优化
  • docs / test / chore 文档、测试或工程维护
  • 其他:

范围

  • 关联 Issue / 需求:PR fix(mobile): 修复消息分享图片卡住与导出失败 #2950 合并后的消息分享图片后续增强。
  • 本 PR 包含:iOS 原生 WKWebView HTML/CSS 导出、长页面分片截图与原生合图、空白检测、SVG 最终兜底、分享文件生命周期修复及回归测试。
  • 明确不包含:恢复已验证不可靠的 foreignObject → Canvas.toDataURL() 链路;Android 原生导出改造;远程图片下载或持久化。
  • 用户可见变化:大多数 iOS 消息分享图片可保留标题、列表、表格、代码块等真实 Markdown 排版;原生失败时仍输出现有 SVG。
  • 是否存在 breaking change:无。

UI 变化

  • 引用的设计规范:docs/design-rules/DESIGN.md。本 PR 不新增页面布局、控件或 UI 文案;分享图继续复用现有 HTML/CSS、语义色以及 Light/Dark logo 资源,变化仅为导出实现和最终图片还原度。

怎么验证的

自动验证

pnpm test:unit:related
结果:PASS,apps/mobile related unit tests 通过。

pnpm --filter mobile typecheck
结果:PASS。

pnpm exec vitest run src/__tests__/conversationShareWebViewHtml.test.ts
结果:PASS,10/10。

git diff --check
结果:PASS。

pnpm check:dco
结果:PASS,origin/main..HEAD 共 4 个提交均带 DCO 签名。

手工验证

  • 分支 / worktree:cindy/sweet-babbage.cindy-worktrees/sweet-babbage
  • Metro:端口 8081,由当前 worktree 的 Mobile Debug 构建使用;日志出现 iOS Bundled
  • 原生构建:Cindy Host iOS Simulator,Debug-iphonesimulatorBUILD SUCCEEDED;artifact 47efc911-01cf-4348-a847-d4cd92784dda
  • 设备:iPhone 17 Pro 模拟器,iOS 26.5。
  • 用例:任务“编写测试用Markdown文本”中分享包含标题、列表、任务列表、表格、代码块、链接等内容的长消息。
  • 结果:Metro 出现 [conversation-share] native webview export succeeded,未出现 SVG fallback;系统分享面板生成约 342 KB PNG,系统“预览”可见真实 Markdown 排版,不再为空白图或整图加载失败。

未执行的验证

  • 未单独目检暗色模式:本 PR 未修改颜色 token,并继续复用现有 Light/Dark 分享资源。
  • 未验证真实 iPhone:已在 Host 管理的 iOS 26.5 模拟器完成原生构建和完整分享预览链路。

风险

风险分类

  • 无已知风险
  • SQLite / migration
  • system prompt
  • 协议兼容
  • 权限 / 安全 / 用户数据
  • 存量插件兼容(批准状态 / 指纹 / manifest 校验 / 安装布局 / 包格式)
  • 原生层 / fingerprint / OTA
  • 跨平台差异
  • 其他:

影响与回滚

  • 影响范围:仅 iOS 消息分享图片主路径;Android 继续使用原有 SVG 路径。原生结果异常、超时、超出像素限制或判定为空白时会自动回落 SVG。
  • 为什么冷更不可避免:需要修改 Swift 中 WKWebView 的窗口挂载、分片 takeSnapshot 和原生合图逻辑,纯 JS / OTA 无法修复旧原生实现。
  • 存量装机影响:该改动会改变 Mobile runtime fingerprint;旧安装包升级前不会获得新的 Swift 导出实现,仍保留原有分享能力。新 runtime 的 JS 与原生实现配套下发。
  • 发版节奏建议:随下一次 Mobile 冷更包发布;合并前需要仓库指定把关人对冷更影响明确确认。
  • 其他风险:成功分享的 PNG 留在系统 cache,由系统回收,避免分享扩展延迟读取失败;外链图片无法离线带入时继续显示现有可见占位。
  • 回滚 / 降级方式:回滚本 PR 即恢复 SVG 主路径;运行时已有 SVG 最终兜底,无需迁移用户数据。

提交前检查

  • 已 review 完整 diff
  • 每个 commit 都带 DCO 签名(git commit -s,见 DCO
  • UI 改动已在「UI 变化」注明引用的设计规范章节(不涉及 UI 则跳过)
  • 未提交凭证、令牌或授权文件
  • 已补充必要文档
  • 已确认测试结果或说明未执行原因

David added 4 commits August 19, 2026 12:29
Signed-off-by: David <david@DaviddeMacBook-Air.local>
Signed-off-by: David <david@DaviddeMacBook-Air.local>
Signed-off-by: David <david@DaviddeMacBook-Air.local>
Signed-off-by: David <david@DaviddeMacBook-Air.local>
@DavidShenXD
DavidShenXD requested a review from a team as a code owner August 19, 2026 10:21

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3b2939b1a9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/mobile/app/sessions/[sessionId].tsx Outdated
@greptile-apps

greptile-apps Bot commented Aug 19, 2026

Copy link
Copy Markdown

Greptile Summary

此 PR 将 iOS 消息分享图片的主路径切换为挂载在独立窗口中的原生 WKWebView 分片截图,并保留 SVG 兜底。

  • 使用固定视口逐屏截图并在 Swift 侧拼接长图,同时检测空白输出
  • 在原生导出前加载内置图片资源并生成完整 HTML
  • 延后清理分享 PNG,并以有限保留和批量回收控制缓存增长
  • 增加覆盖原生窗口挂载、分片渲染、空白检测和缓存生命周期的回归断言

Confidence Score: 5/5

当前未发现仍需阻止合并的故障,PR 看起来可以安全合并。

未发现仍然存在的阻塞性故障。

Important Files Changed

Filename Overview
apps/mobile/app/sessions/[sessionId].tsx 接入 iOS 原生 HTML 导出、资源预加载、SVG 兜底以及新的分享文件生命周期管理。
apps/mobile/modules/xdt-screenshot-monitor/ios/XdtScreenshotMonitorModule.swift 将 WKWebView 挂载到独立窗口,以固定视口分片截图、原生拼接并检测空白输出。
apps/mobile/src/session/ConversationShareWebView.tsx 新增保留最近三份分享图片并分批删除旧缓存的 best-effort 清理逻辑。
apps/mobile/src/tests/conversationShareWebViewHtml.test.ts 更新源码级回归断言以覆盖原生分片导出和分享 PNG 保留策略。

Sequence Diagram

sequenceDiagram
  participant U as 用户
  participant JS as 会话分享流程
  participant WK as 原生 WKWebView
  participant FS as Cache
  participant IOS as iOS 分享面板
  U->>JS: 分享所选消息
  JS->>JS: 构建 HTML 并加载内置资源
  JS->>WK: renderConversationShareHtmlToPng
  loop 固定视口分片
    WK->>WK: 滚动、等待绘制并截图
  end
  WK->>WK: 拼接长图并检测空白
  alt 原生导出成功
    WK-->>JS: PNG Base64
  else 原生失败或结果为空白
    JS->>JS: 使用 SVG 导出兜底
  end
  JS->>FS: 写入临时 PNG
  JS->>IOS: shareAsync(PNG URL)
  JS->>FS: 后续分享开始时有界清理旧文件
Loading

Reviews (4): Last reviewed commit: "test(mobile): normalize source line endi..." | Re-trigger Greptile

@MagicLizi MagicLizi added status:threads-open 还有未 resolve 的评审讨论(review-pr 自动维护,仅展示) touches:cold-update 改动影响移动端冷更新(review-pr 自动维护,仅展示) touches:product-ui 改动碰到产品 / UI 面(review-pr 自动维护,仅展示) labels Aug 19, 2026
@MagicLizi

Copy link
Copy Markdown
Contributor

@DavidShenXD 👋 这个 PR 还有 1 条 review conversation 没 resolve(apps/mobile/app/sessions/[sessionId].tsx),auto-review 因此暂时跳过、没法继续审查 / 合并。

如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。

Signed-off-by: David <david@DaviddeMacBook-Air.local>
@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

反馈已处理

已处理:

  • 修复消息分享 PNG 缺少有界清理的问题:失败、取消或操作失活时立即删除本次产物;成功分享后保留文件供 iOS 分享扩展延迟读取。

变更:

  • 每次新分享开始前保留最近 3 个成功 PNG,并最多批量回收 8 个更旧文件。
  • 成功分享后不立即删除当前 PNG,避免系统预览再次出现加载失败。
  • 补充测试断言,锁定保留数量、清理批次和失败删除条件。

验证:

  • pnpm --filter mobile exec vitest run src/__tests__/conversationShareWebViewHtml.test.ts:10/10 通过。
  • pnpm --filter mobile typecheck:通过。
  • pnpm test:unit:related:通过。
  • git diff --check:通过。
  • pnpm check:dco:5 个提交均通过。

备注:

  • 成功文件不会在 shareAsync 返回时立即删除,而是在后续分享开始前有界回收,以兼顾系统扩展读取时序与缓存容量。

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 444f5df1bc

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/mobile/app/sessions/[sessionId].tsx
@MagicLizi

Copy link
Copy Markdown
Contributor

@DavidShenXD 👋 这个 PR 还有 1 条 review conversation 没 resolve(apps/mobile/app/sessions/[sessionId].tsx),auto-review 因此暂时跳过、没法继续审查 / 合并。

如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。

@MagicLizi MagicLizi removed the status:threads-open 还有未 resolve 的评审讨论(review-pr 自动维护,仅展示) label Aug 19, 2026
Signed-off-by: David <david@DaviddeMacBook-Air.local>
@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

反馈已处理

已处理:

  • 修复 Android 进入分享选择态时仍构建原生分享 HTML 的无效开销。

变更:

  • 仅在 iOS 准备原生 WebView 分享所需的 HTML 与 branding data URI;Android 保持原有消息选择和 SVG 分享路径不变。
  • 补充源码守卫测试,锁定平台门控、资源加载门控和原生导出可用性判断。

验证:

  • pnpm --filter mobile exec vitest run src/__tests__/conversationShareWebViewHtml.test.ts:10/10 通过。
  • pnpm --filter mobile run --if-present typecheck:通过。
  • pnpm test:unit:related:apps/mobile 相关单测通过。
  • git diff --check:通过。
  • pnpm check:dco:通过。

备注:

  • Android 分享行为未改变;无其他事项。

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 39735263c5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@MagicLizi

Copy link
Copy Markdown
Contributor

@DavidShenXD 👋 这个 PR 还有 1 条 review conversation 没 resolve(apps/mobile/modules/xdt-screenshot-monitor/ios/XdtScreenshotMonitorModule.swift),auto-review 因此暂时跳过、没法继续审查 / 合并。

如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。

@MagicLizi MagicLizi added the status:threads-open 还有未 resolve 的评审讨论(review-pr 自动维护,仅展示) label Aug 19, 2026
Signed-off-by: David <david@DaviddeMacBook-Air.local>

@MagicLizi MagicLizi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审查结果:P1×2,需修复后重新审查

P1-1: 原生导出路径对宽内容(表格/代码块)会静默回退到 SVG

位置: apps/mobile/modules/xdt-screenshot-monitor/ios/XdtScreenshotMonitorModule.swift:227

问题: guard captureWidth <= webView.bounds.width + 1 会在内容宽度超过固定视口时 reject。但 conversationShareWebViewHtml.ts:376-386 明确设置了 #xdt-content table { max-width: none; overflow: visible }#xdt-content pre { max-width: none; overflow: visible },允许表格和代码块溢出容器。因此对于包含长代码行或宽表格的消息,stage.scrollWidth 会合法超出固定视口宽度,触发 guard 回退到 SVG 路径——恰好是本 PR 声称要改进的内容类型。

失败场景: 分享一条包含 width: max-content 表格的消息 → 原生路径的 captureWidth > bounds.width + 1 → 静默回退到 SVG → 用户看不到 "原生富文本排版" 的改进。

P1-2: 已分享 PNG 可能在 iOS 分享扩展仍在读取时被删除

位置: apps/mobile/app/sessions/[sessionId].tsx:6129-6141

问题: shareCompleted 只在 shareAsync 返回后、isShareOperationActive() 检查通过后才设为 true。如果在系统分享面板显示期间用户导航到其他会话(push 通知/deep link),isShareOperationActive() 返回 false,函数提前 return,shareCompleted 保持 falsefinally 块立即删除刚分享的 PNG。

失败场景: 用户点分享 → 系统分享面板弹出 → 收到 push 通知跳转 → shareSelectionActive 被 cleanup effect 重置 → isShareOperationActive() 为 false → PNG 被 finally 删掉 → iOS 分享扩展读取文件失败。


建议修复方向:

  1. P1-1: 对 captureWidth > bounds.width 的情况,考虑动态扩展 webview frame(或 fallback 前 log 明确警告),而不是静默 reject。
  2. P1-2: 文件保留决策应基于 shareAsync 是否成功返回,而不是 UI 选择状态。

@MagicLizi MagicLizi added status:awaiting-author 等作者修改(review-pr 自动维护,仅展示) and removed status:threads-open 还有未 resolve 的评审讨论(review-pr 自动维护,仅展示) labels Aug 19, 2026
@MagicLizi

Copy link
Copy Markdown
Contributor

💡 UI 变更提示

命中 UI 路径(apps/mobile/app/sessions/[sessionId].tsx / apps/mobile/modules/xdt-screenshot-monitor/ios/XdtScreenshotMonitorModule.swift / apps/mobile/src/session/ConversationShareWebView.tsx)但 description 未附界面效果证据——建议补充改动后效果:截图/录屏,或改动后界面的 HTML 页面(```html 代码块、.html 附件或在线预览链接),便于确认界面符合 DESIGN.md 设计规范。

@MagicLizi MagicLizi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

代码审查通过,无 P0/P1 问题。WebView tiled-capture 实现结构清晰,安全边界、内存限制和 fallback 路径均到位。

@MagicLizi
MagicLizi merged commit d8b5b28 into main Aug 19, 2026
19 checks passed
@MagicLizi
MagicLizi deleted the cindy/sweet-babbage branch August 19, 2026 14:53
@MagicLizi

Copy link
Copy Markdown
Contributor

原生 WebView 截图分享终于稳了 👏 tile 拼接加 SVG fallback 的设计兼顾性能和可靠性,跨端的打磨肉眼可见。

@MagicLizi MagicLizi removed the status:awaiting-author 等作者修改(review-pr 自动维护,仅展示) label Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

touches:cold-update 改动影响移动端冷更新(review-pr 自动维护,仅展示) touches:product-ui 改动碰到产品 / UI 面(review-pr 自动维护,仅展示)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants