Skip to content

Reloadable dochandles - #28

Open
paulsonnentag wants to merge 19 commits into
mainfrom
reloadable-dochandles
Open

Reloadable dochandles#28
paulsonnentag wants to merge 19 commits into
mainfrom
reloadable-dochandles

Conversation

@paulsonnentag

@paulsonnentag paulsonnentag commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Goal

Make drafts good enough for daily usage. We should do a proper exploration of this but in the meantime we need a basic version of drafts that works.

Fixes

  • Change group caching: store change groups in an automerge document so we don't need to recompute this every time
  • Time travel: you can scrub through a draft's history without the tool remounting, and diff against any point you choose
  • Draft actions: reworked into a per-card menu (fork, fork from a version, merge, delete) instead of the drag-out gesture.
  • Draft hierarchy: fork off any draft, merge back into the parent, merged drafts disappear
  • Attribution: timelines show who made the changes; we store an actorId -> contact mapping for that
  • Read only mode: add readonly mode for tldraw and codemirror so you can't edit things when scrubbed to a version in the past
  • Share drafts: link to a draft, you still share the whole document with all drafts. For more granular sharing we'll need keyhive

Todos

  • move scopeReset handling into codemirror plugin

…riptors

Checkpoint-only moves (scrubbing rewrites CheckedOutDraft.at) now re-answer
every live repo:handle-descriptor subscription with freshly pinned backing
urls, so OverlayRepo swaps handle backings in place instead of the main view
remounting. DocumentAreaRoot drops pinnedDocUrl and keys the main view on the
plain selected doc url. Stale in-flight resolutions are gated by a per-batch
AbortController (replacing the switchEpoch counter).
Scrubbing history / switching drafts swaps a live handle's backing
(change event with scopeReplaced: true); the old undo entries describe
a different timeline, so both editors now drop their stacks on that
event:

- codemirror-base owns history()/historyKeymap in a compartment
  (createHistoryExtension) and cycles it out and back in on
  scopeReplaced -- CodeMirror has no clear-history API, and single
  ownership matters because the history state field is a module-level
  singleton. codemirror-markdown no longer registers its own history.
- the sync plugin's full-doc rebuild dispatch is annotated
  addToHistory: false + remote: true, so the swap itself is never
  undoable and the reset is order-independent.
- tldraw4 clears the editor's stack via useClearHistoryOnScopeSwap in
  the automerge bindings (editor-level hook: the HistoryManager only
  exists once <Tldraw> mounts, so this can't live in useAutomergeStore).

Remote automerge patches entering the local undo history is a separate
upstream issue (automerge-codemirror PR pending).
Paint the timeline indicator (dot + line) on top of the group rows and
always draw it in the foreground color. Drop the "current" badge; in its
place the card title shows a "Return to latest" pill while the timeline
is pinned. Dragging the scrubber all the way to the top now returns to
the live latest version instead of freezing at the newest change.
…ache docs

The sidebar re-ran Automerge.diff over every change on each edit to group
history, blocking the main thread. A fill engine now computes activity
groups in idle-time slices and persists them in a change-group-cache doc
linked from each draft; the sidebar renders straight from the cache and
resolves scrub positions on demand from change metadata (no diffs).
…iffs

Scrubbing wrote from=to (no diff) and live drafts always diffed against
their fork point via an implicit provider fallback, so diffs were neither
visible when scrubbing nor controllable. The checkpoint's per-doc `from`
is now the only baseline authority: an eye toggle in the card title
writes it — group-start heads when pinned to a group (the baseline stays
anchored while scrubbing within it), fork-point heads on a live draft —
and the fallback is gone, so closing the eye simply removes the
baselines. "Return to latest" moves left; the eye sits right.
…f's from point

Plus timeline polish: selecting a group re-anchors the baseline to its start,
GitHub-style relative times, softer scrubber lines and selection styling, no
group-row highlight, 10-minute grouping gap, and a tooltip on the disabled eye.
@patchcrow

patchcrow commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

✅ Patchwork glance ready

https://patchwork-preview-28--patchwork-base.netlify.app

Patchwork from inkandswitch/patchwork.inkandswitch.com, packages from this PR at ceb5a79.

build-info.json

…erge up, delete

Drag-to-fork and the New draft button are gone; a selected card's "..."
menu now forks at the latest heads or at the scrubbed version, merges a
draft up into the draft it was forked off (hover highlights the target
card), and deletes a draft after a confirm dialog. Drafts track their
parent, list depth-first, and render indented beneath Main. Merged
drafts hide from the list; the scrubber sticker drops the doc title.
…hile the eye is open

The scrubber sticker used to diff only the single change under the head,
so extending the baseline never changed its +/- counts. It now reads the
live checkpoint and sums each member's from -> to diff. Also: headroom
above the head line so the dot and sticker stop crowding the card
header, the selected header keeps the card's color, and the version
footer is gone (the version still logs to the console).
…les (drafts cache, actor attribution, codemirror history)
…y extension tracks isReadOnly() live and the comment UI disables writes while read-only
…dOnly() live and gate the store write-back behind a ref; align tldraw4's automerge-repo pins with @automerge/react
The list provider mirrors the checked-out draft into the URL (replaceState,
so no re-route or history spam) and applies an incoming draft= once the
draft appears in this doc's tree — so links work even before the draft doc
has synced. The router clears the param when navigating to another doc.
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.

3 participants