Conversation
There was a problem hiding this comment.
🟡 Changes recommended
There are confirmed correctness issues in text-fragment directive decoding and in locator range fallback behavior when a domRange is present but invalid.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR adds support for resolving locator highlights via locations.domRange and WICG :~:text= text-fragment directives, including a shared helper for extracting the directive and a vendored/trimmed Google polyfill implementation to work with detached DOMs.
Changes:
- Add
getTextFragment()to the shared HTML locator extensions and accompanying unit tests. - Vendor and export text-fragment matching/generation utilities in
@readium/helpers, plus a directive decoder. - Update navigator locator-to-range resolution to prefer
domRangeand:~:text=fragments before falling back to quote anchoring, and adjust build/test wiring for the new helpers dependency.
File summaries
| File | Description |
|---|---|
| shared/vite.config.js | Marks @readium/helpers as external for shared build output. |
| shared/test/html/Locator.test.ts | Adds tests validating getTextFragment() extraction behavior. |
| shared/src/publication/html/Locations.ts | Adds getTextFragment() that decodes :~:text= directives from locations.fragments. |
| shared/package.json | Adds workspace dependency on @readium/helpers. |
| shared/jest.config.cjs | Maps @readium/helpers to local source for Jest runs. |
| pnpm-lock.yaml | Records the new workspace dependency linkage. |
| navigator/vite.config.js | Adds @readium/helpers to Rollup externals for navigator build. |
| navigator-html-injectables/src/helpers/locator.ts | Resolves ranges via domRange and text fragments before quote anchoring. |
| helpers/src/vendor/text-fragments-polyfill/textFragmentMatcher.ts | Adds vendored/ported matcher logic for resolving directives to DOM Ranges. |
| helpers/src/vendor/text-fragments-polyfill/textFragmentGenerator.ts | Adds vendored/ported generator logic for creating directives from Ranges. |
| helpers/src/vendor/text-fragments-polyfill/README.MD | Documents provenance and trimming/porting choices of the vendored polyfill. |
| helpers/src/vendor/text-fragments-polyfill/LICENSE | Includes upstream Apache-2.0 license text. |
| helpers/src/textFragmentDirective.ts | Adds directive decoding helper used by shared locator extensions. |
| helpers/src/index.ts | Re-exports directive and vendored polyfill APIs from helpers package entrypoint. |
Review details
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
- Files reviewed: 9/14 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
🔵 Needs a closer look
There are confirmed runtime-failure/fallback-blocking edge cases (invalid CSS selectors and doc.body assumptions) plus a compatibility concern (new Range()) that should be addressed before merge.
Review details
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
navigator-html-injectables/src/helpers/locator.ts:16
- resolveTextSearchRoot can throw on an invalid css selector and unconditionally falls back to doc.body, which can be null/undefined for detached or non-HTML documents; either case prevents later fallback strategies from running.
navigator-html-injectables/src/helpers/locator.ts:23 - resolveDomRangePoint uses doc.querySelector(point.cssSelector) without guarding against invalid selectors; a DOMException here escapes to the outer try/catch and prevents falling back to text-fragment or text-quote anchoring.
- Files reviewed: 9/14 changed files
- Comments generated: 0 new
- Review effort level: Lite
This adds support for
locations.domRangeandlocations.fragmentsinDecoratormodule, as well as a helper to extract the text fragment inshared.Note Google’s polyfill is vendored due to it assuming
document.body, which is an issue with a detached DOM.