Rebuild the memo editor as a single CodeMirror 6 "decorated source"
editor. The document is the raw markdown, stored verbatim and styled in
place (markers stay visible), so the editor never serializes a tree back
to markdown — removing the round-trip fidelity bug class (inline images,
setext headings, ordered-list indentation, HTML entities) that the old
editor needed per-case patches for.
- MemoEditor/Editor: CodeMirror 6 + lang-markdown (GFM). Tokens, heading
lines, #tag/@mention, and the autocomplete popover are styled in plain
CSS (Editor/editor.css) with theme tokens, not a CSS-in-JS theme.
- Tab/Shift-Tab nest/outdent list items (marker-aware, ordered items
renumbered so nesting is CommonMark-valid); Escape blurs; #tag
autocomplete sourced from useTagCounts.
- Focus-mode toolbar reimplemented as markdown-text ops; active state read
from the Lezer tree via the backend-agnostic formatting/commands catalog.
- Remove the old serialize-back-to-markdown editor (its Editor dir,
PlainEditor, the editor-mode system) and its now-unused dependencies
(marked, textarea-caret, and the rich-text editor packages).
- Consolidate toolbar components under Toolbar/. Read-only MemoContent
rendering is unchanged.
Mirror the #tag mark so the TipTap editor styles @username while typing
and on load, matching the read-only view. A shared mention grammar
(utils/mention-grammar.ts) feeds both the editor tokenizer and the
read-only remark renderer so they can't drift.
Styling only -- no autocomplete dropdown (ListUsers is admin-only and
there is no user-search RPC). Bare emails still autolink to mailto: and
are never treated as mentions: the tokenizer's start() skips email-glued
@s so marked's GFM autolinker keeps seeing the whole address.
Extract the Tailwind classes for common markdown elements (paragraph,
blockquote, lists, inline code, link, hr, headings) into a single
markdownStyles.ts consumed by both the read-only MemoContent components
and the WYSIWYG editor, replacing the duplicated per-element strings and
the .memo-wysiwyg CSS block. Heading classes are precomputed per level so
the hot renderHTML path is a lookup, not a cn() merge.
Also require at least one character after `#` before opening the tag
suggestion menu so a bare `#` (or `# ` heading) no longer conflicts with
markdown headings.
Two bugs caused users to be redirected to /auth too frequently:
1. Race condition in Promise.all([initInstance(), initAuth()]):
initInstance() makes a gRPC request whose auth interceptor calls
getAccessToken() synchronously. When the access token was expired,
getAccessToken() eagerly deleted it from localStorage as a "cleanup"
side-effect. By the time initAuth() ran and checked hasStoredToken(),
localStorage was already empty, so it skipped the getCurrentUser()
call and the token refresh cycle entirely — logging the user out even
when the refresh-token cookie was still valid. Fix: remove the
localStorage deletion from getAccessToken(); clearAccessToken()
(called on confirmed auth failure and logout) handles proper cleanup.
2. React Query retry: 1 caused a second refresh+redirect attempt after
auth failures. The auth interceptor already handles token refresh and
request retry internally. If it still throws Unauthenticated, the
redirect is already in flight — a React Query retry only fires another
failed refresh and a redundant redirectOnAuthFailure() call. Fix: use
a shouldRetry function that skips retries for Unauthenticated errors
while keeping the existing once-retry behaviour for other errors.
- Deleted MemoContentContext and its associated types.
- Updated Tag and TaskListItem components to use MemoViewContext instead.
- Refactored MemoContent component to eliminate context provider and directly use derived values.
- Simplified MemoViewContext to only include essential data.
- Enhanced error handling in various components by introducing a centralized error handling utility.
- Improved type safety across components and hooks by refining TypeScript definitions.
- Updated remark plugins to enhance tag parsing and preserve node types.