diff --git a/.github/workflows/bug-server-pr-bundle.yml b/.github/workflows/bug-server-pr-bundle.yml index fa86e3e01..9dbe19e59 100644 --- a/.github/workflows/bug-server-pr-bundle.yml +++ b/.github/workflows/bug-server-pr-bundle.yml @@ -9,6 +9,7 @@ permissions: jobs: build-pr-bundle: + if: github.event.pull_request.head.repo.full_name != github.repository runs-on: ubuntu-latest timeout-minutes: 30 steps: diff --git a/.github/workflows/bug-server.yml b/.github/workflows/bug-server.yml index 384fa2d81..bfdc2a810 100644 --- a/.github/workflows/bug-server.yml +++ b/.github/workflows/bug-server.yml @@ -5,7 +5,7 @@ on: workflow_dispatch: inputs: pr_number: - description: 'PR number to test (including fork PRs)' + description: 'Fork PR number to test' required: true type: string head_sha: @@ -22,9 +22,10 @@ permissions: jobs: build: - if: github.event_name != 'workflow_dispatch' + if: >- + github.event_name == 'push' || + (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) runs-on: ubuntu-latest - steps: - uses: actions/checkout@v4 - name: Use Node.js 24.x diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 1044918ea..1ccef0117 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -22,7 +22,7 @@ importers: specifier: ~0.5.7 version: 0.5.7 '@visactor/vrender': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../packages/vrender '@visactor/vutils': specifier: ~1.0.12 @@ -95,7 +95,7 @@ importers: ../../packages/react-vrender: dependencies: '@visactor/vrender': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../vrender '@visactor/vutils': specifier: ~1.0.12 @@ -168,10 +168,10 @@ importers: ../../packages/react-vrender-utils: dependencies: '@visactor/react-vrender': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../react-vrender '@visactor/vrender': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../vrender '@visactor/vutils': specifier: ~1.0.12 @@ -241,16 +241,16 @@ importers: ../../packages/vrender: dependencies: '@visactor/vrender-animate': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../vrender-animate '@visactor/vrender-components': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../vrender-components '@visactor/vrender-core': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../vrender-core '@visactor/vrender-kits': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../vrender-kits devDependencies: '@internal/bundler': @@ -320,7 +320,7 @@ importers: ../../packages/vrender-animate: dependencies: '@visactor/vrender-core': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../vrender-core '@visactor/vutils': specifier: ~1.0.12 @@ -393,13 +393,13 @@ importers: ../../packages/vrender-components: dependencies: '@visactor/vrender-animate': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../vrender-animate '@visactor/vrender-core': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../vrender-core '@visactor/vrender-kits': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../vrender-kits '@visactor/vscale': specifier: ~1.0.12 @@ -518,7 +518,7 @@ importers: specifier: 2.4.1 version: 2.4.1 '@visactor/vrender-core': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../vrender-core '@visactor/vutils': specifier: ~1.0.12 @@ -649,19 +649,19 @@ importers: ../../tools/bugserver-trigger: dependencies: '@visactor/vrender': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../../packages/vrender '@visactor/vrender-animate': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../../packages/vrender-animate '@visactor/vrender-components': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../../packages/vrender-components '@visactor/vrender-core': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../../packages/vrender-core '@visactor/vrender-kits': - specifier: workspace:1.1.11 + specifier: workspace:1.1.12 version: link:../../packages/vrender-kits devDependencies: '@internal/bundler': diff --git a/common/config/rush/version-policies.json b/common/config/rush/version-policies.json index 20f8b2bf1..ef5b55c90 100644 --- a/common/config/rush/version-policies.json +++ b/common/config/rush/version-policies.json @@ -1 +1 @@ -[{"definitionName":"lockStepVersion","policyName":"vrenderMain","version":"1.1.11","nextBump":"patch"}] +[{"definitionName":"lockStepVersion","policyName":"vrenderMain","version":"1.1.12","nextBump":"patch"}] diff --git a/docs/package.json b/docs/package.json index 6fd1203a7..3530cd1a1 100644 --- a/docs/package.json +++ b/docs/package.json @@ -13,7 +13,7 @@ "@visactor/vchart": "1.3.0", "@visactor/vutils": "~1.0.12", "@visactor/vgrammar": "~0.5.7", - "@visactor/vrender": "workspace:1.1.11", + "@visactor/vrender": "workspace:1.1.12", "markdown-it": "^13.0.0", "highlight.js": "^11.8.0", "axios": "^1.4.0", diff --git a/docs/superpowers/plans/2026-09-17-bug-server-pr-routing.md b/docs/superpowers/plans/2026-09-17-bug-server-pr-routing.md new file mode 100644 index 000000000..b0c45da5e --- /dev/null +++ b/docs/superpowers/plans/2026-09-17-bug-server-pr-routing.md @@ -0,0 +1,36 @@ +# Bug Server PR 来源分流实现计划 + +**目标:** 消除同一 PR 在 Bug Server CI 与 Bug Server PR Bundle 中的重复构建。 + +**架构:** 按 PR head 仓库完整名称与当前仓库名称比较,在两个构建 job 上设置互斥条件。仓库内 PR 自动构建和测试,fork PR 构建产物后由维护者通过现有手动入口提交测试。 + +**技术栈:** GitHub Actions、actionlint、Node.js、Python。 + +## 约束 + +- 基于最新 `develop` 创建 `codex/bugserver-pr-routing`,向 `develop` 提交 PR。 +- PR 目标分支维持 `main`、`develop`、`dev/**`,保留 `main` push 和手动入口。 +- 维持现有构建命令、Node.js 24、Ubuntu runner、权限和产物校验边界。 +- 分流采用 job 级条件;另一构建 job 显示 `skipped`,不启动 runner。 + +## 任务 1:修改 workflow 与说明 + +文件:`.github/workflows/bug-server.yml`、`.github/workflows/bug-server-pr-bundle.yml`、`tools/bugserver-trigger/README.md`、`docs/superpowers/specs/2026-09-17-bug-server-dispatch-design.md`。 + +- [x] 将 CI 的 `build.if` 改为 `github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository)`。 +- [x] 为 `build-pr-bundle` 增加 `if: github.event.pull_request.head.repo.full_name != github.repository`。 +- [x] 说明手动入口依赖 fork PR 的 bundle,仓库内 PR 应重跑 CI;同步输入描述和执行边界。 + +## 任务 2:验证与提交 + +- [x] 读取实际 YAML 条件,校验仓库内 PR、外部 fork PR、同组织不同仓库 PR、`main` push、手动触发共 5 种场景。 +- [x] 运行 `node --test .github/scripts/bug-server-dispatch.test.cjs`、`python3 -B -m unittest discover -s .github/scripts -p 'test_extract_bug_server_bundle.py'`、两个 workflow 的 actionlint、Prettier 与 `git diff --check`。 + +提交阶段通过仓库推送钩子,创建面向 `develop` 的 PR,并核对 PR 的分支及线上检查启动状态;发布结果记录在 PR 说明中。 + +## 本地验证结果 + +- 5 种事件场景通过,PR 目标分支过滤和手动提交依赖保持正确。 +- Node 来源校验测试 36 项、Python ZIP 校验测试 6 项全部通过。 +- actionlint 1.7.12、Prettier 和 `git diff --check` 通过。 +- 未修改构建命令和测试客户端,本次不重复完整构建。线上回归结果以 PR 检查为准。 diff --git a/docs/superpowers/specs/2026-09-17-bug-server-dispatch-design.md b/docs/superpowers/specs/2026-09-17-bug-server-dispatch-design.md index 603c95698..498ccbcb0 100644 --- a/docs/superpowers/specs/2026-09-17-bug-server-dispatch-design.md +++ b/docs/superpowers/specs/2026-09-17-bug-server-dispatch-design.md @@ -6,13 +6,21 @@ ## 数据流与权限边界 -1. `bug-server-pr-bundle.yml` 仅由 `pull_request` 触发。在只读仓库权限、无持久化 checkout 凭据、无 Bug Server token 的 runner 上检出准确 head SHA,执行 Rush 构建。缓存写入作用域属于该 PR,不属于默认分支。产物名为 `bug-server-pr--`,保留 7 天。 +1. `bug-server-pr-bundle.yml` 仅在外部 fork 的 `pull_request` 中执行构建。在只读仓库权限、无持久化 checkout 凭据、无 Bug Server token 的 runner 上检出准确 head SHA,执行 Rush 构建。缓存写入作用域属于该 PR,不属于默认分支。产物名为 `bug-server-pr--`,保留 7 天。 2. `bug-server.yml` 的手动入口只允许默认分支,保留 `pr_number`、`head_sha`。可信脚本校验输入、base 仓库及 PR 当前 head,再从指定 PR bundle workflow 查找成功运行。 3. 来源校验绑定 workflow ID/路径、事件、运行状态、base/head 仓库 ID、源分支及 run head SHA。fork 的运行记录可能没有 PR 列表,不能因此拒绝所有外部 PR;如列表存在则还需匹配 PR 编号。 4. 选择最新匹配运行中唯一且未过期的命名产物,复核 artifact API 的 run ID、仓库 ID 与 SHA。失败时要求先成功运行 PR bundle 工作流,不回退到其他提交或较旧运行。 5. 提交 job 只检出 `github.workflow_sha` 对应的可信脚本。通过 artifact ID 下载 ZIP,只接受一个名为 `index.js` 的普通文件,最大 64 MiB。可信 Python 脚本只把文件字节写入固定位置,不按 ZIP 路径解压,不执行产物。 6. 可信 TypeScript 客户端的依赖独立安装且禁用 lifecycle scripts。仅最后的 API 调用 step 注入 `BUG_SERVER_TOKEN`;PR 元数据与产物来源由可信校验 job 提供。summary 记录 PR、SHA 和来源构建。 -7. 两个 workflow 默认 `contents: read`;查询 PR 需要 `pull-requests: read`,查询/下载 artifact 需要 `actions: read`。原有 push / pull_request 自动 Bug Server 步骤保持原来的构建和测试行为。 +7. 两个 workflow 默认 `contents: read`;查询 PR 需要 `pull-requests: read`,查询/下载 artifact 需要 `actions: read`。`main` push 和仓库内 PR 保留自动构建和测试。 + +## 按 PR 来源分流 + +对于目标分支为 `main`、`develop`、`dev/**` 的 PR,比较 `github.event.pull_request.head.repo.full_name` 与 `github.repository`:相等时仅执行 **Bug Server CI** 的 `build` job,不相等时仅执行 **Bug Server PR Bundle** 的 `build-pr-bundle` job。每个 PR 事件只实际构建一次;同组织的其他仓库也属于外部来源。 + +`pull_request` 事件过滤器不能按来源仓库过滤,因此使用 job 级 `if`。两个 workflow 仍可能出现运行记录,但另一构建 job 会显示 `skipped`,不启动 runner。`main` push 继续自动构建和测试;手动入口仍然只校验并提交 fork PR 的已有产物。仓库内 PR 需要重测时重跑 CI,不再生成手动入口需要的 bundle。 + +验证仓库内 PR、外部 fork PR、同组织不同仓库 PR、`main` push 和手动触发的执行矩阵,并运行 actionlint、既有 Node/Python 测试及仓库提交、推送检查。 ## 维护者操作变化 diff --git a/packages/react-vrender-utils/CHANGELOG.json b/packages/react-vrender-utils/CHANGELOG.json index 5770acb6f..83f42aa5b 100644 --- a/packages/react-vrender-utils/CHANGELOG.json +++ b/packages/react-vrender-utils/CHANGELOG.json @@ -1,6 +1,12 @@ { "name": "@visactor/react-vrender-utils", "entries": [ + { + "version": "1.1.12", + "tag": "@visactor/react-vrender-utils_v1.1.12", + "date": "Tue, 13 Oct 2026 08:17:49 GMT", + "comments": {} + }, { "version": "1.1.11", "tag": "@visactor/react-vrender-utils_v1.1.11", diff --git a/packages/react-vrender-utils/CHANGELOG.md b/packages/react-vrender-utils/CHANGELOG.md index 6b2f3c14a..94ccfe291 100644 --- a/packages/react-vrender-utils/CHANGELOG.md +++ b/packages/react-vrender-utils/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log - @visactor/react-vrender-utils -This log was last generated on Thu, 17 Sep 2026 07:31:09 GMT and should not be manually modified. +This log was last generated on Tue, 13 Oct 2026 08:17:49 GMT and should not be manually modified. + +## 1.1.12 +Tue, 13 Oct 2026 08:17:49 GMT + +_Version update only_ ## 1.1.11 Thu, 17 Sep 2026 07:31:09 GMT diff --git a/packages/react-vrender-utils/package.json b/packages/react-vrender-utils/package.json index e92c5a256..afb12c8dd 100644 --- a/packages/react-vrender-utils/package.json +++ b/packages/react-vrender-utils/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/react-vrender-utils", - "version": "1.1.11", + "version": "1.1.12", "description": "", "sideEffects": false, "main": "cjs/index.js", @@ -27,8 +27,8 @@ "react-dom": "^18.2.0" }, "dependencies": { - "@visactor/vrender": "workspace:1.1.11", - "@visactor/react-vrender": "workspace:1.1.11", + "@visactor/vrender": "workspace:1.1.12", + "@visactor/react-vrender": "workspace:1.1.12", "@visactor/vutils": "~1.0.12", "react-reconciler": "^0.29.0", "tslib": "^2.3.1" diff --git a/packages/react-vrender/CHANGELOG.json b/packages/react-vrender/CHANGELOG.json index 72105fcdf..a42683918 100644 --- a/packages/react-vrender/CHANGELOG.json +++ b/packages/react-vrender/CHANGELOG.json @@ -1,6 +1,12 @@ { "name": "@visactor/react-vrender", "entries": [ + { + "version": "1.1.12", + "tag": "@visactor/react-vrender_v1.1.12", + "date": "Tue, 13 Oct 2026 08:17:48 GMT", + "comments": {} + }, { "version": "1.1.11", "tag": "@visactor/react-vrender_v1.1.11", diff --git a/packages/react-vrender/CHANGELOG.md b/packages/react-vrender/CHANGELOG.md index 702541820..9474877cd 100644 --- a/packages/react-vrender/CHANGELOG.md +++ b/packages/react-vrender/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log - @visactor/react-vrender -This log was last generated on Thu, 17 Sep 2026 07:31:09 GMT and should not be manually modified. +This log was last generated on Tue, 13 Oct 2026 08:17:49 GMT and should not be manually modified. + +## 1.1.12 +Tue, 13 Oct 2026 08:17:48 GMT + +_Version update only_ ## 1.1.11 Thu, 17 Sep 2026 07:31:09 GMT diff --git a/packages/react-vrender/package.json b/packages/react-vrender/package.json index 23e0d0404..3e1536308 100644 --- a/packages/react-vrender/package.json +++ b/packages/react-vrender/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/react-vrender", - "version": "1.1.11", + "version": "1.1.12", "description": "", "sideEffects": false, "main": "cjs/index.js", @@ -26,7 +26,7 @@ "react": "^18.2.0" }, "dependencies": { - "@visactor/vrender": "workspace:1.1.11", + "@visactor/vrender": "workspace:1.1.12", "@visactor/vutils": "~1.0.12", "react-reconciler": "^0.29.0", "tslib": "^2.3.1" diff --git a/packages/vrender-animate/CHANGELOG.json b/packages/vrender-animate/CHANGELOG.json index ed7224b9a..dbea8dd6c 100644 --- a/packages/vrender-animate/CHANGELOG.json +++ b/packages/vrender-animate/CHANGELOG.json @@ -1,6 +1,12 @@ { "name": "@visactor/vrender-animate", "entries": [ + { + "version": "1.1.12", + "tag": "@visactor/vrender-animate_v1.1.12", + "date": "Tue, 13 Oct 2026 08:17:49 GMT", + "comments": {} + }, { "version": "1.1.11", "tag": "@visactor/vrender-animate_v1.1.11", diff --git a/packages/vrender-animate/CHANGELOG.md b/packages/vrender-animate/CHANGELOG.md index 36b9167a7..4128853be 100644 --- a/packages/vrender-animate/CHANGELOG.md +++ b/packages/vrender-animate/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log - @visactor/vrender-animate -This log was last generated on Thu, 17 Sep 2026 07:31:09 GMT and should not be manually modified. +This log was last generated on Tue, 13 Oct 2026 08:17:49 GMT and should not be manually modified. + +## 1.1.12 +Tue, 13 Oct 2026 08:17:49 GMT + +_Version update only_ ## 1.1.11 Thu, 17 Sep 2026 07:31:09 GMT diff --git a/packages/vrender-animate/package.json b/packages/vrender-animate/package.json index 67f2c5cbb..7195ada1c 100644 --- a/packages/vrender-animate/package.json +++ b/packages/vrender-animate/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vrender-animate", - "version": "1.1.11", + "version": "1.1.12", "description": "", "sideEffects": false, "main": "cjs/index.js", @@ -37,7 +37,7 @@ }, "dependencies": { "@visactor/vutils": "~1.0.12", - "@visactor/vrender-core": "workspace:1.1.11" + "@visactor/vrender-core": "workspace:1.1.12" }, "devDependencies": { "@internal/bundler": "workspace:*", diff --git a/packages/vrender-components/CHANGELOG.json b/packages/vrender-components/CHANGELOG.json index 5f6345c1c..0480221f9 100644 --- a/packages/vrender-components/CHANGELOG.json +++ b/packages/vrender-components/CHANGELOG.json @@ -1,6 +1,12 @@ { "name": "@visactor/vrender-components", "entries": [ + { + "version": "1.1.12", + "tag": "@visactor/vrender-components_v1.1.12", + "date": "Tue, 13 Oct 2026 08:17:49 GMT", + "comments": {} + }, { "version": "1.1.11", "tag": "@visactor/vrender-components_v1.1.11", diff --git a/packages/vrender-components/CHANGELOG.md b/packages/vrender-components/CHANGELOG.md index 2e2859b60..0fe9877c3 100644 --- a/packages/vrender-components/CHANGELOG.md +++ b/packages/vrender-components/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log - @visactor/vrender-components -This log was last generated on Thu, 17 Sep 2026 07:31:09 GMT and should not be manually modified. +This log was last generated on Tue, 13 Oct 2026 08:17:49 GMT and should not be manually modified. + +## 1.1.12 +Tue, 13 Oct 2026 08:17:49 GMT + +_Version update only_ ## 1.1.11 Thu, 17 Sep 2026 07:31:09 GMT diff --git a/packages/vrender-components/package.json b/packages/vrender-components/package.json index f07e58d9c..2e2e260a1 100644 --- a/packages/vrender-components/package.json +++ b/packages/vrender-components/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vrender-components", - "version": "1.1.11", + "version": "1.1.12", "description": "components library for dp visualization", "sideEffects": false, "main": "cjs/index.js", @@ -69,9 +69,9 @@ "dependencies": { "@visactor/vutils": "~1.0.12", "@visactor/vscale": "~1.0.12", - "@visactor/vrender-core": "workspace:1.1.11", - "@visactor/vrender-kits": "workspace:1.1.11", - "@visactor/vrender-animate": "workspace:1.1.11" + "@visactor/vrender-core": "workspace:1.1.12", + "@visactor/vrender-kits": "workspace:1.1.12", + "@visactor/vrender-animate": "workspace:1.1.12" }, "devDependencies": { "@internal/bundler": "workspace:*", diff --git a/packages/vrender-core/CHANGELOG.json b/packages/vrender-core/CHANGELOG.json index 4c91b2a79..84c5fd828 100644 --- a/packages/vrender-core/CHANGELOG.json +++ b/packages/vrender-core/CHANGELOG.json @@ -1,6 +1,18 @@ { "name": "@visactor/vrender-core", "entries": [ + { + "version": "1.1.12", + "tag": "@visactor/vrender-core_v1.1.12", + "date": "Tue, 13 Oct 2026 08:17:49 GMT", + "comments": { + "none": [ + { + "comment": "Fix font variant and style measurements for wrapped, clipped and vertical text." + } + ] + } + }, { "version": "1.1.11", "tag": "@visactor/vrender-core_v1.1.11", diff --git a/packages/vrender-core/CHANGELOG.md b/packages/vrender-core/CHANGELOG.md index ea619f642..5b2f349c8 100644 --- a/packages/vrender-core/CHANGELOG.md +++ b/packages/vrender-core/CHANGELOG.md @@ -1,6 +1,13 @@ # Change Log - @visactor/vrender-core -This log was last generated on Thu, 17 Sep 2026 07:31:09 GMT and should not be manually modified. +This log was last generated on Tue, 13 Oct 2026 08:17:49 GMT and should not be manually modified. + +## 1.1.12 +Tue, 13 Oct 2026 08:17:49 GMT + +### Updates + +- Fix font variant and style measurements for wrapped, clipped and vertical text. ## 1.1.11 Thu, 17 Sep 2026 07:31:09 GMT diff --git a/packages/vrender-core/__tests__/unit/graphic/text-font-layout.test.ts b/packages/vrender-core/__tests__/unit/graphic/text-font-layout.test.ts new file mode 100644 index 000000000..9a2572d27 --- /dev/null +++ b/packages/vrender-core/__tests__/unit/graphic/text-font-layout.test.ts @@ -0,0 +1,120 @@ +import { application } from '../../../src/application'; +import type { IGraphicUtil, TextOptionsType } from '../../../src/interface'; +import { ATextMeasure } from '../../../src/core/contributions/textMeasure/AtextMeasure'; +import { DefaultGraphicService } from '../../../src/graphic/graphic-service/graphic-service'; +import { createText } from '../../../src/graphic/text'; +import { createWrapText } from '../../../src/graphic/wrap-text'; +import { createGroup } from '../../../src/graphic/group'; + +describe('text layout uses the rendered font', () => { + const graphicUtil = application.graphicUtil; + const graphicService = application.graphicService; + + beforeEach(() => { + let font: TextOptionsType = {}; + // 使用确定的字体宽度隔离宿主字体差异,仍经过真实 ATextMeasure 的截断和测量算法。 + const context = { + setTextStyleWithoutAlignBaseline: (options: TextOptionsType) => (font = options), + measureText: (text: string) => { + const advance = font.fontVariant === 'small-caps' ? 20 : font.fontStyle === 'italic' ? 15 : 10; + const width = text.length * advance; + return { + width, + actualBoundingBoxAscent: 16, + actualBoundingBoxDescent: 4, + fontBoundingBoxAscent: 16, + fontBoundingBoxDescent: 4 + }; + } + }; + const measure = new ATextMeasure(); + measure.configure({ context, bindTextMeasure: (): void => undefined } as unknown as IGraphicUtil, 'browser'); + application.graphicUtil = { getTextMeasureInstance: () => measure } as unknown as IGraphicUtil; + application.graphicService = new DefaultGraphicService(); + }); + + afterEach(() => { + application.graphicUtil = graphicUtil; + application.graphicService = graphicService; + }); + + function wrapped(attributes = {}, create = createText) { + const text = create({ + text: 'abcdefgh', + fontFamily: 'Arial', + fontSize: 20, + lineHeight: 20, + whiteSpace: 'normal', + wordBreak: 'break-all', + maxLineWidth: 55, + ...attributes + }); + return text; + } + + test.each([createText, createWrapText])('wraps small-caps at the available width (%#)', create => { + const text = wrapped({ fontVariant: 'small-caps' }, create); + expect(text.multilineLayout.lines.map(line => line.str)).toEqual(['ab', 'cd', 'ef', 'gh']); + expect(text.multilineLayout.lines.map(line => line.width)).toEqual([40, 40, 40, 40]); + }); + + test('measures explicit lines with the configured font', () => { + const text = createText({ text: ['abcd', 'ab'], fontSize: 20, fontVariant: 'small-caps' }); + expect(text.multilineLayout.lines.map(line => line.width)).toEqual([80, 40]); + }); + + test('clips a single line including its suffix using the configured font', () => { + const text = createText({ + text: 'abcdefgh', + fontSize: 20, + fontVariant: 'small-caps', + maxLineWidth: 100, + ellipsis: '...' + }); + expect(text.clipedText).toBe('ab...'); + expect(text.clipedWidth).toBe(100); + }); + + test.each([createText, createWrapText])('invalidates wrapped layout when the variant changes (%#)', create => { + const text = wrapped({}, create); + expect(text.multilineLayout.lines.map(line => line.str)).toEqual(['abcde', 'fgh']); + text.setAttribute('fontVariant', 'small-caps'); + expect(text.multilineLayout.lines.map(line => line.str)).toEqual(['ab', 'cd', 'ef', 'gh']); + text.setAttributes({ fontVariant: '' }); + expect(text.multilineLayout.lines.map(line => line.str)).toEqual(['abcde', 'fgh']); + }); + + test.each([createText, createWrapText])('invalidates wrapped layout when the style changes (%#)', create => { + const text = wrapped({}, create); + expect(text.multilineLayout.lines.map(line => line.str)).toEqual(['abcde', 'fgh']); + text.setAttributes({ fontStyle: 'italic' }); + expect(text.multilineLayout.lines.map(line => line.str)).toEqual(['abc', 'def', 'gh']); + expect(text.multilineLayout.lines.map(line => line.width)).toEqual([45, 45, 30]); + }); + + test.each([createText, createWrapText])('recomputes layout when entering and leaving a font state (%#)', create => { + const text = wrapped({}, create); + text.states = { caps: { fontVariant: 'small-caps' } }; + expect(text.multilineLayout.lines.map(line => line.str)).toEqual(['abcde', 'fgh']); + text.useStates(['caps'], false); + expect(text.multilineLayout.lines.map(line => line.str)).toEqual(['ab', 'cd', 'ef', 'gh']); + text.useStates([], false); + expect(text.multilineLayout.lines.map(line => line.str)).toEqual(['abcde', 'fgh']); + }); + + test('inherits the font variant from the group theme while respecting an explicit override', () => { + const group = createGroup({}); + group.setTheme({ text: { fontVariant: 'small-caps' } }); + const inherited = wrapped(); + const overridden = wrapped({ fontVariant: '' }); + group.add(inherited); + group.add(overridden); + expect(inherited.multilineLayout.lines.map(line => line.str)).toEqual(['ab', 'cd', 'ef', 'gh']); + expect(overridden.multilineLayout.lines.map(line => line.str)).toEqual(['abcde', 'fgh']); + }); + + test('measures vertical Latin text with the configured font', () => { + const text = createText({ text: 'abcd', direction: 'vertical', fontSize: 20, fontVariant: 'small-caps' }); + expect(text.AABBBounds.height()).toBe(80); + }); +}); diff --git a/packages/vrender-core/package.json b/packages/vrender-core/package.json index e48d30767..6b338d04d 100644 --- a/packages/vrender-core/package.json +++ b/packages/vrender-core/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vrender-core", - "version": "1.1.11", + "version": "1.1.12", "description": "", "sideEffects": [ "./src/modules.ts", diff --git a/packages/vrender-core/src/graphic/text.ts b/packages/vrender-core/src/graphic/text.ts index cda8e5f4f..bf61d5caa 100644 --- a/packages/vrender-core/src/graphic/text.ts +++ b/packages/vrender-core/src/graphic/text.ts @@ -29,6 +29,8 @@ const TEXT_UPDATE_TAG_KEY = [ 'fontSize', 'fontFamily', 'fontWeight', + 'fontStyle', + 'fontVariant', 'ellipsis', 'lineHeight', 'direction', @@ -279,6 +281,8 @@ export class Text extends Graphic implements IText { textBaseline = textTheme.textBaseline, fontSize = textTheme.fontSize, fontWeight = textTheme.fontWeight, + fontStyle = textTheme.fontStyle, + fontVariant = textTheme.fontVariant, ellipsis = textTheme.ellipsis, maxLineWidth, stroke = textTheme.stroke, @@ -306,7 +310,12 @@ export class Text extends Graphic implements IText { return this._AABBBounds; } const textMeasure = application.graphicUtil.getTextMeasureInstance(this.textMeasureId || this.stage?.textMeasureId); - const layoutObj = new CanvasTextLayout(fontFamily, { fontSize, fontWeight, fontFamily, lineHeight }, textMeasure); + // 测量与绘制必须使用同一字体,否则 small-caps 等变体会越过换行宽度和包围盒。 + const layoutObj = new CanvasTextLayout( + fontFamily, + { fontSize, fontWeight, fontFamily, fontStyle, fontVariant, lineHeight }, + textMeasure + ); const layoutData = layoutObj.GetLayoutByLines( text, textAlign, @@ -352,6 +361,8 @@ export class Text extends Graphic implements IText { lineWidth = textTheme.lineWidth, wordBreak = textTheme.wordBreak, fontWeight = textTheme.fontWeight, + fontStyle = textTheme.fontStyle, + fontVariant = textTheme.fontVariant, // widthLimit, ignoreBuf = textTheme.ignoreBuf, measureMode = textTheme.measureMode, @@ -374,7 +385,7 @@ export class Text extends Graphic implements IText { } const textMeasure = application.graphicUtil.getTextMeasureInstance(this.textMeasureId || this.stage?.textMeasureId); - const textOptions = { fontSize, fontWeight, fontFamily, lineHeight }; + const textOptions = { fontSize, fontWeight, fontFamily, fontStyle, fontVariant, lineHeight }; const layoutObj = new CanvasTextLayout(fontFamily, textOptions, textMeasure as any); // layoutObj内逻辑 @@ -555,6 +566,8 @@ export class Text extends Graphic implements IText { fontFamily = textTheme.fontFamily, fontSize = textTheme.fontSize, fontWeight = textTheme.fontWeight, + fontStyle = textTheme.fontStyle, + fontVariant = textTheme.fontVariant, stroke = textTheme.stroke, lineWidth = textTheme.lineWidth, // wordBreak = textTheme.wordBreak, @@ -595,7 +608,7 @@ export class Text extends Graphic implements IText { const strEllipsis = (ellipsis === true ? textTheme.ellipsis : ellipsis) as string; const data = textMeasure.clipTextWithSuffixVertical( verticalData, - { fontSize, fontWeight, fontFamily }, + { fontSize, fontWeight, fontFamily, fontStyle, fontVariant }, maxLineWidth, strEllipsis, false, @@ -606,7 +619,7 @@ export class Text extends Graphic implements IText { } else { const data = textMeasure.clipTextVertical( verticalData, - { fontSize, fontWeight, fontFamily }, + { fontSize, fontWeight, fontFamily, fontStyle, fontVariant }, maxLineWidth, false ); @@ -621,7 +634,7 @@ export class Text extends Graphic implements IText { const w = t.direction === TextDirection.HORIZONTAL ? fontSize - : textMeasure.measureTextWidth(t.text, { fontSize, fontWeight, fontFamily }); + : textMeasure.measureTextWidth(t.text, { fontSize, fontWeight, fontFamily, fontStyle, fontVariant }); width += w; t.width = w; diff --git a/packages/vrender-core/src/graphic/wrap-text.ts b/packages/vrender-core/src/graphic/wrap-text.ts index b2c8ada94..4bb3a5909 100644 --- a/packages/vrender-core/src/graphic/wrap-text.ts +++ b/packages/vrender-core/src/graphic/wrap-text.ts @@ -51,6 +51,8 @@ export class WrapText extends Text { lineWidth = textTheme.lineWidth, wordBreak = textTheme.wordBreak, fontWeight = textTheme.fontWeight, + fontStyle = textTheme.fontStyle, + fontVariant = textTheme.fontVariant, // widthLimit, ignoreBuf = textTheme.ignoreBuf, heightLimit = 0, @@ -71,7 +73,11 @@ export class WrapText extends Text { } const textMeasure = application.graphicUtil.getTextMeasureInstance(this.textMeasureId || this.stage?.textMeasureId); - const layoutObj = new CanvasTextLayout(fontFamily, { fontSize, fontWeight, fontFamily }, textMeasure as any) as any; + const layoutObj = new CanvasTextLayout( + fontFamily, + { fontSize, fontWeight, fontFamily, fontStyle, fontVariant }, + textMeasure as any + ) as any; // layoutObj内逻辑 const lines = text.map(l => l.toString()) as string[]; diff --git a/packages/vrender-kits/CHANGELOG.json b/packages/vrender-kits/CHANGELOG.json index 57a14a7fc..dd0681514 100644 --- a/packages/vrender-kits/CHANGELOG.json +++ b/packages/vrender-kits/CHANGELOG.json @@ -1,6 +1,12 @@ { "name": "@visactor/vrender-kits", "entries": [ + { + "version": "1.1.12", + "tag": "@visactor/vrender-kits_v1.1.12", + "date": "Tue, 13 Oct 2026 08:17:49 GMT", + "comments": {} + }, { "version": "1.1.11", "tag": "@visactor/vrender-kits_v1.1.11", diff --git a/packages/vrender-kits/CHANGELOG.md b/packages/vrender-kits/CHANGELOG.md index 25eca1dde..bafa78c2c 100644 --- a/packages/vrender-kits/CHANGELOG.md +++ b/packages/vrender-kits/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log - @visactor/vrender-kits -This log was last generated on Thu, 17 Sep 2026 07:31:09 GMT and should not be manually modified. +This log was last generated on Tue, 13 Oct 2026 08:17:49 GMT and should not be manually modified. + +## 1.1.12 +Tue, 13 Oct 2026 08:17:49 GMT + +_Version update only_ ## 1.1.11 Thu, 17 Sep 2026 07:31:09 GMT diff --git a/packages/vrender-kits/package.json b/packages/vrender-kits/package.json index 463e0bf28..bdad14ed1 100644 --- a/packages/vrender-kits/package.json +++ b/packages/vrender-kits/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vrender-kits", - "version": "1.1.11", + "version": "1.1.12", "description": "", "sideEffects": false, "main": "cjs/index-node.js", @@ -38,7 +38,7 @@ }, "dependencies": { "@visactor/vutils": "~1.0.12", - "@visactor/vrender-core": "workspace:1.1.11", + "@visactor/vrender-core": "workspace:1.1.12", "@resvg/resvg-js": "2.4.1", "roughjs": "4.6.6", "gifuct-js": "2.1.2", diff --git a/packages/vrender/CHANGELOG.json b/packages/vrender/CHANGELOG.json index 6e610ed51..327fd8b39 100644 --- a/packages/vrender/CHANGELOG.json +++ b/packages/vrender/CHANGELOG.json @@ -1,6 +1,12 @@ { "name": "@visactor/vrender", "entries": [ + { + "version": "1.1.12", + "tag": "@visactor/vrender_v1.1.12", + "date": "Tue, 13 Oct 2026 08:17:49 GMT", + "comments": {} + }, { "version": "1.1.11", "tag": "@visactor/vrender_v1.1.11", diff --git a/packages/vrender/CHANGELOG.md b/packages/vrender/CHANGELOG.md index d4c98a15f..e66fe0ef6 100644 --- a/packages/vrender/CHANGELOG.md +++ b/packages/vrender/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log - @visactor/vrender -This log was last generated on Thu, 17 Sep 2026 07:31:09 GMT and should not be manually modified. +This log was last generated on Tue, 13 Oct 2026 08:17:49 GMT and should not be manually modified. + +## 1.1.12 +Tue, 13 Oct 2026 08:17:49 GMT + +_Version update only_ ## 1.1.11 Thu, 17 Sep 2026 07:31:09 GMT diff --git a/packages/vrender/package.json b/packages/vrender/package.json index 3c0c7563d..dc2d9f81e 100644 --- a/packages/vrender/package.json +++ b/packages/vrender/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vrender", - "version": "1.1.11", + "version": "1.1.12", "description": "", "sideEffects": true, "main": "cjs/index.js", @@ -32,10 +32,10 @@ "test-watch": "cross-env DEBUG_MODE=1 jest --watch -c jest.config.js" }, "dependencies": { - "@visactor/vrender-core": "workspace:1.1.11", - "@visactor/vrender-kits": "workspace:1.1.11", - "@visactor/vrender-animate": "workspace:1.1.11", - "@visactor/vrender-components": "workspace:1.1.11" + "@visactor/vrender-core": "workspace:1.1.12", + "@visactor/vrender-kits": "workspace:1.1.12", + "@visactor/vrender-animate": "workspace:1.1.12", + "@visactor/vrender-components": "workspace:1.1.12" }, "devDependencies": { "@internal/bundler": "workspace:*", diff --git a/tools/bugserver-trigger/README.md b/tools/bugserver-trigger/README.md index 6eafaa8f0..7c79fd80b 100644 --- a/tools/bugserver-trigger/README.md +++ b/tools/bugserver-trigger/README.md @@ -2,11 +2,25 @@ `scripts/trigger-test.ts` uploads `dist/index.js`, waits for an SCM build, starts the Bug Server photo tests, and waits for their results. It requires `BUG_SERVER_TOKEN`. -## Manually test a PR +## Automatic runs + +PRs targeting `main`, `develop` or `dev/**` are routed by their source repository, so each PR event builds only once: + +| Event | Bug Server CI | Bug Server PR Bundle | +| ------------------ | ---------------------------------- | ------------------------------------------------- | +| Same-repository PR | Build and submit tests | Skip the build | +| Fork PR | Skip the automatic build and tests | Build and save the artifact for manual submission | +| Push to `main` | Build and submit tests | Not triggered | + +The job conditions compare `github.event.pull_request.head.repo.full_name` with `github.repository`. GitHub cannot filter `pull_request` triggers by source repository, so both workflows may appear in Actions, but the other build job is skipped without starting a runner. + +To retest a same-repository PR, re-run **Bug Server CI**. The manual entry below uses fork PR artifacts; same-repository PRs no longer produce a **Bug Server PR Bundle** artifact. + +## Manually test a fork PR After the workflows are merged into the repository's default branch (`develop`), wait for **Bug Server PR Bundle** to succeed for the reviewed PR head. Fork runs may need a maintainer's approval. Then maintainers with repository write access can open **Actions → Bug Server CI → Run workflow**. Select **develop**, then enter: -- `pr_number`: the PR number, including PRs from external forks. +- `pr_number`: the number of a PR submitted to this repository from an external fork. - `head_sha`: the full 40-character SHA of the PR head that you reviewed. The equivalent CLI command is: @@ -27,11 +41,11 @@ The manual entry consumes an existing PR bundle; it does not build PR code. Arti ## Execution boundaries -1. **Bug Server PR Bundle** runs only on `pull_request`, builds the exact head with read-only repository permissions, disabled persisted checkout credentials and no Bug Server token. Any cache writes are confined to the PR scope. It uploads `bug-server-pr--`. +1. **Bug Server PR Bundle** builds only for fork `pull_request` events, using the exact head with read-only repository permissions, disabled persisted checkout credentials and no Bug Server token. Any cache writes are confined to the PR scope. It uploads `bug-server-pr--`. 2. The manual **resolve-manual-target** job validates the current PR head and source workflow ID/path, PR event, successful run, repository IDs, source branch and run SHA. It requires one non-expired artifact with matching GitHub API provenance. Fork runs can omit PR associations; the repository/branch/SHA checks still bind the source. 3. **submit-manual-bundle** uses scripts from the immutable default-branch workflow commit. It downloads the selected artifact ID and accepts only a single regular `index.js` entry, up to 64 MiB. The trusted extractor writes bytes to a fixed path without extracting archive paths. The client only uploads those bytes; it never executes the bundle or PR package scripts. -Both workflows default to `contents: read`. Manual lookup and download jobs also need `actions: read`, and target validation needs `pull-requests: read`. The Bug Server token is injected only into the final API client step. Existing push and pull-request automatic runs retain their build and test behavior with read-only repository permissions. A fork PR's automatic Bug Server run still cannot obtain repository secrets; use the manual entry for Bug Server validation. +Both workflows default to `contents: read`. Manual lookup and download jobs also need `actions: read`, and target validation needs `pull-requests: read`. The Bug Server token is injected only into the final API client step. Pushes to `main` and same-repository PRs retain their automatic build and test behavior with read-only repository permissions. Fork PRs automatically build only the artifact; use the manual entry for Bug Server validation. The default-branch manual workflow does not check out or build PR code. This replaces the earlier `cache-mode` approach and does not require scanner exceptions. diff --git a/tools/bugserver-trigger/package.json b/tools/bugserver-trigger/package.json index f8efe9134..7ae572dcb 100644 --- a/tools/bugserver-trigger/package.json +++ b/tools/bugserver-trigger/package.json @@ -8,11 +8,11 @@ "ci": "ts-node --transpileOnly --skipProject ./scripts/trigger-test.ts" }, "dependencies": { - "@visactor/vrender": "workspace:1.1.11", - "@visactor/vrender-core": "workspace:1.1.11", - "@visactor/vrender-kits": "workspace:1.1.11", - "@visactor/vrender-components": "workspace:1.1.11", - "@visactor/vrender-animate": "workspace:1.1.11" + "@visactor/vrender": "workspace:1.1.12", + "@visactor/vrender-core": "workspace:1.1.12", + "@visactor/vrender-kits": "workspace:1.1.12", + "@visactor/vrender-components": "workspace:1.1.12", + "@visactor/vrender-animate": "workspace:1.1.12" }, "devDependencies": { "@rushstack/eslint-patch": "~1.1.4",