feat(templates): author templates on the note surface - #941
Merged
Conversation
|
React Doctor found no new issues. 🎉 Reviewed by React Doctor for commit |
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
React Doctor flagged six `no-ref-current-in-render` errors: the latest-ref
pattern was assigning during render, which React may replay or discard.
Every one now syncs in an effect, matching the convention already documented
in the tabs context ("must be in useEffect per React rules").
Four real defects fixed alongside it:
- The editor read its tab from `useActiveTab()`, so in a split view it drove
whichever tab was focused rather than its own. `TabContent` now passes
`tabId` down.
- Keying the surface off `templateId` remounted the whole editor the moment a
draft adopted its new id — mid-typing, right after Create. The key is pinned
to the identity the tab opened with, and the new template seeds the query
cache so the surface never flips to loading.
- Pending tag colours were stored lower-cased but looked up raw-cased, so a
tag typed with a capital lost its colour until the notes query caught up.
- The unsaved-changes prompt used `AlertDialogAction`, which is Dialog.Close:
it fired `onOpenChange(false)` on top of the handler, and the guard read
that as a Cancel that aborted the close the user had just confirmed. The
registry now ignores a resolution that lands while one is in flight, and
drops the Save button when the pending work has no name to save under.
Regression tests cover the remount and the tab-identity fixes; both were
confirmed to fail against the unfixed code.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Templates were authored on a bespoke form — its own header, a description textarea, and the note editor boxed inside a card. This replaces that screen with the note editing surface itself, so a template is written exactly the way a note is.
What changes
Authoring.
pages/template-editor.tsxis rewritten to compose the primitives the note page already uses —NoteLayout,NoteTitle,TagsRow,InfoSection,GhostAffordanceRow,ContentArea.pages/note.tsxis untouched: what is shared is the components, not the page.Saving. A new template is an in-memory draft — nothing reaches the database until Create Template (disabled while the name is blank), so a half-typed template never litters the list. After creation the button becomes Update and saving turns continuous: an 800 ms debounce, skipped whenever the serialized payload is byte-identical to the last persisted one, since every write enqueues a sync item. The state machine lives in
hooks/use-template-draft.ts, isolated from rendering.Closing. A dirty draft marks its tab and prompts Save / Don't Save / Cancel on close. The guard registry sits inside the tabs context (
contexts/tabs/close-guard.tsx), so the ✕, middle-click, the tab context menu, ⌘W, and close-others/right/all are all covered without patching the six call sites. Tabs that register no guard behave exactly as before. Window close and app quit are deliberately out of scope.Built-ins. Open read-only with a Duplicate & Edit button that opens the editable copy in a new tab.
Settings. Clicking a row opens the editor tab instead of the inline preview, which is removed.
Bugs fixed along the way
Both pre-existing, both surfaced by rewriting this code:
mapFromTemplatePropertyTypehad no reverse mapping forselect,multiselect, orrating, so they fell through totext. Editing anything in a template holding a select property rewrote that property's type permanently.lib/template-properties.tsfixes it structurally:TemplatePropertystays the stored truth behind a stable id and maps to the UI shape for display only — nothing maps back. Index-derived ids (prop-${index}), which also broke under reorder, are gone with it.IconPickerButton.Removals
pages/templates.tsxand thetemplatestab type — a 19K list page nothing in the app ever opened. Safe on restore: an unrecognised stored tab lands on the "unknown tab type" branch, not a crash.pages/settings/template-preview.tsx— superseded by the editor tab.phaseF.pagesTemplateEditorandphaseF.pagesTemplateslocale keys, orphaned by the two deletions above.Compatibility
No schema, IPC contract, vault format, or sync protocol change. The
descriptionfield is no longer edited here but is preserved on disk —updateTemplatetreats an omitteddescriptionas "keep", so existing values survive untouched and still show in the Settings list.Verification
pnpm typecheck(includes contracts, architecture,ipc:check),pnpm lint(0 errors),git diff --checki18n:checkand theicu-brace-style/placeholder-paritylocale tests green. The literal{{title}}in the content placeholder is ICU-escaped, which the brace test enforces.pnpm docs:impact --base origin/main --strictandpnpm docs:buildwindow.api.templates.*directly, so the UI rewrite does not touch them.Not yet done: an interactive smoke pass in a running app.
Spec:
docs/superpowers/specs/2026-08-04-template-editor-as-note-page-design.md