Skip to content

Slides: hit-test text-bearing shapes and pin ctx transform#330

Merged
hackerwins merged 2 commits into
mainfrom
fix/slides-text-shape-hit-test
Jun 1, 2026
Merged

Slides: hit-test text-bearing shapes and pin ctx transform#330
hackerwins merged 2 commits into
mainfrom
fix/slides-text-shape-hit-test

Conversation

@hackerwins

Copy link
Copy Markdown
Collaborator

Summary

Two coupled bugs prevented selecting PPTX-imported text placeholder shapes (type:'shape', kind:'rect', data.text set, no fill / no stroke). On the originally-reported deck every text click landed on the back-most full-bleed background image instead.

  • hitShape visibility gate ignored data.text. PR Slides: edit text inside shapes (PowerPoint / Google Slides parity) #321 folded inline text into ShapeElement.data.text but didn't update the hit-test side, so text-only shapes were silently unclickable. Fixed by extending the gate to include hasText (with the same OPEN_PATH_KINDS exclusion the fill branch carries, so bracket / brace shapes with text fall through to the stroke-band test rather than false-positive-ing across an auto-closed C / U interior).
  • SlideRenderer leaves the ctx with scale((hostWidth / SLIDE_WIDTH) * dpr) after each paint. Path2D variants of isPointInPath / isPointInStroke interpret the path through the active transform while the query (lx, ly) stays in canvas-pixel space, so interior clicks in logical coords landed outside every shape's scaled-down footprint. Browser instrumentation confirmed the running matrix was a = d ≈ 0.267. The unit-test canvas stub ignores transforms, which masked it. Fixed by routing all four hitTestSlide call sites through a new SlidesEditorImpl.hitTestAt helper that pins the ctx to identity around the hit-test and restores on exit.

Bundled in this PR: a vitest 3 → ^4.1.8 bump across the workspace to clear GHSA-5xrq-8626-4rwp (the vitest UI critical advisory). scripts/verify-entropy.mjs:267 fails the pre-push lane on any critical, so the bug fix and the dep bump have to ship together.

