feat: integrate Milkdown editor for note-taking functionality
- Added Milkdown dependencies: @milkdown/kit, @milkdown/react, @milkdown/theme-nord. - Implemented MilkdownEditor component for rich text editing in notes. - Updated /notes/$noteId route to include editable title and auto-saving functionality. - Enhanced UI with loading states, saving indicators, and delete confirmation dialog. - Applied Milkdown-specific CSS overrides for consistent theming and styling. - Improved note update logic with debounced saving and cleanup on unmount.
This commit is contained in:
26
progress.txt
26
progress.txt
@@ -269,3 +269,29 @@
|
||||
- `notes.create` returns `{ id }` which can be used directly for navigation in the `onSuccess` callback
|
||||
- TanStack Router file-based routing: `notes.$noteId.tsx` generates `/notes/:noteId` route automatically — `Route.useParams()` provides typed `{ noteId }`
|
||||
---
|
||||
|
||||
## 2026-02-22 - US-016
|
||||
- What was implemented:
|
||||
- Installed `@milkdown/kit`, `@milkdown/react`, `@milkdown/theme-nord` (following official Milkdown installation guide)
|
||||
- Created `MilkdownEditor` wrapper component at `src/renderer/components/notes/MilkdownEditor.tsx`
|
||||
- Uses official React recipe: `MilkdownProvider` + `useEditor` hook with `Editor.make()` configuring `commonmark`, `listener`, `history` plugins
|
||||
- `listenerCtx.markdownUpdated()` fires onChange callback via stable `useRef` (avoids editor re-creation)
|
||||
- `defaultValueCtx` sets initial markdown content from SQLite
|
||||
- Rewrote `src/renderer/routes/notes.$noteId.tsx` with full editor page:
|
||||
- Editable title: borderless shadcn/ui `Input` (border-0, shadow-none, focus-visible:ring-0), saves on blur via `notes.update({ id, title })`
|
||||
- Auto-save: `onChange` from Milkdown triggers 500ms debounced `notes.update({ id, content })` via `useRef` + `setTimeout`/`clearTimeout`
|
||||
- "Saving..." indicator: shadcn/ui `Badge` (variant=secondary) shown while debounce is pending, hidden on mutation `onSettled`
|
||||
- Back button: shadcn/ui `Button` (variant=ghost, size=icon) with `ArrowLeft` Lucide icon, `window.history.back()`
|
||||
- Loading/not-found states handled
|
||||
- Added Milkdown/ProseMirror CSS overrides in `src/renderer/globals.css` using semantic color variables (`var(--foreground)`, `var(--muted)`, `var(--border)`, `var(--muted-foreground)`, `var(--primary)`)
|
||||
- Typecheck passes (zero errors)
|
||||
- Files changed: `src/renderer/components/notes/MilkdownEditor.tsx` (new), `src/renderer/routes/notes.$noteId.tsx`, `src/renderer/globals.css`, `package.json`, `package-lock.json`, `prd.json`, `progress.txt`
|
||||
- **Learnings for future iterations:**
|
||||
- `@milkdown/kit` is the recommended all-in-one package — it bundles core, preset-commonmark, plugin-listener, plugin-history, and utilities under sub-paths like `@milkdown/kit/core`, `@milkdown/kit/preset/commonmark`, etc.
|
||||
- The `useEditor` hook from `@milkdown/react` takes `(root) => Editor.make()...` — the `root` param is the DOM element Milkdown manages, set via `ctx.set(rootCtx, root)`
|
||||
- Use `useRef` for the onChange callback passed to `listenerCtx.markdownUpdated()` — this avoids re-creating the editor instance when the callback identity changes
|
||||
- `listenerCtx.markdownUpdated((_ctx, markdown, prevMarkdown))` provides both current and previous markdown — compare them to avoid firing on no-op updates
|
||||
- For debounced auto-save: `useRef<ReturnType<typeof setTimeout>>` + `clearTimeout`/`setTimeout` is simpler than external debounce libraries; cleanup in `useEffect` return prevents stale saves
|
||||
- Nord theme (`@milkdown/theme-nord`) provides base ProseMirror structure; override with CSS using the app's semantic color variables for consistent theming
|
||||
- Import both `@milkdown/theme-nord/style.css` and `@milkdown/kit/prose/view/style/prosemirror.css` for proper base styling
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user