Skip to content

feat(html): the pdf annotator reports its changes and takes a tool press - #906

Merged
andiwand merged 2 commits into
mainfrom
feat/annotation-host-api
Sep 19, 2026
Merged

andiwand merged 2 commits into
mainfrom
feat/annotation-host-api

Conversation

@andiwand

@andiwand andiwand commented Sep 19, 2026

Copy link
Copy Markdown
Member

Why

Each host adds a script over odr.annotation that it should not need: the Android app (editing-bridge.js, opendocument-app/OpenDocument.droid#662), the iOS app (DocumentViewController.pageMessageBridge and pressMarker), and the website (frame-bridge.js). The three copies do the same two things:

  1. They count the pending marks after every pointerup, pointercancel and selectionchange, and wait 120 ms because a mark settles 50 ms after the pointer lifts. The reason is that the annotator has no change callback.
  2. They decide what a tool button does. Only the page can see the selection, so this logic had to be in a script in the page.

What

  • odr.onAnnotationChange({count}) fires on each change to what is pending: a mark, a finished stroke, an undo, a removal and a clear. It does not fire while a stroke is drawn, or when the count does not change.
  • odr.annotation.press(tool, {color, width}):
    • Over a selection (with a tool that is not ink), the tool marks the selection once in the colour given, clears the selection, and no tool stays armed.
    • With no selection, the press arms the tool, or disarms it if it is armed already. null disarms.
    • It returns the tool that stays armed.
  • odr.annotation.recolor(tool, style): over a selection it marks it once, the same as press. With no selection it restyles the tool if that tool is armed. It returns the tool that stays armed.

The behaviour is the same as the behaviour of the host copies. One detail is different: a mark made once does not change the colour that is set for the armed tool.

Not in this PR

  • The callback wiring stays in each host (odr.onEditChange = …, and now odr.onAnnotationChange = …). The host must hook into the callbacks, because the core cannot know the transport (addJavascriptInterface, WKScriptMessageHandler or postMessage). A single odr.connect(post) could take all the callbacks, but a pdf page does not carry editing.js, so no one script could hold it. A second API for the same callbacks gives a host nothing.
  • markOnSelection stays false by default. Every host sets it with one setOptions call.

Test

test/browser/annotation/tests.html: 22 new checks, run in headless Chrome. The result is 63 checks and 0 failures. On main, the result is 41 checks and 0 failures.

andiwand and others added 2 commits September 19, 2026 09:23
A host had to count the pending marks after every pointer event, because
odr.annotation had no callback. The count settles 50ms after the pointer
lifts, so each host also waited on a timer. odr.onAnnotationChange({count})
now reports each change: a mark, a finished stroke, an undo, a removal and
a clear.

odr.annotation.press(tool, style) and recolor(tool, style) do what a tool
button does. Over a selection, the tool marks it once and no tool stays
armed. With no selection, press arms or disarms the tool, and recolor
restyles the armed tool. The Android app, the iOS app and the website each
had their own copy of this logic.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H3sFjPT7zd8fvmpqhznzEP
`press` and `recolor` tested the selection for text and then disarmed
the tool, even when no page held the selection and nothing was marked.
Now they disarm only when the mark is made, so such a press arms the
tool.

The comments and the changelog entry are shorter.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C725vurM2ZqBY6ddV2FMZ7
@andiwand
andiwand merged commit 5682b30 into main Sep 19, 2026
33 checks passed
@andiwand
andiwand deleted the feat/annotation-host-api branch September 19, 2026 07:35
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