feat: add Memory tab with custom vocabulary to AI settings#3816
feat: add Memory tab with custom vocabulary to AI settings#3816devin-ai-integration[bot] wants to merge 13 commits intomainfrom
Conversation
✅ Deploy Preview for hyprnote ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for hyprnote-storybook canceled.
|
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
b1133c9 to
389c98a
Compare
Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
389c98a to
b96e938
Compare
Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
apps/desktop/src/components/settings/memory/custom-vocabulary.tsx
Outdated
Show resolved
Hide resolved
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
apps/desktop/src/components/settings/memory/custom-vocabulary.tsx
Outdated
Show resolved
Hide resolved
….devin.ai/proxy/github.com/fastrepl/hyprnote into devin/1770712735-add-memory-tab
Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
| } else if (e.key === "Escape") { | ||
| e.preventDefault(); | ||
| onCancelEdit(); |
There was a problem hiding this comment.
🟡 Edit form state not reset on cancel, showing stale value on re-edit
When a user edits a vocabulary item, modifies the text, then cancels (via Escape key or X button), and then re-opens the edit mode, the form input shows the previously typed (unsaved) value instead of the current item.text.
Root Cause
The VocabularyItem component creates a useForm instance with defaultValues: { text: item.text } at line 167-170. TanStack Form's defaultValues are only used for initial form state — they do not automatically reset when the user cancels editing.
Both cancel paths fail to call form.reset() before exiting edit mode:
- Escape key handler at line 234-236: calls
onCancelEdit()without resetting. - X button at line 267: calls
onCancelEdit()without resetting.
Since VocabularyItem uses key={item.rowId} (apps/desktop/src/components/settings/memory/custom-vocabulary.tsx:122), the component does NOT unmount/remount between edit sessions — the form instance and its stale state persist across cancel → re-edit cycles.
Scenario:
- User clicks edit on item "hello"
- User types "world" into the input
- User presses Escape or clicks X →
onCancelEdit()setseditingIdto null, span shows "hello" - User clicks edit again on the same item → input shows "world" (stale) instead of "hello"
Impact: Confusing UX — users see a value they previously discarded, and if they immediately press Enter to confirm, the item gets updated to the stale value they intended to cancel.
Prompt for agents
In apps/desktop/src/components/settings/memory/custom-vocabulary.tsx, the VocabularyItem component needs to reset its form state when cancelling an edit. There are two cancel paths that need fixing:
1. The Escape key handler around line 234-236: add form.reset() before onCancelEdit()
2. The X button onClick handler at line 267: change from onClick={onCancelEdit} to an inline handler that calls form.reset() then onCancelEdit()
Specifically:
- Line 235-236: change to:
form.reset();
onCancelEdit();
- Line 267: change onClick={onCancelEdit} to onClick={() => { form.reset(); onCancelEdit(); }}
Was this helpful? React with 👍 or 👎 to provide feedback.
feat: add Memory tab with custom vocabulary to AI settings
Summary
Restores custom vocabulary management as a Memory tab within AI settings. Users can add, edit, search, and delete industry/company-specific terms intended to improve transcription accuracy.
Changes across layers:
memorySchema(Zod) andmemoriestable (TinyBase) withuser_id,type,text,created_atfieldsmemoriestable (memories.json), registered alongside existing persistersvisibleVocabsquery filtering memories bytype === "vocab"CustomVocabularyViewcomponent with search/add/edit/delete, inline editing with duplicate detectionMemoryvariant added toAiTabenum (Rust + generated TS bindings), wired intoai.tsxUpdates since last revision
onSubmithandler now checks for existing terms before callingmutations.create().onCancelEdit()is now called unconditionally on submit so the user always exits edit mode.user_idguard inuseVocabMutations.create()to prevent persisting rows with emptyuser_idbefore the store is fully initialized (matches the guard pattern used elsewhere in the codebase, e.g.ai.tsx).onSubmithandler caused by a bad merge — a duplicated code block madeCustomVocabularyViewunparseable. The handler is now correctly structured with duplicate detection.main.Review & Testing Checklist for Human
ONBOARDING=0 pnpm -F desktop tauri dev) and verify the Memory tab appears in AI settings with the Brain icon, vocabulary can be added/edited/deleted, and items persist after restartvisibleVocabsquery does not filter byuser_id— unlikevisibleTemplatesand other queries that usewhere("user_id", param("user_id")), the new query only filters onwhere("type", "vocab"). If multi-user data isolation matters for this table, auser_idfilter needs to be added to the query definition inmain.ts.useSetPartialRowCallbacktype cast —updateRowinuseVocabMutationsusesas (args: { rowId: string; text: string }) => voidwhich could mask type mismatches at runtimebindings.gen.tsstays in sync — it was manually edited to add"memory"alongside the Rust enum change; verify regeneration produces the same resultcustom_vocabularyfield during transcription (seeplugins/local-stt/src/ext.rs). If end-to-end integration is expected, that's a follow-upRecommended test plan: Open AI settings → click Memory tab → add a few terms → search → try adding a duplicate (Enter + button) → edit one → delete one → restart app → confirm terms persist.
Notes