feat: add persistent conversation feature with cross-platform file restoration#66
Merged
Merged
Conversation
Copilot
AI
changed the title
[WIP] Add remember conversation feature for user persistence
feat: add persistent conversation storage with hybrid file reference strategy
Feb 4, 2026
Copilot
AI
changed the title
feat: add persistent conversation storage with hybrid file reference strategy
refactor: eliminate DRY violations and add user feedback for persistence operations
Feb 4, 2026
Copilot
AI
changed the title
refactor: eliminate DRY violations and add user feedback for persistence operations
fix: apply Biome formatter to resolve linting error
Feb 4, 2026
Copilot
AI
changed the title
fix: apply Biome formatter to resolve linting error
feat: redesign restore session modal with WhatsApp theme and elevated UI
Feb 4, 2026
Copilot
AI
changed the title
feat: redesign restore session modal with WhatsApp theme and elevated UI
fix: add responsive design to restore session modals for mobile devices
Feb 4, 2026
Copilot
AI
changed the title
fix: add responsive design to restore session modals for mobile devices
Implement persistent conversation feature with hybrid storage strategy
Feb 4, 2026
Copilot
AI
changed the title
Implement persistent conversation feature with hybrid storage strategy
Implement persistent conversation storage with automatic file restoration
Feb 4, 2026
Copilot
AI
changed the title
Implement persistent conversation storage with automatic file restoration
Implement persistent conversation feature with automatic file restoration
Feb 4, 2026
The Electron file picker (openFile dialog) returns the absolute path but it was discarded when creating the File object from the buffer. This caused all Electron-picked files to be stored as 'reselect-required' instead of 'electron-path', forcing manual reselection on every restore. Now FileDropZone passes the path from Electron's dialog result via a new 'paths' callback parameter. handleFilesSelected prefers this explicit path over file.path (drag-drop fallback).
- Toast: reset visibility and restart timer when message prop changes - FileDropZone: use showOpenFilePicker() on Chrome for file input to capture FileSystemFileHandle (drag-drop already captures handles; this fixes the regular "click to browse" flow) - Remove unused isFileSystemAccessSupported import from +page.svelte - Electron dialog path already fixed (result.path passed via paths callback parameter)
- Electron IPC: add lstat pre-check for cross-platform symlink rejection (O_NOFOLLOW not supported on Windows), with graceful fallback to O_RDONLY when O_NOFOLLOW unavailable - FileDropZone: enable multi-select in showOpenFilePicker to match the existing <input multiple> behavior - CSS: use :where(button) for low-specificity cursor rule so Tailwind utilities can override it
The reselect modal was using the web file input which doesn't capture the absolute file path in Electron. Now uses the Electron native dialog (electronAPI.openFile) which returns the path, and passes it through to updatePersistedChat so the entry is upgraded from 'reselect-required' to 'electron-path'. After one reselect, future restores work automatically.
…grades persisted entries The reselect modal was using plain <input type=file> on Chrome, which doesn't capture a FileSystemFileHandle. This meant even after reselecting, the entry stayed as 'reselect-required' and the user would be prompted again on every restart. Now: - Chrome/Edge: uses showOpenFilePicker() to capture a handle, then upgrades the persisted entry from 'reselect-required' to 'file-handle' via storeFileHandle + updatePersistedChat - Electron: uses native dialog to capture path, upgrades to 'electron-path' - Drag-drop in reselect modal also captures handle via getAsFileSystemHandle() After one reselect, future restores work automatically on all platforms.
Removing a chat only clears it from the current session. Persisted data in IndexedDB is preserved so the chat reappears in the restore modal on next launch. To permanently forget a chat, user must toggle Remember Conversation off.
The sidebar "Import chat" button was using a plain <input type=file> which doesn't reliably provide file.path in Electron. Now uses the same platform-aware import as the main drop zone: - Electron: native dialog via electronAPI.openFile() with path capture - Chrome/Edge: showOpenFilePicker() with handle capture - Fallback: regular file input Also fixes Node.js Buffer pool issue in dialog:openFile handler (was sending entire shared ArrayBuffer instead of just the file's portion).
a4decbd to
a744b92
Compare
…back - Replace dynamic import of storeFileHandle with static import - Remove 6 unused i18n keys from all 10 locale files - Remove dead exports: promptForFileHandle, getPersistedChat, getBookmarksForChatAsExport - Add error toast on restore failure in handleRestoreChats - Extract shared file-picker helpers (openZipFilePicker, openElectronFile, getElectronFilePath) - Add aria-label to FileDropZone drop zone - Rename persistence_close_notification to close_notification in Toast
…view feedback - Extract ChatAvatar component replacing 3 inline avatar implementations - Add shared formatRelativeDate helper (label/compact modes) - Add Button size="lg" variant with disabled styles - Vertically center Modal on all breakpoints, not just desktop - RestoreSessionModal: remove description banner, only chat list scrolls, fixed footer with action buttons - Fix message ID collisions across chats by including filename in hash - Fix Toast timer not restarting when message changes while visible - Gate reselect flow: only upgrade persisted entry when validation passes - Add persistence_restore_failed i18n key across all 10 locales - Remove unused isFileSystemAccessSupported import and function
github-actions Bot
pushed a commit
that referenced
this pull request
Apr 1, 2026
# [1.30.0](v1.29.4...v1.30.0) (2026-04-01) ### Bug Fixes * preserve original line content in parseLine for sender/content ([ec7c3da](ec7c3da)) ### Features * add persistent conversation feature with cross-platform file restoration ([#66](#66)) ([432bdb4](432bdb4)) * improve date format detection and resilience ([d8f9ddc](d8f9ddc)), closes [#69](#69)
Contributor
|
🎉 This PR is included in version 1.30.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
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.
Summary
Implements "Remember Conversation" toggle that persists chat sessions across app restarts using IndexedDB (
idb-keyval). Three platform strategies:FileSystemFileHandlein IndexedDBStores metadata, bookmarks, transcriptions, and settings (~1MB). Never stores the ZIP file itself.
Closes #65
New Files
src/lib/persistence.svelte.ts— Core persistence module (IndexedDB, file handles, validation)src/lib/components/RestoreSessionModal.svelte— Multi-chat restore selection UIsrc/lib/components/ReselectFileModal.svelte— File reselect with drag-dropsrc/lib/components/Toast.svelte— Notification toastssrc/lib/components/Modal.svelte— Generic modal wrappersrc/lib/components/ModalContent.svelte— Modal content wrappersrc/lib/helpers/format.ts— Shared text utilitiesModified Files
src/routes/+page.svelte— Restore flow, remember toggle, toast integrationsrc/lib/components/ChatList.svelte— "Remember Conversation" context menu itemsrc/lib/bookmarks.svelte.ts— Export bookmarks by chat IDsrc/lib/transcription.svelte.ts— Get/set transcriptions for persistenceelectron/main.cjs— Secure file read IPC handlerelectron/preload.cjs— Expose file read APImessages/*.json— 18+ i18n strings across 10 languagesChanges from Original Copilot PR
Rebased onto current main and fixed:
$effectguarded against re-firing with one-time flag.zip-only check, symlink protection, async readstartIndexWorker()andmakeProgressCallback()— eliminated ~150 duplicated linesgetInitials()utilityValidation
npm run check— 0 errors, 0 warningsnpm run lint— 0 issuesnpm run build— passesTest Plan