Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions docs/decisions/annotation-pen-style.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Annotation Pen Style

Status: accepted
Date: 2026-04-07
Context:

- `rsnap` uses the pen tool for screenshot annotation, not for professional drawing.
- Users prefer a polished handwritten annotation look over faithful reproduction of every mouse
wobble.
- The governing behavior contract for this decision is `docs/spec/annotation-pen.md`.
- Earlier iterations exposed the wrong tradeoff:
- raw-point fidelity preserved tiny dents and uneven curvature that made rough circles and arcs
look amateurish
- segmented capsule or dense-dab rendering produced visible scalloping, gaps, or jagged preview
- auto-closing loops and shape guessing changed user intent too aggressively for general
annotation strokes
- The product goal is therefore "make the mark look better than the hand drew it" while keeping
the stroke recognizably human and fast enough for live annotation.

Decision:

- Treat the pen as an annotation stylizer, not a faithful brush.
- Optimize the final stroke for visual quality even when that means discarding small input
deviations.
- Keep the implementation layered:
- use a light online stroke model during drag so preview remains smooth and reasonably consistent
with the final stroke
- apply a stronger finalize pass on release to suppress micro-wobbles and improve curvature
continuity
- Default the finalize pass to preserve the large-scale path while aggressively removing
high-frequency local dents, bumps, and shallow reversals.
- Prefer an open freehand annotation model over generic shape inference or auto-closing behavior.
- Keep the live interaction and the finalized result within the same visual family so the product
feels like a polished annotation pen instead of a post-hoc shape replacement tool.

Alternatives considered:

- Preserve raw input as the main source of truth.
- Rejected because it retains hand jitter and produces visibly uneven annotation curves.
- Smooth only after mouse release with no online modeling.
- Rejected because preview quality remains poor and the final stroke changes too abruptly on
release.
- Auto-close near-loops or infer shapes such as circles and checks.
- Rejected because annotation strokes are varied and the correction often overshoots user intent.
- Render the stroke as segmented capsules or dense visible dabs.
- Rejected because segment boundaries and scalloping are noticeable in `egui` preview.
- Keep increasing generic smoothing passes without explicitly targeting micro-wobbles.
- Rejected because it softens the whole stroke without reliably eliminating the small dents users
actually notice.

Consequences:

- Future tuning should bias toward stronger beautification, not higher input fidelity.
- "Small" defects should be interpreted relative to annotation scale, especially brush width and
short local span, rather than as fixed absolute pixels.
- Changes that preserve tiny dents, shallow notches, or uneven arc curvature are regressions even
if they are more faithful to the pointer path.
- If a future implementation needs to become more precise, precision should be an explicit mode,
not the default annotation behavior.
- New work on the pen should prefer:
- micro-wobble suppression
- curvature-continuity improvements
- preview/final consistency
- subtle online prediction or stabilization
- New work on the pen should avoid by default:
- generic shape inference
- auto-closing loops
- segmented visible stroke primitives
- raw-path fidelity as a success metric
4 changes: 2 additions & 2 deletions docs/decisions/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,5 @@ Then keep the body decision-oriented:

## Current decision records

No durable decision records have been written yet. Add one here when a tradeoff needs to remain
discoverable beyond commit history and code comments.
- `docs/decisions/annotation-pen-style.md` for the pen-tool tradeoff that prioritizes polished
screenshot annotation over faithful pointer-path reproduction
126 changes: 126 additions & 0 deletions docs/spec/annotation-pen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# rsnap Annotation Pen Contract

Purpose: Define the normative behavior contract for the Frozen-mode pen tool used for screenshot
annotation.

Status: normative

Read this when: You are implementing, reviewing, tuning, or validating pen-tool behavior in
Frozen mode, including preview, commit, undo/redo, and export.

Not this document: Design rationale for why annotation beautification is preferred over pointer
fidelity, or current implementation notes. Use
`docs/decisions/annotation-pen-style.md` for rationale and `docs/reference/` for implementation
context.

Defines:
- the product goal and required behavior for Frozen-mode pen strokes
- preview, commit, export, and undo/redo invariants for pen annotations
- prohibited default behaviors for generic shape inference and automatic loop closure

## Scope

This contract applies to the `FrozenToolbarTool::Pen` path after a capture has entered Frozen
mode.

This contract governs:

- stroke preview while the pointer is down
- stroke finalization when the pointer is released
- committed annotation rendering in the frozen surface
- annotation export behavior for copy and save flows
- undo and redo behavior for committed pen strokes

This contract does not require:

- professional illustration or precision drawing behavior
- automatic conversion into canonical shapes such as circles, rectangles, arrows, or checks
- exact reproduction of the pointer path

## Product objective

The pen tool is an annotation stylizer, not a faithful freehand brush.

Required product objective:

- The pen tool MUST prioritize producing visually polished screenshot annotations over preserving
every small deviation in the input path.
- The resulting mark SHOULD look recognizably hand-drawn, but better than the raw pointer motion.
- Small high-frequency wobble, dents, shallow reversals, and uneven curvature SHOULD be treated as
disposable noise when removing them improves the overall mark.
- Large-scale stroke direction, openness, and endpoint intent MUST remain recognizable.

