Skip to content

Document markup mode - #39

Open
tpavlu wants to merge 1 commit into
mainfrom
docs/markup-mode
Open

tpavlu wants to merge 1 commit into
mainfrom
docs/markup-mode

Conversation

@tpavlu

@tpavlu tpavlu commented Aug 12, 2026

Copy link
Copy Markdown

What changed and why

Markup mode ships in the plan editor and is one of the features the landing page leads with, but the docs never mentioned it — searching for "markup" returned nothing. This adds a new Collaboration page, plans/collaboration/markup.mdx, covering how to enter the mode, the three input gestures (draw / type / speak), the tool set and hotkeys, how to adjust annotations, what Send markup actually produces, and what happens to unsent annotations. It is listed in docs.json after reviews, with one cross-linking sentence added to plans/getting-started/rich-content.mdx and plans/collaboration/reviews.mdx.

Verified

Everything on the page was confirmed by reading the product repo (ref-tools/ref):

  • Entry point and hotkey — the marker button lives in the plan header's editor mode controls (plan-app/client/src/components/EditorRightActionControls.tsx, MarkupModeButton.tsx); toggleMarkupMode has default hotkey Cmd+Shift+M, context global (plan-app/client/src/lib/hotkeys/actions.ts). formatHotkey (lib/hotkeys/types.ts) renders Cmd as Ctrl on Windows/Linux, which is why the page states the mapping once rather than per shortcut.
  • Desktop only — handleToggleMarkupMode returns early when isMobile (routes/EditorPage.tsx), and the mobile branch of EditorRightActionControls.tsx never renders the marker button.
  • Read-only document, remote edits still flow — lib/editor/markupReadOnly.ts applies EditorView.editable(false) + EditorState.readOnly only, deliberately no changeFilter, so remote Yjs transactions still apply while user input is blocked. MarkupOverlay.tsx flips it on for the lifetime of the mode.
  • Gestures and first-run copy — lib/markup/markupGestures.ts: left button drag draws, click without drag drops a focused note, button === 2 starts voice capture; components/MarkupIntroPopover.tsx is the wording the page mirrors ("Left-click + drag to draw", "Strokes, arrows & shapes in red ink", "Hold right-click to speak", "Becomes a note when you release", "Click anywhere to type a note").
  • Tools and keys — components/MarkupToolbar.tsx (Draw D, Arrow A, Rectangle R, Line L; undo/redo; Cancel; Send markup with Cmd+Enter), plus the key handler in MarkupOverlay.tsx (D/A/R/L, Delete/Backspace on the selection, Cmd+Z / Cmd+Shift+Z / Ctrl+Y, Escape deselects then exits via consumeMarkupEscape in lib/markup/markupTypes.ts).
  • Annotation editing — drag-to-move and hit-testing in markupGestures.ts; endpoint/corner reshaping via shapeAnchors / reshapeShapeEndpoint / reshapeRectCorner in MarkupOverlay.tsx; note double-click-to-edit, side-handle resize, and empty-note discard on blur in components/MarkupLabel.tsx.
  • Line anchoring — annotations store an anchorLine and are re-resolved on scroll/resize (MarkupOverlay.tsx redraw effect, resolveAnchoredPoint in markupTypes.ts).
  • What Send produces — lib/markup/markupSend.ts captures each cluster (lib/markup/markupCapture.ts rasterizes the editor content DOM with the ink and notes composited on top, including self-rasterized HTML widget iframes), uploads the PNGs as attachments, and calls sendMessage with threadId: null, i.e. a new thread. The agent instructions the page paraphrases are buildMarkupPrompt in lib/markup/markupClusters.ts (read the plan, interpret the strokes/notes, apply the edits with Edit leaving unrelated content untouched, note ambiguity in chat). Clustering of nearby annotations into one image is clusterAnnotations in the same file. On success MarkupOverlay.handleSend calls onThreadLaunched + onExit, and EditorPage.handleMarkupThreadLaunched selects the thread and opens the chat panel.
  • Voice requirements — lib/markup/transcription/index.ts: support requires navigator.mediaDevices.getUserMedia, the mic anchor acquires permission, and isSpeechCaptureSupported() gates the speak row in the intro popover, so the page says the option is not offered where speech capture is unavailable.
  • Unsent annotations discarded — MarkupStore (lib/markup/markupStore.ts) is in-memory and created per overlay mount; EditorPage.tsx resets isMarkupMode on documentId change with the comment "Ephemeral per plan — leaving the plan (or toggling off) discards any unsent annotations".
  • Rich-content cross-link — widget iframes are snapshotted and composited during capture (compositeWidgetIframes in markupCapture.ts), which is what backs "diagrams and widgets are captured as they render".

Unverified / omitted

  • Availability / rollout. The overlay and its button are behind a Statsig gate (MARKUP_MODE in plan-app/client/src/hooks/useFeatureFlag.ts, comment still says "during the spike"). Gate state lives in Statsig, not the repo, so I could not confirm from code that the mode is on for everyone. The page therefore makes no availability claim beyond "desktop". Unblocked by a look at the gate's rollout in Statsig — if it is not at 100%, this page should say so or wait.
  • Sending markup as review comments. buildMarkupCommentsPrompt exists in lib/markup/markupClusters.ts (leave the feedback as comments, optionally attached to an open review) but has no non-test caller, so there is no UI path to it. Not documented.
  • Multiplayer and persistence of annotations. No code stores or transmits annotations before Send, but I did not find code that defines what other viewers see, so the page only states that unsent annotations are discarded and are not saved with the plan — it makes no claim about collaborators seeing them.
  • Alt-click / Shift+Cmd+Enter capture download. Explicitly a debug affordance in markupSend.ts; left out on purpose.
  • Exact clustering constants (8-line merge gap, 10-image cap) and the region padding. Real but internal tuning; the page describes the behaviour qualitatively instead.
  • Which agent/model handles the markup thread. sendMessage is called without an explicit model, so the resolution happens server-side; not documented.
  • Screenshots. No images added — I could not produce product screenshots without running the app.

Conflicts

This PR edits docs.json (one line added to the Collaboration group). Sibling PRs in this batch touch the same file; conflicts there are expected and small.


Generated by Claude Code

This branch has not been deployed

No deployments
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.

2 participants