In production (Docker/Next.js standalone), getAllNotes() can return cached results
when called immediately after createNote(skipRevalidation:true). This caused newly
created notes to disappear after the cache reload overwrote the optimistic local state.
Fix: remove triggerRefresh() from both handleCreateNote (NotesTabsView) and
handleNoteCreated (HomeClient). The note is already added optimistically to local
state and does not need a server round-trip.
- NoteHistoryModal: remove window.location.reload(), use onRestored(restored) callback
- NotesTabsView: revert sync derivation to useEffect, add prevNotesRef to detect
server-side content changes (restore) vs local edits — fixes note disappear bug
and cross-notebook notes appearing after refresh
- NoteInlineEditor key: include updatedAt so restoration remounts editor with fresh content
- note-card: render title/content/labels from note prop directly, not optimisticNote
The useEffect synced local state from props but fired on every keystroke
because onChange updates the parent, which creates a new note prop reference.
This cleared the save timer and isDirty flag before auto-save could fire,
making notes unsavable in tabs view. The key prop with updatedAt already
handles the restore remount case.
- Complete rewrite of note-history-modal: version list with inline restore/delete,
split diff view with synced scrolling, rich preview for markdown/richtext/checklist
- Fix restore not updating editor: use key={id-updatedAt} on NoteInlineEditor to
force remount on restore, and add sync useEffect to reset local state on prop changes
- Add sync useEffect in NoteEditor for external note updates
- Add historyEnabled to NOTE_LIST_SELECT (no more 'Activer' on every modal open)
- Replace browser confirm() with shadcn AlertDialog for delete confirmation
- Add i18n keys: currentVersion, compareVersions, diffTitle, diffSelectHint, deleteVersionDesc
- Install diff + @types/diff for visual line diffing
- Fix MasonryGrid render-phase sync to preserve local sizes
- Update notes-tabs-view merge logic for restored note propagation
- Remove debug console.log from restoreNoteVersion
- If /api/ai/convert-markdown fails, abort the type change and show error toast
- Only switch to richtext after successful content conversion
- Switching back to markdown always works (no conversion needed)
- Add i18n keys for conversion success/failure messages
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Each theme includes light and dark mode variants with carefully tuned
oklch colors for foreground, card, borders, sidebar, and all UI elements.
Total: 10 named themes + dark mode + auto (system preference).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Agent-created notes are now type 'markdown' by default (raw markdown content)
- Switching note type from markdown → richtext auto-converts content to HTML
via /api/ai/convert-markdown endpoint using the existing markdownToHtml utility
- Users can freely switch between markdown and richtext in the note type selector
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add DeepSeek, OpenRouter, Mistral, Z.AI, LM Studio as AI providers
with editable model names via Combobox in admin settings
- Fix OpenRouter broken by normalizeProvider bug in config.ts
- Convert agent-created notes from Markdown to HTML (TipTap rich text)
- Add Notification model + in-app notifications for agent results
- Agent notification click opens the created note directly
- Add note count display on notebook and inbox headers
- Fix checklist toggle in card view (persist state via localCheckItems)
- Add checklist creation option in tabs/list view (dropdown on + button)
- Fix image description ENOENT error with HTTP fallback
- Improve UI contrast across all themes (input, border, checkbox visibility)
- Add font family setting (Inter vs System Default) in Appearance settings
- Fix CSS font-sans variable conflict (removed dead Geist references)
- Update README with new features and 8 providers
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Same fix as reformulate — raise limit from 500 to 2000 and use i18n
error keys instead of hardcoded English messages.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Raise MAX_WORDS from 500 to 2000 for text reformulation
- Error messages now use i18n keys (ai.wordCountMin/Max) instead of
hardcoded English strings
- Client translates server errors using the user's UI language
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add reminder action to NoteActions (masonry view) and NoteMetaSidebar (tabs view)
- NotificationPanel now fetches and displays upcoming/overdue reminders alongside share requests
- Badge count includes overdue reminders; overdue items show mark-as-done toggle
- Sidebar trash count refreshes on NoteRefreshContext trigger (no more manual refresh)
- Add "Clear completed" button on reminders page with clearCompletedReminders action
- Add i18n keys for new features (en/fr)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The admin form saves Ollama URLs as OLLAMA_BASE_URL_TAGS/EMBEDDING/CHAT,
but the factory only read OLLAMA_BASE_URL — causing 500 errors in Docker
where no localhost fallback exists.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When chatScope is "this note", the AI had access to note_search and note_read
tools which let it pull content from any note. Now these tools are excluded via
a webOnly flag in buildToolsForChat — only web_search/web_scrape remain if
the user toggled web search.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- When chat scope is "this note" (noteContext present), skip RAG/semantic
search entirely. Previously the AI received all user notes as context
even when scoped to a single note, causing irrelevant responses.
- Replace 3 native <select> elements for Ollama models with searchable
Combobox component (tags, embeddings, chat providers).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The sync effect in notes-tabs-view was always preferring local.title over
fresh.title from the DB. When a note was created with an AI-generated title,
triggerRefresh() fetched the note with its title from DB, but the merge
overwrote it with the empty local title. Now uses local.title || fresh.title
so fresh titles show through when local is empty.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add image description service + API route for AI-powered image analysis
- Image title generation returns 3 selectable suggestions via TitleSuggestions component
- Add "Describe images" action in AI assistant (individual + collective)
- Fix pin refresh propagation in card and tabs view
- Fix note creation refresh in tabs mode, pass all notes to tabs view
- Add RTL support (dir="auto") on note content elements
- Pass UI language dynamically to AI endpoints instead of hardcoded 'fr'
- Add 18 missing i18n keys in both en.json and fr.json
- Sparkles button on images for AI title generation (bottom-right, pulse animation)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Chat (AIChat floating widget): conversationId was never captured from
the API response, so every message created a new conversation with no
context. Now creates the conversation upfront before streaming (same
pattern as ChatContainer) so the ID persists across messages.
Note history: was stored globally in UserAISettings, so enabling
history on one note enabled it for ALL notes. Now each Note has its
own historyEnabled boolean field. The "Enable history" action only
affects the specific note. A migration adds the column with default
false.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Remove revalidatePath('/chat') from createConversation — it caused
the server to re-render the /chat page after each new conversation,
which reset the useChat hook state and created a new conversation
for every message instead of continuing the existing one.
Sidebar now refreshes client-side after streaming completes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Commit untracked 20260428150000_add_note_history migration (creates
NoteHistory table + noteHistory column) that was causing 500s in prod
- Exclude static files (.json, .svg, .ico, etc.) from auth middleware
to fix manifest.json 403
- Add console.error to echo API routes for production debug visibility
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Backup before migration (pg_dump/SQLite copy), DB connection wait with
retries, idempotent prisma migrate deploy, old backup cleanup (keep 5),
and server refuses to start if migration fails.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add noteHistoryMode setting (manual default / auto) with DB migration
- Manual mode: commit button in editor toolbar creates snapshots on demand
- Auto mode: smart snapshots with 20-char diff threshold + 5min cooldown,
structural changes (color, pin, archive, labels) bypass cooldown
- Add delete individual history entries from history modal
- Fix sidebar: Notes nav no longer active on notebook pages
- Fix sidebar icon: replace filled Lightbulb with outlined FileText
- Fix title suggestions: change from amber to sky blue color scheme
- Fix hydration mismatch: add suppressHydrationWarning on locale dates
- Complete i18n: add history, sort, and AI chat translations for all 16 languages
- Translate French AI assistant section (40+ keys) from English to French
- Update README with new features and stack info
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This introduces guarded migrations with automatic backups, fixes note creation after DB reset, and wires snapshot/restore history across notes surfaces.
Next.js bakes public/ at build time — dynamically uploaded files were
never served in Docker standalone mode. Store uploads in data/uploads/
and serve via /api/uploads/ with a rewrite for backward compatibility.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add stop button to all chat interfaces (floating, contextual, full-page)
- Add conversation sliding window (50 messages) to prevent context overflow
- Add chat timeout warning (30s toast)
- Force response language in chat system prompt (mandatory per-locale)
- Add image paste from clipboard in all note editors (card, list, input)
- Fix upload API to infer extension from MIME type for clipboard images
- Add image description support in note AI chat (base64 vision)
- Fix search regex crash on special characters (escape user input)
- Fix search case-insensitivity on PostgreSQL (mode: 'insensitive')
- Add try/catch around semantic search in chat route (prevent blocking)
- Add new chat button to floating AI assistant
- Fix empty thinking bubbles for reasoning models (filter non-text parts)
- Remove duplicate AI assistant toggle from note editor header
- Improve link metadata scraping (timeout, content-type check, relative URLs)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Same pattern as TitleSuggestionService: getModel() + generateText
with system/user prompts. LanguageDetectionService (tinyld) auto-detects
note content language. Labels now match note language (Persian note → Persian labels).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Use LanguageDetectionService (tinyld) to detect the language of note
content, same pattern as TitleSuggestionService. No more hardcoded
per-language prompts — single prompt with detected language injected.
Labels now match the note content language (e.g. Persian note → Persian labels).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>