## Required behavior

### Availability and mode boundary

- The pen tool applies only in Frozen mode.
- The pen tool annotates the current frozen capture and does not modify live-mode selection flow.
- Pen annotations are part of the frozen capture state and therefore participate in preview, copy,
save, and undo/redo.

### Preview and commit consistency

- Drag-time preview MUST already use a beautified stroke path family rather than raw pointer
segments.
- Pointer-release finalization MAY apply stronger beautification than drag-time preview.
- Pointer-release finalization MUST remain in the same geometric family as preview and MUST NOT
replace the stroke with a materially different shape character.
- The user MUST NOT see a low-quality jagged preview that becomes the first acceptable result only
after release.

### Beautification contract

- Pen beautification MUST prefer final visual quality over raw pointer fidelity.
- The beautification pass MUST suppress small local defects when they are inconsistent with the
surrounding stroke trend.
- The beautification pass MUST improve curvature continuity for arcs and rounded marks rather than
only smoothing positions point-by-point.
- "Small" defects MUST be interpreted relative to annotation scale, especially stroke width and
short local span, rather than as a fixed absolute pixel threshold.
- The default tuning SHOULD be aggressive enough that rough circles, arcs, and check-like marks
look intentionally smooth without requiring the user to draw like a professional illustrator.

### Open-stroke semantics

- Pen strokes MUST remain open by default.
- The implementation MUST NOT auto-close near-loops by default.
- The implementation MUST NOT infer or replace generic shapes by default.
- If future precision or shape-assisted behavior is added, it MUST be an explicit mode or user
action rather than the default pen behavior.

### Endpoint and large-scale path intent

- Finalized strokes MUST preserve endpoint intent.
- Finalized strokes MAY move endpoints slightly only when required to preserve visible continuity,
but they MUST NOT materially relocate the apparent start or end of the mark.
- Finalized strokes MUST preserve the large-scale path trend even when local jitter is discarded.

### Rendering and export

- On-screen pen rendering MUST appear continuous and anti-aliased.
- Exported pen rendering MUST appear continuous and anti-aliased.
- The committed export path used by clipboard copy and save MUST include the same committed pen
annotations visible in Frozen mode.
- Export MUST NOT omit committed pen strokes and MUST NOT render them with a different stroke
family from the committed on-screen result.

### Undo and redo

- Undo and redo MUST operate on committed pen strokes.
- Undo and redo MUST round-trip the committed beautified stroke, not the raw pointer samples.
- Re-export after undo or redo MUST reflect the current committed stroke set exactly.

## Explicit non-goals for the default pen behavior

- exact pointer-path fidelity
- generic shape recognition
- auto-closing loops
- exposing every tiny dent or wobble in the raw hand motion
- professional drawing precision as the default interaction goal

## Governing rationale

The accepted rationale for this contract lives in:

- `docs/decisions/annotation-pen-style.md`
6 changes: 5 additions & 1 deletion docs/spec/capture-session.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Defines:
- capture-session entry, live-mode, frozen-mode, and export invariants
- hovered-window, region-selection, and fullscreen fallback behavior
- the current macOS scroll-capture contract
- the presence of Frozen-mode annotation state in session output

This repository contains a pure-Rust screenshot prototype targeting macOS first, with a
cross-platform architecture.
Expand Down Expand Up @@ -61,6 +62,8 @@ cross-platform architecture.
- `Space` -> copy the frozen cropped PNG (region/window/fullscreen) to the system clipboard, then exit
- On macOS, the frozen toolbar may expose `Recognize Text`, which runs Apple Vision OCR on the current frozen capture, copies the recognized text to the clipboard, and exits
- Cmd+S (macOS) / Ctrl+S -> save the frozen cropped PNG to disk, then exit
- In Frozen mode, toolbar-driven annotations are part of the frozen capture state; the pen-tool
contract lives in `docs/spec/annotation-pen.md`
- Esc -> cancel and exit without copying
- After a dragged-region freeze enters Frozen mode, dragging inside the bright region
repositions the frozen capture rect without resizing it and keeps it on the same monitor
Expand Down Expand Up @@ -195,5 +198,6 @@ Research and cross-platform notes live in:

## Current non-goals

- Annotation/editor UI, pinning, and advanced editing tools.
- Rich annotation/editor tooling beyond the current frozen toolbar tools, pinning, and advanced
editing workflows.
- Cross-monitor selection and cross-monitor window capture behavior.
2 changes: 2 additions & 0 deletions docs/spec/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ Then keep the body explicit:

## Current specs

- `docs/spec/annotation-pen.md` for the Frozen-mode pen-tool behavior contract and beautification
invariants
- `docs/spec/capture-session.md` for the capture-flow and scroll-capture behavior contract
- `docs/spec/performance.md` for render cadence, performance scenarios, metrics,
thresholds, and known performance-contract gaps
Loading
Loading