Skip to content

Image Markup 2.0.0: document session, highlighter, step numbers, redaction, export - #68

Merged
nwparker merged 13 commits into
mainfrom
nwparker/image-markup-v2
Nov 3, 2026
Merged

nwparker merged 13 commits into
mainfrom
nwparker/image-markup-v2

Conversation

@nwparker

@nwparker nwparker commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

ELI5

Image Markup 2.0.0 moves onto Orca's new editor APIs and adds the tools people mark up screenshots with most: a highlighter, numbered steps, and a real redaction tool. It can also export a copy as a new PNG.

Safe to merge before the Orca release. The catalog lists 2.0.0 (needs Orca 1.4.248) ahead of 1.0.2 and 1.0.0 (1.4.245), and Orca installs the newest release its version can run, so current users keep 1.0.2 until they update Orca.

What the user sees

Before (1.0.2)

  • Pen, arrow, box, ellipse and text only.
  • Covering a password with a box did not remove it: the saved file keeps the original image inside, so the secret was still in the file. The README told people to use another tool.
  • If something else rewrote the image while it was open (say, an agent taking a new screenshot to the same path), markup that had not reached Orca yet was silently dropped.
  • Copy only explained that it could not copy, and there was no way to save a separate file.

After (2.0.0)

  • Highlighter (H): a wide, see-through stroke. Hold Shift for a straight line along text.
  • Step number (N): each click places the next numbered disc. Deleting one renumbers the rest.
  • Redact (X): drag over a secret to pixelate it into coarse, noisy tiles. On save the pixels are removed from the visible image and from the original kept inside the file. The redaction is not kept as an editable shape. Until you save you can still move it or undo it.
  • Export: saves a copy with the markup as a new PNG through Orca's save dialog (desktop app). The open file is not touched.
  • When the file changes outside the editor, markup Orca had not received yet is moved onto the new image and stays unsaved, with a toast that says so.
  • Orca marks the tab unsaved, saves it, and waits for the latest stroke to be encoded before saving. A refused save keeps the markup unsaved instead of looking saved.

Highlighter, steps, redaction and an arrow, dark theme
The same, light theme
The original kept inside the saved file: the key is redacted there too

Mechanism

  • Sync: the hand-written version tracking, echo detection and resend-after-conflict code is replaced by orca.document.session({ decode, encode, merge }) in the new build/src/document-sync.js. Encoding an image is asynchronous (a full-size render and PNG encode), and the session's encode must be synchronous. So the editor encodes first, then calls change with the finished file, and encode just hands that file over. The editor also answers Orca's flush itself, so a save right after a stroke waits for that stroke.
  • Merge: merge(base, outside, mine) keeps the outside file's image and shapes, adds the shapes this editor drew since base, and drops the ones it deleted. Shapes are matched by their saved form. A merge cannot encode, so it carries the outside file plus a stale flag, and the editor encodes it and sends it as its next change. Strokes still waiting for the debounce merge the same way.
  • Redaction: before a save, redactions are drawn into the original and that redacted original is the one stored. If there is no other markup, the file is just the redacted image, with no hidden data.
  • Copy: Orca main still refuses the async Clipboard API in plugin frames, and execCommand('copy') carries text only. So in the desktop app the toolbar shows Export instead of Copy. Copy comes back without a plugin update if Orca allows it.
  • engines.orca is now >=1.4.248, the first release with the document session.

Why this approach

  • Session value: it holds the encoded file, not just the scene. The alternative was to encode inside encode, but that needs a synchronous PNG encoder, which would block the frame for seconds on large screenshots.
  • Redaction: it burns pixels into the kept original instead of keeping an editable shape. An editable redaction would have to keep the secret in the file, which defeats the point.
  • Export: it uses Orca's save dialog rather than a clipboard stand-in such as HTML with an embedded image, which nothing an agent reads can paste.
  • Crop: not in this release. It changes the image size under every other shape and the merge, so it should get its own change.

Testing

  • npm test in build/: 67 tests pass.
    • New Node tests run document-sync.js against Orca's real session through mockEditorHost.
    • The browser suite now runs Orca's real orca.document script, copied from Orca main. It covers an agent rewrite under unsent markup, a conflict merge, a stale edit stopping the save, every new tool, and that the kept original has no unredacted pixels.
  • orca plugin check passes. orca plugin test passes the 33 Node tests; it cannot load the Playwright suite, which imports playwright-core.
  • Live, on an Orca build of main (aaf4a5d) with its own user-data folder, run hidden:
    • orca plugin shot --editor --act drew every new tool in both themes.
    • Cmd+S saved through Orca, and the saved file reopens with editable shapes and a redacted original.
    • An outside write to a clean tab shows the new image.
  • scripts/release-entry.mjs, scripts/check-catalog.mjs and the tools tests pass.

Known limit

When the tab already has unsaved markup and the file changes on disk, Orca shows its Reload/Overwrite banner instead of calling the editor's merge. Orca's merge path reads the disk as text only, so it does not run for byte editors. Nothing is lost silently: the banner asks.

Risk

Medium: this changes how user images are saved.

  • The session code is shared Orca code with its own tests.
  • The file format is unchanged, so 1.x files open in 2.0.0 and 2.0.0 files open in 1.x. Exception: a 2.0.0 file's step numbers and highlights are dropped by 1.x's loader, but the visible image still shows them.
  • To back out, revert to 1.0.2.

orca-marked-dark

orca-marked-light

orca-saved-kept-original

…ction, export

Saving runs on Orca's document session, so Orca owns the unsaved state and saves, and an
outside write merges markup onto the new image instead of dropping it. Adds Highlighter, Step
number and Redact tools (redactions are burned into the kept original), and Export through
Orca's save dialog. Needs Orca 1.4.248.
- Markup drawn before a redaction is burned into the kept original with it, so no text or
  stroke under a redaction survives in the saved scene.
- Redactions are a flat fill of the area's mean colour, hatched, instead of a reversible mosaic.
- EXIF-rotated JPEGs keep their markup and original on reopen.
- Markup merged onto a new image during an encode is still sent.
- The first edit marks the tab unsaved at once, so closing during an encode asks first.
- Step numbers are renumbered after a merge.
- An undo back to the handed-over scene sends that real file again, so a save never writes
  the stand-in.
- The stand-in is the last file with the latest shapes in its markup, marked to be redrawn: if
  Orca saves it anyway (say, after a view switch mid-encode), it reopens with every shape and
  is encoded again.
- With a redaction in the scene the stand-in is a note only: an orCp PNG chunk, or a JPEG APP9
  segment the pixel CRC skips, so the markup stays readable.
- Redaction scenes get the redraw stand-in too, with the last file's own original and no shape
  drawn before the last redaction; reopening applies the redaction again at once.
- Opening a saved stand-in without a redaction no longer dirties the tab; the next edit redraws.
- A stand-in note that reached disk is stripped on decode, so later saves don't keep it.
@nwparker
nwparker merged commit 259adc3 into main Nov 3, 2026
1 check passed
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