Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ jobs:
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm typecheck
- run: pnpm test:release
- run: pnpm format:check
- run: pnpm build
- run: pnpm exec playwright install --with-deps chromium
Expand Down
25 changes: 9 additions & 16 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,21 @@ jobs:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.release.tag_name || github.sha }}
fetch-depth: 0
- uses: pnpm/action-setup@v6
with:
version: 11.6.0
- uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm
- name: Validate stable release version
- name: Validate stable release version and tagged package source
env:
RELEASE_TAG: ${{ inputs.release_tag || github.event.release.tag_name }}
run: |
node --input-type=module <<'NODE'
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
const { version } = JSON.parse(readFileSync('package.json', 'utf8'));
assert.match(version, /^\d+\.\d+\.\d+$/);
assert.equal(process.env.RELEASE_TAG, `v${version}`);
NODE
run: node scripts/verify-release-source.mjs
- run: pnpm install --frozen-lockfile
- run: pnpm typecheck
- run: pnpm test:release
- run: pnpm format:check
- run: pnpm exec playwright install --with-deps
- run: pnpm test
Expand All @@ -54,7 +49,10 @@ jobs:
- uses: actions/upload-artifact@v7
with:
name: npm-tarball
path: artifacts/*.tgz
path: |
artifacts/*.tgz
artifacts/release.json
artifacts/publish-release.mjs
if-no-files-found: error
retention-days: 7

Expand All @@ -76,9 +74,4 @@ jobs:
name: npm-tarball
path: artifacts
- name: Publish the verified tarball through npm trusted publishing
run: |
set -eu
set -- artifacts/*.tgz
[ "$#" -eq 1 ]
[ -f "$1" ]
npm publish "$PWD/$1" --ignore-scripts --access public --provenance --tag latest
run: node artifacts/publish-release.mjs artifacts/release.json
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Changelog

## 0.3.0

- Preserve unsaved Office edits when updating read-only mode or close-button visibility; serialize correlated mode changes without rebuilding the iframe.
- Retry editor readiness negotiation for slow startup and retain legacy editable compatibility; stream legacy chunks with cancellation and acknowledgement backpressure.
- Support ordinary HTTP hosts without `crypto.randomUUID`.
- Make PDF canvases responsive and cap per-page backing pixels/DPR; add opt-in virtual rendering with bounded active canvases and explicit cancellation lifetime.
- Expose the full custom element TypeScript API and safely identify constructors returned by manual registration.
- Remove the unused production ranui dependency and obsolete style transformer.
- Release demo-owned Blob URLs on replacement/disposal and explain the remote Office iframe privacy boundary.
- Verify release source inputs and exact artifact integrity; make matching-version publish retries idempotent.

## 0.2.0

- Restore bubbling, composed `load` and `error` events; emit only for the current operation.
Expand Down
71 changes: 55 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ or conversion server is required by this package.
## Install and automatic registration

```sh
npm install @ranui/preview@0.2.0
npm install @ranui/preview@0.3.0
```

```ts
Expand Down Expand Up @@ -53,20 +53,21 @@ The existing root namespace (`import { preview } from '@ranui/preview'`),

## API

| Attribute / property | Default | Behavior |
| --------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| `src` | unset | PDF, DOCX, PPTX, XLSX or XLS URL; setting it opens the preview |
| `baseUrl` / `baseurl` | `https://edit.chaxus.com` | Office editor deployment URL, including a path/query if needed |
| `readonly` | false | Opt-in Office read-only mode; empty attribute or property `true` enables it; `readonly="false"` disables it |
| `closeable` | enabled | Set `closeable="false"` to hide the close control and disable Escape dismissal |
| `label` | trigger text or “Preview file” | Accessible name of the trigger |
| Attribute / property | Default | Behavior |
| ---------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| `src` | unset | PDF, DOCX, PPTX, XLSX or XLS URL; setting it opens the preview |
| `baseUrl` / `baseurl` | `https://edit.chaxus.com` | Office editor deployment URL, including a path/query if needed |
| `readonly` | false | Opt-in Office read-only mode; empty attribute or property `true` enables it; `readonly="false"` disables it |
| `closeable` | enabled | Set `closeable="false"` to hide the close control and disable Escape dismissal |
| `pdfMode` / `pdf-mode` | `eager` | PDF rendering mode: all pages, or opt-in viewport rendering with `virtual` |
| `label` | trigger text or “Preview file” | Accessible name of the trigger |

`src`, `label`, `baseUrl` and `closeable` retain their string property contracts;
`readonly` is a boolean property. Use attributes before registration completes.
`readonly` is a boolean property; `pdfMode` accepts `eager` or `virtual`. Use attributes before registration completes.
Call `showPreview()` or `closePreview()` after upgrading the element.

`load` fires once when all PDF pages finish painting or the Office editor confirms
opening. `error` fires once on a download, unsupported format or renderer error;
`load` fires once when all PDF pages finish painting (default), the initial viewport
and buffer finish painting (`pdf-mode="virtual"`), or the Office editor confirms opening. `error` fires once on a download, unsupported format or renderer error;
its `detail` contains the error. Both events bubble and cross shadow boundaries.
Replacing the source, closing or disconnecting cancels the active operation and
suppresses its obsolete events. Each element owns its dialog, iframe and worker.
Expand All @@ -87,10 +88,39 @@ messages only from that iframe and its configured origin.
Existing deployments using the `RENDER_OFFICE` chunk protocol remain supported
in the default editable mode. An old renderer without the embed API cannot
promise enforced read-only mode: requesting it returns an explicit error asking
you to update the deployment. Updating `readonly` while open reloads the file in
the requested mode. This controls editing in the renderer; it does not prevent
you to update the deployment. Updating `readonly` while open uses
`document:set-readonly` without reloading the file, preserving unsaved edits. Changing
`closeable` only updates the dialog control. Mode-change errors emit `error` while
leaving the existing editor available. This controls editing in the renderer; it does not prevent
users from downloading or copying bytes they already have access to.

## PDF rendering and memory

PDF canvases resize with their container. Backing stores are capped at DPR 2,
4,194,304 pixels per page and 8,192 pixels per dimension; unusually large pages
trade resolution for bounded allocation. Default `eager` mode still paints every
page before `load`, so total canvas memory grows with page count.

For long documents, set `<r-preview pdf-mode="virtual" src="/long.pdf">`.
Virtual mode maintains page-height placeholders and renders the viewport plus a
buffer, retaining at most six canvases. Scrolling and container resizing repaint
as needed. Its PDF worker remains alive until the preview closes, its source
changes or the element disconnects. Errors after initial `load` emit `error`.

`document.createElement('r-preview')` exposes the full TypeScript element API.
Manual registration may return an unrelated constructor when a host already owns
the tag; narrow its result with `isPreviewConstructor` before constructing a viewer:

```ts
import { registerPreview, isPreviewConstructor } from '@ranui/preview/manual';
const Preview = await registerPreview('app-file-preview');
if (isPreviewConstructor(Preview)) {
const element = new Preview();
element.pdfMode = 'virtual';
document.body.append(element);
}
```

## Intranet and offline deployment

- **PDF:** serve the installed package's complete `dist` directory. Its bundled
Expand All @@ -115,6 +145,10 @@ component makes no request to the remote Office editor.

## Compatibility and migration

0.3.0 preserves eager PDF completion and adds optional virtual rendering. UI and
read-only changes preserve the active Office editor. The unused production `ranui`
dependency and obsolete style transformer have been removed.

0.2.0 preserves automatic `r-preview` registration and existing URL attributes,
while adding manual registration and opt-in read-only behavior. It replaces
vendored PDF.js 3.2 with 6.4 and corrects page numbering, completion, failures and
Expand All @@ -134,17 +168,22 @@ pnpm exec playwright install
pnpm typecheck
pnpm format:check
pnpm test
pnpm test:release
pnpm build
pnpm test:package
```

Package verification installs the actual tarball in an isolated consumer and
checks strict TypeScript, manual SSR imports, public exports and PDF assets.
GitHub CI runs browser regressions and packaging checks. A stable GitHub release
`v0.2.0` runs the release checks and publishes the exact verified tarball through
npm trusted publishing. Configure `@ranui/preview` for repository `ranuts/fileview`,
`v0.3.0` runs the release checks and publishes the exact verified tarball through
npm trusted publishing. A release manifest records its SHA-512 integrity. Retrying
publishing skips an already published version only when registry integrity matches;
a mismatch fails. Manual retries verify the release tag's package inputs while
allowing workflow-only recovery fixes. Configure `@ranui/preview` for repository `ranuts/fileview`,
workflow `release.yml`, environment `npm`, and allow direct `npm publish`.

Live Office checks and release coverage: [verification report](docs/verification-0.2.0.md).
Live Office checks and release coverage: [0.3.0 verification report](docs/verification-0.3.0.md)
and [0.2.0 report](docs/verification-0.2.0.md).

See [CHANGELOG.md](CHANGELOG.md). Licensed under [AGPL-3.0](LICENSE).
64 changes: 50 additions & 14 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
## 安装和自动注册

```sh
npm install @ranui/preview@0.2.0
npm install @ranui/preview@0.3.0
```

```ts
Expand Down Expand Up @@ -51,19 +51,21 @@ vue({ template: { compilerOptions: { isCustomElement: (tag) => tag === 'app-file

## API

| 属性 | 默认值 | 行为 |
| --------------------- | --------------------------- | ----------------------------------------------------------------------------- |
| `src` | 无 | PDF、DOCX、PPTX、XLSX、XLS 文件 URL;赋值后打开预览 |
| `baseUrl` / `baseurl` | `https://edit.chaxus.com` | Office 编辑器页面地址,可以带路径和查询参数 |
| `readonly` | false | 显式启用 Office 只读模式;空属性或 JS 属性 true 启用,`readonly="false"` 关闭 |
| `closeable` | 启用 | `closeable="false"` 隐藏关闭按钮,同时禁用 Escape 关闭 |
| `label` | 触发元素文字或 Preview file | 触发元素的无障碍名称 |
| 属性 | 默认值 | 行为 |
| ---------------------- | --------------------------- | ----------------------------------------------------------------------------- |
| `src` | 无 | PDF、DOCX、PPTX、XLSX、XLS 文件 URL;赋值后打开预览 |
| `baseUrl` / `baseurl` | `https://edit.chaxus.com` | Office 编辑器页面地址,可以带路径和查询参数 |
| `readonly` | false | 显式启用 Office 只读模式;空属性或 JS 属性 true 启用,`readonly="false"` 关闭 |
| `closeable` | 启用 | `closeable="false"` 隐藏关闭按钮,同时禁用 Escape 关闭 |
| `pdfMode` / `pdf-mode` | `eager` | PDF 全页渲染,或显式设置 `virtual` 按可见区域渲染 |
| `label` | 触发元素文字或 Preview file | 触发元素的无障碍名称 |

`src`、`label`、`baseUrl`、`closeable` 保留原来的字符串属性约定;新增的
`readonly` 是布尔 JavaScript 属性。注册完成前可以使用 HTML 属性。
`readonly` 是布尔 JavaScript 属性;`pdfMode` 接受 `eager` 或 `virtual`。注册完成前可以使用 HTML 属性。
元素升级完成后可调用 `showPreview()` 和 `closePreview()`。

PDF 所有页面绘制完成,或 Office 编辑器确认打开成功后,触发一次 `load`。
默认 PDF 所有页面绘制完成、虚拟模式首屏及缓冲区域绘制完成,或 Office
编辑器确认打开成功后,触发一次 `load`。
下载、格式或编辑器错误触发一次 `error`,其 `detail` 为错误对象。
两个事件都可以冒泡并穿过 Shadow DOM。替换来源、关闭、移除元素会取消
当前操作,旧操作不会再发完成事件。每个实例独立管理弹窗、iframe 和 worker。
Expand All @@ -81,9 +83,36 @@ Office 渲染由 iframe 部署负责。当前

原来使用 `RENDER_OFFICE` 分块协议的自托管部署,在默认可编辑模式下继续
兼容。旧编辑器没有只读接口时,不能保证真正只读,因此请求只读会明确提示
升级部署。预览过程中修改 `readonly` 会按新的模式重新打开原文件。
升级部署。预览过程中修改 `readonly` 通过 `document:set-readonly`
更新当前编辑器,保留未保存的编辑内容;修改 `closeable` 只更新弹窗控件。
模式切换失败会触发 `error`,并保留原有编辑器。
只读约束的是编辑器的编辑行为,无法阻止拥有文件的用户下载或复制内容。

## PDF 渲染与内存

PDF 画布随容器宽度缩放。每页画布的 DPR 上限为 2,像素上限为 4,194,304,
单边上限为 8,192 像素。超大页面会降低分辨率以限制单页分配;默认 `eager`
模式仍在所有页面绘制完成后发出 `load`,总画布内存会随页数增加。

长文档可以设置 `<r-preview pdf-mode="virtual" src="/long.pdf">`。
虚拟模式保留页面高度占位,只绘制可见区域及缓冲区,同时最多保留六个画布;
滚动和容器尺寸变化会按需重绘。worker 保留到关闭预览、替换来源或移除元素。
首屏 `load` 之后的渲染失败仍会触发 `error`。

`document.createElement('r-preview')` 现在具有完整的 TypeScript 元素接口。
手动注册遇到已被宿主占用的标签时,可能返回其他组件的构造函数;创建实例前
可以使用 `isPreviewConstructor` 判断:

```ts
import { registerPreview, isPreviewConstructor } from '@ranui/preview/manual';
const Preview = await registerPreview('app-file-preview');
if (isPreviewConstructor(Preview)) {
const element = new Preview();
element.pdfMode = 'virtual';
document.body.append(element);
}
```

## 内网和离线部署

- **PDF:** 部署完整 `dist`。PDF.js API、worker 和
Expand All @@ -106,6 +135,9 @@ PDF 预览不会创建 Office iframe;仅注册组件、放置未打开的组

## 兼容与迁移

0.3.0 保留默认 PDF 全页完成语义,新增可选虚拟渲染。弹窗控件与只读切换
保留当前 Office 编辑器;移除了未使用的生产依赖 ranui 和旧样式转换插件。

0.2.0 保留自动注册和现有 URL 属性,新增可选手动注册和只读模式。PDF.js
由内置 3.2 升至 6.4,修复页码、重复页面、完成时机、失败处理和 worker 清理。
CI 验证当前 Chromium、Firefox、WebKit;新引擎不再沿用未经验证的
Expand All @@ -122,16 +154,20 @@ pnpm exec playwright install
pnpm typecheck
pnpm format:check
pnpm test
pnpm test:release
pnpm build
pnpm test:package
```

包验证会在隔离目录安装实际 tarball,检查严格 TypeScript、手动入口 SSR、
公开入口和 PDF 资产。GitHub CI 执行浏览器回归与包验证。
发布正式 GitHub Release `v0.2.0` 后,发布流程会验证并发布同一个 tarball。
npm 可信发布配置为包 `@ranui/preview`、仓库 `ranuts/fileview`、工作流
发布正式 GitHub Release `v0.3.0` 后,发布流程会验证并发布同一个 tarball。
发布清单记录 SHA-512 完整性;重试时只有 registry 的同版本完整性匹配才会
跳过发布,不匹配则失败。手动重试会核对 tag 的包输入文件,并允许仅工作流
修复的恢复操作。npm 可信发布配置为包 `@ranui/preview`、仓库 `ranuts/fileview`、工作流
`release.yml`、环境 `npm`,并允许直接 `npm publish`。

真实 Office 测试与覆盖边界见[验证报告](docs/verification-0.2.0.md)。
真实 Office 测试与覆盖边界见 [0.3.0 验证报告](docs/verification-0.3.0.md)
和 [0.2.0 报告](docs/verification-0.2.0.md)。

变更见 [CHANGELOG.md](CHANGELOG.md),许可证为 [AGPL-3.0](LICENSE)。
Loading
Loading