Skip to content

feat(desktop): HTML 文件在编辑器旁预览页面,按工作区静态站点加载并实时更新 - #1923

Merged
xiaoyumuxi merged 3 commits into
previewfrom
feat/desktop-html-preview
Nov 14, 2026
Merged

xiaoyumuxi merged 3 commits into
previewfrom
feat/desktop-html-preview

Conversation

@xiaoyumuxi

Copy link
Copy Markdown
Collaborator

关联 Issue / Linked issue

PR 类型 / Type

  • feat:新功能或用户可见的改进
  • fix:缺陷修复
  • docs:文档、Agent Note、Skill 等规则文件
  • release:稳定版发布准备

改动说明 / What changes

背景 / Why

迁移到 Viva 桌面端后,.html 文件只能当纯文本打开,看不到页面(旧 macOS 端有内嵌 HTML 预览)。#1903 已经让 Markdown、SVG、PDF、图片走系统 web 引擎,这次让 HTML 文件也在编辑器旁显示页面:页面按工作区静态站点加载样式、脚本和图片,编辑时实时更新。哪些请求能读、读哪个文件由 Rust Core 决定,平台层只负责读文件和交给 web view。

改动 / Changes

HTML 文件的双栏预览(desktop/internal/workbench/html_preview.go,新文件)

  • .html、.htm、.xhtml 文件(虚拟文档除外)在有 web 引擎时以“源码 + 页面”双栏打开,标签栏末尾复用 SVG 的三态切换:源码 / 两者 / 仅预览。
  • 打开时在后台向 Core 要站点(htmlPreview.site);Core 返回失败时该文件显示“无法预览此 HTML 文件。”,并记录一次 html_preview_site 失败日志。
  • 编辑器里的文本(含未保存的修改)每 150 毫秒最多交给页面一次,页面重新加载后回到原来的滚动位置;文本超过 8 MiB 时显示“此 HTML 文件过大,无法预览。”。
  • 预览工具栏:后退、前进、重新加载、在浏览器中打开(先保存,再用系统浏览器打开磁盘上的文件)。
  • 页面里用户点击的站外链接(https、mailto 等)交给 followDocumentLink,用系统应用打开,页面停在原处。

站点页(desktop/internal/webpreview/site.go,新文件;webpreview.go)

  • 新增 SitePage:单独的 lithe-site://site/ 协议,开启 Ephemeral。Cookie、localStorage 等不落盘,关闭预览后不保留,也不与其他 HTML 文件或 Markdown 预览(lithe-preview)共享。
  • 文档自己的地址直接返回编辑器里的文本,其余请求在 goroutine 里交给 Core 的 htmlPreview.resolveRequest:
    • file 读磁盘文件,单个文件上限 64 MiB,超过返回 413;
    • directory 返回跳转到 index.html 的页面;
    • denied 返回 403(如 /.env、私钥文件);
    • none 返回 404。
    • Core 出错时返回 404 并记录 web_preview_request。
  • 导航规则:主框架只能停留在站点内;iframe 可以加载 http、https、data、blob;用户手势触发的站外导航交给 Follow,脚本自动跳到站外的导航被取消;window.open 在页面内打开,开往站外的每秒最多交给 Follow 一次(Windows 不区分是否由用户触发)。
  • 站点页不接收页面消息,只接收滚动位置,用于重新加载后恢复。
  • Page 新增 Reload、Back、Forward;Config 新增 Address(文档在站点里的地址)和 Request(请求交给谁判定);Page 改为通过内部 webView 接口使用 *app.WebView,站点测试用替身代替系统 web view。

Core 客户端(desktop/internal/core/html_preview.go,新文件)

  • HTMLPreviewSite(document, workspace) 和 ResolveHTMLPreviewRequest(document, workspace, path),分别对应 Core 的两个命令。
  • TestHTMLPreviewSharedFixture 通过 contractfixture.CoreFixture 读取固定 Core 检出里的 editor/html-preview-v1.json,与 Core 自己的测试用同一份用例:工作区内页面、隐藏目录、工作区外页面、无效文档路径。

其他 workbench 改动

  • preview_pages.go:站点页打不开时只设置 siteFailed,HTML 文件之后都以源码显示,Markdown、SVG、PDF、图片预览不受影响;其他页面打不开仍设置 failed。打开失败记录 web_preview_open,页面资源失败按阶段记录 web_preview_<stage>。previewView 接口加上 Reload、Back、Forward。
  • documents.go:文档新增 html 预览状态;editor_view.go:HTML 文件可预览时绘制双栏,标签栏显示三态切换。
  • viewer_image.go:查看器工具栏新增 actions,可在缩放之后追加按钮;HTML 预览用它放后退、前进、重新加载和在浏览器中打开(最后一个复用已有词条 desktop.editorMenu.openInBrowser 和已有的 openInBrowser)。
  • browser_preview.go、markdown_preview.go:只更新注释,说明 HTML 文件现在也在编辑器旁显示页面。
  • markdown_web_test.go:测试替身记录 SetDocument 的 MIME,以及 Reload、Back、Forward 的调用次数。

界面文案(shared/i18n/)

  • 新增 5 条中英词条:desktop.htmlPreview.back、.forward、.reload、.tooLarge、.failed,并重新生成 desktop/internal/i18n/shared.generated.go。

文档与记录

  • Agent Note 2026-11-14-web-view-previews.md 新增 HTML 预览一节:单独协议、临时存储、请求由 Core 判定、导航规则,以及被否方案(复用 lithe-preview、持久存储、localhost HTTP 服务、允许远程主框架)。
  • Agent Note 2026-10-22-html-preview-embedded-browser.md 补充 Viva 端的现状和与旧 macOS 内嵌浏览器的差距。
  • 功能矩阵 embedded-viewers.json:两端的证据路径加入 HTML 预览的实现和测试,验证动作和说明补充 HTML 预览。
  • docs/viva-migration.md:HTML 预览从“未迁移”改为已迁移,并列出缺少的功能。

Core 固定版本

  • scripts/desktop/build-desktop-core.sh 的 PINNED_CORE_REVISION 升到 f88bc49(Lithe-core-rust#42 在 main 上的 squash 合入提交),注释说明这次固定带来的命令。

不在本 PR 范围内 / Out of scope

  • 旧 macOS 内嵌浏览器的地址栏、页面缩放、查找、开发者工具、JavaScript 对话框(alert/confirm/prompt)、文件选择、下载、打印、Range 请求和摄像头/麦克风授权,本 PR 都没有做;Viva 的 web view 目前没有对应接口(Note 已列出)。后退/前进按钮始终可点,Viva 不提供 canGoBack。
  • Windows 上页面里以 / 开头的绝对路径找不到文件,Windows 也会留下临时存储的 profile 目录。两者都是 Viva 的限制,需要在 Lithe-IDEA/Lithe-viva 修,Lithe 不加绕过代码。
  • 联调中发现本地构建脚本的既有缺陷:固定 Core 和 LITHE_CORE_RUST_DIR 共用 Cargo 目标目录,切换来源后可能继续链接旧库。已提交 [Bug] 桌面端 Core 构建在固定检出与 LITHE_CORE_RUST_DIR 之间切换后,继续暂存并链接上一份 Core 的静态库 #1922,本 PR 不修。

类型专属:feat

多平台对齐

Lithe 的目标是 macOS 与 Windows 能力对齐。必须选下面一项:

  • 两端都具备:合入后 macOS 与 Windows 都具备该能力(本 PR 同时实现,或本 PR 补齐另一端)
  • 只实现了 macOS:已为 Windows 提交跟进 Issue:#
  • 只实现了 Windows:已为 macOS 提交跟进 Issue:#
  • 刻意只属于某个平台(功能矩阵中另一端记为 platform-specific),理由:

说明:实现是 Viva 两端共用的 Go 代码,Windows 走 WebView2。Windows 实机还没有验证,矩阵中验证状态如实保持 pending。

范围检查 / Scope checks

功能矩阵 / Feature matrix

  • 已更新 shared/platform-feature-matrix/features/embedded-viewers.json:两端的实现状态和验证状态、证据路径、owner、验证动作都如实填写;没有运行验证的保持 pending
  • 已运行 ./scripts/platform-feature-matrix/verify-platform-feature-matrix.sh;改了平台实现路径的,还运行了 ./scripts/platform-feature-matrix/verify-platform-feature-matrix-change.sh origin/preview HEAD
  • 没有提交 .artifacts/ 下生成的矩阵视图
  • [N/A] 没有更新矩阵的,理由:已更新矩阵

插件归属 / Plugin ownership

  • 已在仓库根目录运行 develop-lithe 第三节“插件归属与宿主零依赖”的自查命令,结果为空
  • 新增或修改的测试只使用测试内联的合成插件、Core 契约 fixture 或插件市场索引快照;没有读取具体插件的插件包、仓库检出、构建产物或清单,没有使用真实插件 ID,没有“缺少插件时跳过”的测试
  • 没有为具体插件提供的能力新建或更新宿主功能矩阵条目;宿主条目的证据和验证动作不引用插件的测试、插件包或插件仓库
  • 插件文案没有写进 shared/i18n/;插件的构建步骤、资源路径和补丁没有写进 scripts/resources/、docs/ci-builds.md、Agent Note 或 Skill
  • [N/A] 修改了通用扩展点、可能影响已发布插件的,已在受影响插件的仓库运行它的测试:没有修改插件扩展点,插件网页仍走原来的 lithe-preview / 插件页路径

统一日志系统 / Structured logging

  • 已接入统一日志系统:复用已声明的事件 app.notification.operation_failed(operation 字段是自由取值的标识),通过现有的 a.emit(operationFailedLevel, operationFailedEvent, operationFailedFields(op, err)) 记录,取值为 html_preview_site、web_preview_open、web_preview_request(另有已有的 web_preview_file、web_preview_resolve、web_preview_message);没有新增事件声明,没有 print、直接写 stderr 或手写事件 ID
  • 一个操作只有一条终态,错误只在处理它的边界记录一次:站点失败在 askHTMLSite 记录一次,页面请求失败只在 serveSite 记录;逐个请求的成功不记日志
  • 没有记录源码、编辑内容、环境变量值、命令输出、凭据;错误只经 operationFailedFields 的 error 字段输出
  • [N/A] 旧日志生成和使用校验脚本:仓库中已没有 scripts/logging/,本 PR 没有涉及

界面文案 / i18n

  • 宿主文案都走共享 i18n:在 shared/i18n/ 补齐中英词条,运行 node scripts/i18n/generate-i18n.mjs 并提交生成结果,没有手改生成文件;Viva 代码通过 desktop/internal/i18n/ 的接口读取
  • [N/A] 插件提供的文字由插件自己翻译:本 PR 没有插件文案
  • 已运行 node scripts/i18n/verify-app-language-localization.mjs 和 node scripts/i18n/generate-i18n.mjs --check,没有放宽脚本或扩大豁免名单

逻辑归属 / Logic placement

  • 可共享的逻辑在 Rust Core:站点根目录的确定、请求路径的解析、隐藏文件和密钥文件的拒绝、目录到 index.html 的跳转、MIME 判定都在 Core 的 htmlPreview.site / htmlPreview.resolveRequest,共享 fixture 随 Core 大哥这是要一己之力做一个 idea 编辑器吗 #42 先合入;平台层只读文件、交给 web view 和绘制工具栏
  • [N/A] 修改平台层已有的可共享逻辑时已迁入 Core:没有修改已有的可共享逻辑
  • 工具或运行时专属的知识放在插件或成熟上游,宿主和 Core 没有按工具名加分支

性能与内存 / Performance and memory

  • web view 只在显示 HTML 文件的页面时才打开,隐藏的页面计入已有的 previewPagesKept(6)上限,关闭文件或项目时释放;没有新增轮询(编辑后的更新是 150 毫秒的一次性延时);单个文件读取上限 64 MiB,交给页面的文本上限 8 MiB
  • 主线程没有文件读写:站点查询经 a.background 在后台执行、结果回到界面线程应用,页面请求的 Core 解析和文件读取在 goroutine 里完成;交给页面的是当前文档文本,不跨 C ABI 传整个工作区
  • [N/A] 性能基线数据:没有改动启动、打开项目、输入、滚动、大文件、长输出或搜索路径,没有改动 Viva 编辑器模块;没有新增依赖或运行时

设置 / Settings

N/A:没有新增或修改设置项、设置页或对话框选项。

插件契约 / Plugin contract

  • [N/A] 契约改动已在 Rust Core PR 里随执行它的代码一起提交:Core 大哥这是要一己之力做一个 idea 编辑器吗 #42 新增的是编辑器命令(htmlPreview.*),不属于插件契约、能力版本或插件函数协议
  • scripts/desktop/build-desktop-core.sh 的 PINNED_CORE_REVISION 已升到 f88bc49,桌面端测试经 scripts/desktop/desktop-go.sh 读到的就是这份 Core 和它的 editor/html-preview-v1.json fixture
  • [N/A] 修改插件后端协议的,已运行 sdk-e2e.mjs:没有修改插件后端协议
  • [N/A] 新增或修改宿主方法、宿主通知:没有
原计划编号 方法 用途
— 无 本 PR 没有新增或修改宿主方法与通知

发行包与资源 / Bundle and resources

  • 运行时不向已安装的 app bundle 或 Windows 安装目录写入:HTML 页面使用 Viva 的临时(ephemeral)存储,Cookie、localStorage、缓存都不落盘;页面只读工作区文件,不写文件。已运行 ./scripts/desktop/verify/verify-runtime-bundle-immutability.sh
  • [N/A] 同步资源复用清单:没有新增或修改下载、解压、生成或缓存资源

无用代码与重构 / Dead code and refactoring


验证 / Verification

命令或操作 平台 结果
scripts/desktop/desktop-go.sh test -race ./...(LITHE_CORE_RUST_DIR 指向 659805e,与 f88bc49 树相同) macOS arm64 55 个包全部 ok
scripts/desktop/desktop-go.sh test ./internal/workbench ./internal/webpreview ./internal/core -run 'Viewer|SVG|HTML|Site|ImageFit|ImageCanvas|Checkerboard|Serves|Navigation|ReadFile' -count=1(固定的 f88bc49) macOS arm64 ok
gofmt -l(改动的 Go 文件) macOS 无输出
测试稳定性检查(develop-lithe 要求的两种模式) macOS 通过
node scripts/i18n/generate-i18n.mjs --check、node scripts/i18n/verify-app-language-localization.mjs macOS 通过
./scripts/platform-feature-matrix/verify-platform-feature-matrix.sh、verify-platform-feature-matrix-change.sh origin/preview HEAD macOS 通过
./scripts/agent-notes/verify-agent-notes.sh macOS 通过
./scripts/desktop/verify/verify-runtime-bundle-immutability.sh macOS 通过
Viva 固定版本校验(validate-pins) macOS 通过
插件归属自查命令 macOS 结果为空
Core #42:cargo fmt --check、cargo clippy、cargo test -p lithe-project(85)、cargo test -p lithe-core(790) macOS arm64 全部通过

新增测试:

  • workbench:TestHTMLFileShowsItsPageBesideTheSource、TestHTMLFileIsTextWhereItsPageCannotOpen、TestHTMLFileIsTextWithoutAWebEngine
  • webpreview:TestSiteServesWhatCoreResolvesAndTheFileFromTheEditor、TestSiteKeepsTheMainFrameOnTheSite、TestSiteWindowsLoadInPlaceOrGoToFollowAtMostOncePerInterval、TestSiteShowsEachTextAndKeepsItsScroll、TestSitePageTakesNoMessageButItsPosition、TestSitePathDecodesAddressesOfTheSiteOnly
  • core:TestHTMLPreviewSharedFixture

未能执行的检查和原因:

  • macOS 整机验收(在运行的应用里打开 HTML 文件,检查三态切换、实时更新、工具栏、站外链接、403 和深浅主题、中英文截图)没有做:当前环境没有界面驱动,再开一个实例会和正在使用的 Lithe 共用偏好存储。标记 ready 前补做并附截图。
  • Windows 实机验证没有做:需要确认 WebView2 101 以下打不开时只有 HTML 文件改为源码、以 / 开头的绝对路径的现状,以及临时 profile 目录的残留情况。

提交前确认 / Final checks

  • 分支从最新的 preview 创建;目标分支:feat、fix、docs 为 preview,release 以发布流程要求为准(基于 059d878;之后的 fix(desktop): 卸载插件前等待构建、VCS 和调试会话退出 #1921 没有改动本 PR 的文件)
  • 以 Draft 提交,本地验证通过后才标记 ready
  • 没有无关改动,没有提交 .build/、target/、dist/、.artifacts/ 等产物,没有凭据、个人路径和未脱敏的日志或截图
  • 本次构建、测试或预览启动的 Lithe 应用和子进程已全部关闭,没有进程残留(只运行了测试,没有启动应用)

HTML、HTM、XHTML 文件和 SVG 一样有源码 / 两者 / 仅预览三种模式。页面在单独的 lithe-site 协议和临时存储里打开,每个请求由 Core 的 htmlPreview.resolveRequest 决定;编辑后约 150 毫秒重新加载并回到原滚动位置。工具栏有后退、前进、重新加载和在浏览器中打开。打不开站点页时只有 HTML 文件退回纯文本。
htmlPreview.site 与 htmlPreview.resolveRequest 的期望改由 Core 的
rust/fixtures/editor/html-preview-v1.json 给出,桌面端测试与 Core 测试读同一份。
PINNED_CORE_REVISION 升到 Lithe-core-rust#42 合并 main(#41)后的 659805e。
#42 已 squash 合入 main,合入提交与原分支 659805e 的树相同。
@ghfind-review ghfind-review Bot added the review: high ghfind author score; see https://ghfind.com label Nov 14, 2026
@xiaoyumuxi
xiaoyumuxi marked this pull request as ready for review November 14, 2026 12:24
@xiaoyumuxi
xiaoyumuxi merged commit 2a75223 into preview Nov 14, 2026
6 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: high ghfind author score; see https://ghfind.com

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant