Skip to content

feat: add persistent conversation feature with cross-platform file restoration#66

Merged
rodrigogs merged 33 commits into
devfrom
copilot/add-persistent-conversation-feature
Apr 1, 2026
Merged

feat: add persistent conversation feature with cross-platform file restoration#66
rodrigogs merged 33 commits into
devfrom
copilot/add-persistent-conversation-feature

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 4, 2026

Summary

Implements "Remember Conversation" toggle that persists chat sessions across app restarts using IndexedDB (idb-keyval). Three platform strategies:

Platform Storage Restore
Electron Absolute file path Reads from disk automatically
Chrome/Edge FileSystemFileHandle in IndexedDB Re-requests permission, reads handle
Firefox/Safari Metadata only Prompts user to re-select file

Stores 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 UI
  • src/lib/components/ReselectFileModal.svelte — File reselect with drag-drop
  • src/lib/components/Toast.svelte — Notification toasts
  • src/lib/components/Modal.svelte — Generic modal wrapper
  • src/lib/components/ModalContent.svelte — Modal content wrapper
  • src/lib/helpers/format.ts — Shared text utilities

Modified Files

  • src/routes/+page.svelte — Restore flow, remember toggle, toast integration
  • src/lib/components/ChatList.svelte — "Remember Conversation" context menu item
  • src/lib/bookmarks.svelte.ts — Export bookmarks by chat ID
  • src/lib/transcription.svelte.ts — Get/set transcriptions for persistence
  • electron/main.cjs — Secure file read IPC handler
  • electron/preload.cjs — Expose file read API
  • messages/*.json — 18+ i18n strings across 10 languages

Changes from Original Copilot PR

Rebased onto current main and fixed:

  • Critical: Restore loop now properly awaits user file reselection (was skipping ahead)
  • Critical: $effect guarded against re-firing with one-time flag
  • Critical: Electron IPC hardened with path normalization, .zip-only check, symlink protection, async read
  • DRY: Extracted startIndexWorker() and makeProgressCallback() — eliminated ~150 duplicated lines
  • DRY: Extracted shared getInitials() utility
  • Code quality: Fixed Toast timer leak, dead animation, dead code, unused imports
  • i18n: Replaced all hardcoded English strings with Paraglide messages
  • UX: Fixed drag-drop flicker with counter pattern
  • Added error logging to 5 silent catch blocks
  • Resolved all 15 type errors
  • Removed internal AI handoff document

Validation

  • npm run check — 0 errors, 0 warnings
  • npm run lint — 0 issues
  • npm run build — passes

Test Plan

  • Web (Chrome): Load chat > toggle "Remember Conversation" > reload > verify restore modal > restore chat
  • Web (Chrome): Verify File System Access API flow (file picker, grant access, auto-restore)
  • Web (Firefox): Verify reselect modal appears (no File System Access API)
  • Web: Remove remembered chat > verify IndexedDB cleanup
  • Web: Toggle "Don't show again" > reload > verify modal hidden
  • Electron: Full restore flow with file path persistence
  • Edge case: Select wrong file during reselect > verify validation

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 requested a review from rodrigogs February 4, 2026 19:04
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
rodrigogs added 10 commits April 1, 2026 12:11
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).
@rodrigogs rodrigogs force-pushed the copilot/add-persistent-conversation-feature branch from a4decbd to a744b92 Compare April 1, 2026 15:14
@rodrigogs rodrigogs requested a review from Copilot April 1, 2026 15:33
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 29 out of 31 changed files in this pull request and generated 3 comments.

Comment thread src/routes/+page.svelte Outdated
Comment thread src/routes/+page.svelte
Comment thread src/lib/transcription.svelte.ts
…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
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 32 changed files in this pull request and generated 3 comments.

Comment thread src/routes/+page.svelte Outdated
Comment thread src/routes/+page.svelte
Comment thread src/lib/components/FileDropZone.svelte
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 32 changed files in this pull request and generated 2 comments.

Comment thread src/routes/+page.svelte Outdated
Comment thread src/lib/components/FileDropZone.svelte Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 32 changed files in this pull request and generated 2 comments.

Comment thread src/routes/+page.svelte Outdated
Comment thread src/routes/+page.svelte Outdated
…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
@rodrigogs rodrigogs merged commit 432bdb4 into dev Apr 1, 2026
4 checks passed
@rodrigogs rodrigogs deleted the copilot/add-persistent-conversation-feature branch April 1, 2026 20:27
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)
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 1, 2026

🎉 This PR is included in version 1.30.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Constantly having to Import Zip everytime it Opens - Add Database Chat Storage

3 participants