Test plan

  • pnpm verify:fast green (slides 1,560 / docs 877 / sheets 1,279 / frontend 507 / backend 175 — all under vitest 4)
  • pnpm verify:self green (10 lanes, including verify:entropy now showing only 1 moderate + 1 high — the residual tmp / vitepress-vite advisories that aren't gated by the entropy threshold)
  • Browser smoke on the originally-reported share link: clicked the centres of the three text shapes on slide 1 (074860d4, 9d9b4528, b3921eaa) — all three select correctly via editor.selection.get(). Pre-fix, all three clicks landed on the back-most full-bleed background image (7cb34fec).
  • Regression tests:
    • hits a text-only shape (no fill, no stroke, has data.text)
    • does not hit an empty shape (no fill, no stroke, no text)
    • does not hit OPEN_PATH_KINDS interior even when shape has text

🤖 Generated with Claude Code

hackerwins and others added 2 commits June 1, 2026 23:51
Two coupled bugs prevented selecting PPTX-imported placeholder shapes
(type:'shape', kind:'rect', data.text set, no fill / no stroke). On
the originally-reported deck every text click landed on the back-most
full-bleed background image instead.

1. hitShape's visibility gate rejected any shape without fill or
   stroke. PR #321 folded inline text into ShapeElement.data.text
   but didn't update the hit-test side, so text-only shapes were
   silently unclickable on their visible body.

2. SlideRenderer leaves the canvas ctx with scale((hostWidth /
   SLIDE_WIDTH) * dpr) after each paint. Path2D variants of
   isPointInPath interpret the path commands through the active
   transform while the query (lx, ly) stays in canvas-pixel space,
   so interior clicks in logical coords landed well outside every
   shape's scaled-down footprint. The unit-test canvas stub ignores
   transforms entirely, which is why this latent bug went unnoticed.

Add hasText to the gate (with the same OPEN_PATH_KINDS exclusion the
fill branch carries, so bracket / brace shapes with text fall through
to the stroke-band test instead of false-positive-ing across an auto-
closed C / U interior), and wrap all four hitTestSlide call sites in
a new hitTestAt helper that pins the ctx transform to identity for
the duration of the hit-test. The wrapper also resets cleanly on the
test stub, which exposes save / restore / setTransform as no-ops.

Adds three regression tests in hit-test-elements.test.ts: text-only
shape hits via bbox interior, empty shape misses, and OPEN_PATH_KINDS
shape with text still does not hit on its auto-closed interior.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
GHSA-5xrq-8626-4rwp (Vitest UI server can read + execute arbitrary
files when listening) hit vitest <4.1.0 across 22 transitive paths.
`scripts/verify-entropy.mjs:267` fails the pre-push lane on any
critical advisory, blocking unrelated work.

Bump direct `vitest` and `@vitest/coverage-v8` deps in every package
that pins them (`sheets`, `docs`, `slides`, `frontend`, `cli`) from
^3.1.x to ^4.1.8. pnpm hoist resolves transitive vitest to the new
major across the lockfile.

Two carry-over fixes for the v4 type changes:

- `packages/{sheets,docs}/vite.config.ts` now import `defineConfig`
  from `vitest/config` instead of `vite`. Vite's own `UserConfigExport`
  no longer carries the `test:` key in @vitest/runtime v4, so the
  config-side import must come from vitest.
- `packages/slides/src/view/canvas/ctx-spy.ts` swaps every
  `ReturnType<typeof vi.fn>` slot for a callable `Mock<(...args: any[])
  => any>` alias. v4 widened the default `vi.fn()` return to
  `Mock<Procedure | Constructable>`, which TypeScript treats as a
  union of call + construct signatures and refuses to invoke without
  disambiguation.

Other findings — high `tmp` (transitive via @nestjs/cli) and moderate
`vite` (transitive via vitepress) — are not gated by the entropy
threshold and remain to be addressed separately.

verify:self green: 10 lanes pass in 107s. verify:entropy now reports
"Vulnerabilities: 1 moderate, 1 high" only.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@hackerwins, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 51 minutes and 58 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b9f8b75d-674b-49d0-9f74-8957a66431ec

📥 Commits

Reviewing files that changed from the base of the PR and between b8032bc and 93a9025.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (15)
  • docs/tasks/README.md
  • docs/tasks/active/20260601-slides-shape-text-hit-test-lessons.md
  • docs/tasks/active/20260601-slides-shape-text-hit-test-todo.md
  • packages/cli/package.json
  • packages/docs/package.json
  • packages/docs/vite.config.ts
  • packages/frontend/package.json
  • packages/sheets/package.json
  • packages/sheets/vite.config.ts
  • packages/slides/package.json
  • packages/slides/src/view/canvas/ctx-spy.ts
  • packages/slides/src/view/editor/editor.ts
  • packages/slides/src/view/editor/element-hit.ts
  • packages/slides/test/view/editor/hit-test-elements.test.ts
  • packages/tokens/package.json
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/slides-text-shape-hit-test

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Verification: verify:self

Result: ✅ PASS in 151.4s

Lane Status Duration
tokens:build ✅ pass 1.3s
sheets:build ✅ pass 8.5s
docs:build ✅ pass 8.2s
slides:build ✅ pass 9.8s
verify:fast ✅ pass 94.0s
frontend:build ✅ pass 12.5s
verify:frontend:chunks ✅ pass 0.2s
backend:build ✅ pass 3.3s
cli:build ✅ pass 1.3s
verify:entropy ✅ pass 12.1s

Verification: verify:integration

Result: ✅ PASS

@codecov

codecov Bot commented Jun 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@hackerwins
hackerwins merged commit 5d53136 into main Jun 1, 2026
4 checks passed
@hackerwins
hackerwins deleted the fix/slides-text-shape-hit-test branch June 1, 2026 15:26
@hackerwins hackerwins mentioned this pull request Jun 7, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant