Skip to content

feat: mute CRDT sync chrome and move bold onto ⌘B - #78

Merged
chasehuh merged 1 commit into
mainfrom
task/sync-chrome-bold-shortcut
Aug 5, 2026
Merged

feat: mute CRDT sync chrome and move bold onto ⌘B#78
chasehuh merged 1 commit into
mainfrom
task/sync-chrome-bold-shortcut

Conversation

@chasehuh

@chasehuh chasehuh commented Aug 5, 2026

Copy link
Copy Markdown
Member

PR-A of #77. Two small, independently reviewable locks: the Offline indicator stops looking like a hard error, and ⌘B becomes bold.

Offline chrome

.zed-save-error paints a red bordered alert box. On the CRDT path that fires for a transient WebSocket reconnect — a state where nothing is wrong, because the edit is already durable in IndexedDB and will flush. That is alarm fatigue for a non-event.

New crdtSyncChrome() (lib/crdt/sync-chrome.ts) maps transport status to a muted .zed-titlebar__sync mark placed to the left of Publish: no border, no fill, var(--c-text-muted), role="status" rather than role="alert". Copy and tooltip are unchanged ("Offline" / saved on this device), and Retry is offered only where a manual flush means something.

Path Status Element Look
CRDT loading / syncing .zed-titlebar__sync muted "Syncing…"
CRDT offline .zed-titlebar__sync muted "Offline" + Retry
CRDT synced nothing
Legacy auth / conflict / generic .zed-save-error unchanged red

The red block is now gated on !CRDT_ENABLED — real failures on the legacy whole-document path keep their strong chrome, deliberately.

This also closes the #75 residual. loading and syncing both used to collapse into "saved", so a note still waiting on the WebSocket read "Saved" while it was actually read-only and unconfirmed. They now read "Syncing…".

⌘B bold / ⌘⇧B sidebar

⌘B / Ctrl+B toggles **bold** — wrap a selection, unwrap when the markers are inside or just outside it, and on an empty caret insert **** with the caret centered. One input history event, so ⌘Z undoes it in a single step. It is a plain view.dispatch, so yCollab carries it into Y.Text on the CRDT path for free.

The sidebar moves to ⌘⇧B. Note the window-level keydown had to stop matching plain meta+b entirely rather than merely lose priority: it listens on window, and a CodeMirror keymap's preventDefault() does not stop propagation, so leaving it would have toggled the sidebar and bolded on every press. ⌘\ and ⌘N are untouched.

On macOS defaultKeymap binds emacs-style Ctrl-b to cursorCharLeft; our binding is Mod-b = Cmd there, so they do not collide. On Linux/Windows Mod-b = Ctrl-b, which defaultKeymap does not bind. Bound at Prec.high anyway, matching agentnoteStrikethroughKeymap.

Shared toggle helper

toggleStrikethrough's wrap/unwrap/caret logic moved to toggleInlineMark(view, mark) now that bold is a second real caller — an abstraction with two concrete users, not a speculative one. Strikethrough's behavior is unchanged by construction (same function, same constant), and it picks up the unit tests it never had: both marks run the same table.

Tests

pnpm vitest run259 passed (was 239; +20).

  • lib/editor/toggle-mark.test.ts — wrap, unwrap-from-inside, unwrap-from-outside, empty-caret insert, multi-range, single-undo-step, round-trip, read-only refusal; run against both ** and ~~.
  • lib/crdt/sync-chrome.test.tssynced renders nothing; offline labels Offline + retry and keeps the "Saved on this device" copy; loading/syncing read "Syncing…" and never "Saved".

pnpm exec tsc --noEmit clean. pnpm build green.

pnpm lint reports 9 errors / 6 warnings — all pre-existing. Verified by running eslint against a clean checkout of main: byte-identical counts. They are react-hooks/refs and react-hooks/set-state-in-effect findings in agentnote-app.tsx, codemirror-editor.tsx, and publish-panel.tsx from a newer eslint-config-next. This PR adds none and fixes none (out of scope).

Manual QA

Run with NEXT_PUBLIC_AGENTNOTE_CRDT=1.

  1. Select a word → ⌘B → **word**. ⌘B again → unwrapped. ⌘Z once → back in a single step.
  2. Empty caret → ⌘B → **** with the caret between the pairs.
  3. ⌘B with the sidebar open → sidebar does not toggle. ⌘⇧B toggles it. ⌘\ still toggles it. ⌘N still creates a note.
  4. Bold on a CRDT note in two tabs → the markers converge.
  5. Kill the collab WebSocket → titlebar shows muted gray Offline + Retry to the left of Publish, no red box. Keep typing — edits still apply. Reconnect → the mark clears.
  6. Hard-reload before the socket connects → reads Syncing…, not "Saved".
  7. NEXT_PUBLIC_AGENTNOTE_CRDT=0, two tabs, force a 409 → red zed-save-error still appears with Use server / Overwrite.
  8. Empty state (no note open) → no sync mark (guarded on activeId, so the app does not sit on a permanent "Syncing…").

Refs #77. PR-B (wiki-style [[ links + tags) follows and depends on nothing here.

🤖 Generated with Claude Code

Two small chrome/keymap locks from #77.

Offline chrome: CRDT sync status no longer reuses the red `.zed-save-error`
alert. `crdtSyncChrome()` maps the transport status to a muted
`.zed-titlebar__sync` mark placed to the LEFT of Publish — no border, no
fill, `role="status"`. A CRDT edit is already durable in IndexedDB, so a
2-second reconnect blip is not an alarm. The red block stays exactly as it
was for real legacy failures (session expired / conflict), which is why it
is now gated on `!CRDT_ENABLED`.

Also closes the #75 residual: `loading` and `syncing` used to collapse into
"Saved", so a note still waiting on the WebSocket claimed to be saved while
it was in fact read-only and unconfirmed. They now read "Syncing…".

Shortcuts: ⌘B / Ctrl+B toggles `**bold**` in the editor, and the sidebar
toggle moves to ⌘⇧B. The window-level keydown had to stop matching plain
meta+b entirely rather than yield to the editor — it listens on `window`, so
a CodeMirror keymap's preventDefault would not have stopped it from also
firing. ⌘\ and ⌘N are unchanged.

`toggleStrikethrough`'s wrap/unwrap/caret logic is extracted to
`toggleInlineMark(view, mark)` now that bold is a second real caller, and
picks up the unit tests strikethrough never had (both marks run the same
table, including the single-undo-step guarantee).
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
memo Ready Ready Preview Aug 5, 2026 4:33am

Request Review

@chasehuh
chasehuh merged commit e6f29a5 into main Aug 5, 2026
2 checks passed
@chasehuh
chasehuh deleted the task/sync-chrome-bold-shortcut branch August 5, 2026 04:42
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