commits
Move sync command from cmd/commands/sync.js to its own extension at
extensions/sync/. This follows the same pattern as the groups extension,
using the cmd:ready subscription pattern to register commands.
Add detailed documentation for iOS build system learned through debugging:
- custom-protocol Cargo feature explanation (CRITICAL for production builds)
- Debug vs Release build separation (targets, caches, libraries)
- Why cargo build --lib instead of cargo tauri build (Swift FFI symbols)
- iOS cache system at repo root for workspace sharing
- Build scripts reference and workflows
- App Group containers and UUID lifecycle (when data persists vs lost)
- Troubleshooting guides: localhost error, builds not updating, wrong architecture
Also documents:
- Native WKWebView integration
- Pull-to-refresh with hold gesture
- In-app icon button styling
- Per-profile database files
- Cross-reference to docs/profiles.md
chore: remove accidentally committed backup and cache files
- Remove device-backup-20260204-143925/ directory (backup is in ~/sync/peek-backups/)
- Add device-backup-*/ to .gitignore to prevent future accidents
- Removes ~140 binary WebKit cache files that were accidentally committed
Remove generated timestamp lines from codegen output. These timestamps
change on every run, causing files to appear modified even when source
schema hasn't changed. The generated files still include schema version
and the regeneration command for reference.
Enhances the browser extension popup with:
- Success/error feedback for all commands (green checkmark or red X)
- Sync status bar showing last sync time, pending count, manual sync button
- New commands: server, key, sync for managing sync configuration
Commands:
- tag <tags>: Tag current URL with comma-separated tags
- note <text>: Save text note attached to current page
- search <query>: Search saved items
- server [url]: Show or set sync server URL
- key [apikey]: Show (masked) or set API key
- sync: Trigger sync, shows pull/push counts
UI auto-closes after success (600ms), stays open on error.
Restores mode indicator to command bar:
- Mode indicator shows current mode (page/group/settings)
- Click to cycle through modes
- Auto-hides when in default mode
- Auto-detects page mode for http/https URLs
Originally developed in orphaned commits ysvntsoz..xsvtrksy (Jan 31)
- Add scripts/check-native-modules.js to ensure better-sqlite3 is compiled for Electron's Node ABI
- Add engines field requiring Node >=24.0.0
- Add check:native script and integrate into start:electron and test:electron
- Add trackNavigation API to preload.js datastore
- Import detectModeFromUrl in ipc.ts and set window mode based on URL
- Extract actual URLs from peek://app/page containers in window-list handler
Pull-to-refresh was triggering too easily on accidental pulls. Now requires:
- Pull past threshold (80px)
- Hold for 300ms
- Release while in 'ready' state
Added visual indicator showing 'Hold to refresh...' and 'Release to refresh!'
with distinct styling for each state.
- Fix webview plugin by using direct Tauri commands instead of plugin routing
- Add open_native_webview and close_native_webview commands in lib.rs
- These call Swift FFI functions directly, bypassing iOS plugin system issues
- Frontend now invokes open_native_webview instead of plugin:webview|open_webview
- Restore iOS icon to new single-file format
- Convert AppIcon.appiconset to use single 1024x1024 universal icon
- Use custom Peek clouds icon instead of Tauri default
- Add sim:log:webview script for debugging webview-related logs
- Simplify webview plugin to stub commands (actual work done via direct FFI)
- Rewrite WebviewPlugin.swift to not depend on Tauri Swift API
- Uses standalone C-compatible FFI functions (@_cdecl)
- Remove Swift Package Manager dependency from project.yml
- Update Rust plugin to call FFI functions directly
- Disable prebuild script to use pre-built Rust library
Add two icons to URL items in the mobile app:
- Open in Browser: Opens URL in system default browser (Safari)
- Open in Webview: Opens URL in embedded native WKWebView
Plus: Track visits when URLs are loaded/navigated in the webview.
Changes:
- Add item_visits table to database schema for tracking URL visits
- Create tauri-plugin-webview plugin (stub for cargo, native for Xcode)
- Add record_visit Tauri command
- Add openInBrowser and openInWebview handlers to frontend
- Listen for webview:navigated events to record subsequent visits
- Add card-action-btn styles for the new icons
Delete handlers that were never called from the codebase:
- dark-mode:toggle, dark-mode:system (legacy, replaced by theme:setColorScheme)
- window-set-always-on-top, window-is-always-on-top (never wired up)
- window-state-save, window-state-load (never wired up)
- get-target-window-info (never wired up)
155 lines removed.
- Delete app/cmd/ (24 files) - vestigial, replaced by extensions/cmd/
- Delete app/scripts/ (2 files) - disabled feature with missing HTML files
- Remove dead functions from app/index.js (uninitFeature, initIframeFeature)
- Remove Cmd and Scripts entries from app/config.js (non-existent URLs)
- Simplify app/features.js (all features are now extensions)
- Remove commented-out sendToWindow/onMessage APIs from preload.js
- Fix ESC handling for Active mode windows (IZUI policy compliance)
- Add getFocusedVisibleWindowId and trackNavigation APIs to preload.js
- Update test to use peek://ext/cmd/panel.html and proper ArrowDown interaction
- Add tests/fixtures/extension-app.ts with Chrome/Firefox support
- launchExtension() launches browser with extension loaded
- getSharedExtension()/closeSharedExtension() for shared instance
- Chrome uses --load-extension flag, Firefox uses temp profile
- Auto-detects extension ID from service worker URL
- Add tests/helpers/extension-utils.ts with popup/options helpers
- waitForCommandInput(), typeCommand(), tabComplete(), executeCommand()
- waitForOptionsInit(), fillSyncConfig(), getDiagnostics()
- Toggle helpers for bookmark/tab/history sync features
- Add tests/extension/popup.spec.ts with 15 command bar tests
- Placeholder text, autocomplete suggestions
- Tab completion, Enter execution, Escape close
- Case preservation, args mode behavior
- Add tests/extension/options.spec.ts with 26 options page tests
- Page structure, sync config, diagnostics display
- Refresh button, feature toggles
- Update playwright.config.ts with extension-chrome/firefox projects
- Add package.json scripts:
- test:extension:browser (Chrome)
- test:extension:browser:firefox (Firefox)
- test:extension:browser:visible (headed mode)
resolve: regenerate schema
resolve: regenerate schema
- Remove unused "Features" section from core settings pane
- Update datastore viewer stats to show Items/Tags/Visits instead of
old Addresses/Visits/Content
- Add totalItems and totalTags to DatastoreStats type
- Remove outdated localStorage migration tools from diagnostic pane
(kept backup tools which are still useful)
Update getValidURL in both open.js files to:
- Add optional path pattern (\/.*)?$ to domain regex so URLs like
"google.com/maps" are recognized
- Add localhost pattern with optional port and path support
The panel.js files already had this fix, but the open.js command
validators were rejecting URLs with paths because the regex ended
at the TLD without allowing a path component.
- Add auto-save when editing notes (debounced 500ms, silent save)
- Change Save button to "Done" for note editing
- Restore original content when canceling after auto-save
- More space at top of editor (5rem padding)
- Hide clear button when editing existing items
- Add swipe-down-to-close with dedicated drag handle
- Counter iOS visual viewport scrolling on input focus
- Use fixed keyboard height (350px) for layout stability
- Add empty tags messages ("Add some tags!", "No matching tags")
- Collapsed state for tag section when no tags exist
- Add version marker in header for build verification (v458)
- Native Objective-C swizzle disables input accessory view
- Add comprehensive e2e tests for browser extension sync:
- Browser imports (history, bookmarks, tabs) stay local (syncSource prevents push)
- User-added items (notes, URLs) sync bidirectionally
- Deduplication behavior
- Tags and metadata handling
- Preserve original timestamps as createdAt during imports:
- History: uses earliest visit time
- Bookmarks: uses dateAdded
- User items: use current time (default)
- DataEngine.addItem() now accepts optional createdAt parameter for imports
Tests: 129 passing (all extension tests)
- Minimal UI matching desktop cmd panel style (dark, transparent input)
- Commands: tag, note, search with inline autocomplete
- UI closes immediately on Enter (fire-and-forget)
- Backend saves async via datastore, decoupled from UI
- Removed dropdown/results for now - just input field
- Uses Playwright for dev launch (Google Chrome blocks --load-extension)
Usage: yarn extension:chrome
Then Option+P or click extension icon
Type: "tag foo, bar" or "note my note" and Enter
Add a simple command bar popup to the browser extension with three
basic commands:
- tag: Add tags to the current page (comma-separated)
- note: Save a text note about the current page
- search: Search saved items by title, URL, or content
The popup UI follows the same patterns as the desktop cmd panel:
- Dark/light mode support via prefers-color-scheme
- Keyboard navigation (arrows, tab, enter, escape)
- Results list with icons and descriptions
Backend handlers in background.js:
- handleSaveTags: Creates/updates URL item and applies tags
- handleSaveNote: Creates text item with optional source URL metadata
- handleSearchItems: Filters items by title/content/URL match
- Create config.js with loadConfig() for mode detection
- Create auth.js with middleware factory (single-user/multi-user)
- Update db.js with getProfileDir() for mode-aware path logic
- Update index.js to use auth middleware factory
- Skip multi-user migrations/backups in single-user mode
Single-user mode path: DATA_DIR/profiles/{profileId}/
Multi-user mode path: DATA_DIR/{userId}/profiles/{profileId}/
Environment variables:
- SINGLE_USER_MODE=true to enable
- SINGLE_USER_ID=<id> (default: 'default')
- SINGLE_USER_TOKEN=<token> for optional auth
docs(server): add ARCHITECTURE.md for portability abstractions
- Create storage/types.js with StorageAdapter interface
- Create storage/filesystem-adapter.js with sync/async methods
- Create storage/index.js factory for storage adapter selection
- Refactor db.js saveImage/getImageData to use storage adapter
- Refactor index.js image serving to use getImageData
- Hash-based storage keys for content-addressable storage
Prepares for future R2/S3 backend switching.
resolve: regenerate schema files
Phase 1 of server portability plan. Abstracts better-sqlite3 behind
SqlAdapter interface to enable future support for Cloudflare Durable
Objects SQLite.
- Create sql/types.js with SqlAdapter/SqlAdapterFactory interfaces
- Create sql/better-sqlite3-adapter.js implementing the interface
- Create sql/index.js factory for adapter selection
- Refactor db.js to use adapter instead of direct better-sqlite3 calls
- Refactor users.js to use adapter for system.db
- Update backup.js with TODO for DO SQLite backup strategy
- Update tests to use adapter methods (conn.all/get vs conn.prepare)
All 110 tests pass.
- Web pages (http/https) now load in a fullscreen transparent BrowserWindow
- Webview is positioned at caller-specified coordinates on the invisible canvas
- Floating navbar positioned ABOVE webview with 8px gap
- Navbar shows on hover (trigger zone above webview)
- Webview is draggable via navbar and resizable via corner handle
- Removed old overlay extension (navbar.html/js/css, background.js)
- IZUI integration for ESC key handling (go back or close)
- Added dock.hide() in headless mode to prevent focus stealing
- Added background test scripts to package.json
Implemented comprehensive ESC key handling for the navigation stack:
- Changed default escapeMode from 'close' to 'auto':
- Transient windows (hotkey-opened) close on ESC
- Active windows (app-focused) trigger IZUI navigation instead
- Added 'navigate' and 'ignore' escape modes:
- 'navigate': ESC triggers internal navigation, never closes
- 'ignore': ESC passes through (for non-focusable overlays)
- Preload API wiring:
- Added api.window.getWindowId() for window identification
- Default ESC handler publishes izui:child-escape and closes
- Custom handlers can override via api.escape.onEscape()
- IZUI navigation stack in app/izui.js:
- Tracks child windows via navigationStack
- setupChildEscapeListener() handles child escape events
- Parent windows regain focus when children close
- Fixed overlay ESC interception:
- Overlays now use focusable: false + escapeMode: 'ignore'
- ESC events pass through to underlying web pages
Windows now behave based on how they were opened:
- Transient windows (opened without app focus) close on ESC
- Active windows (opened while app focused) trigger IZUI navigation
This removes the need for explicit escapeMode: 'navigate' on parent
windows like settings and groups - they get the right behavior by
default since they're always opened while the app is focused.
- Remove 50ms delay before closing
- Get window ID before responding to backend
- Add DEBUG logging to trace ESC handling
Add detailed documentation for iOS build system learned through debugging:
- custom-protocol Cargo feature explanation (CRITICAL for production builds)
- Debug vs Release build separation (targets, caches, libraries)
- Why cargo build --lib instead of cargo tauri build (Swift FFI symbols)
- iOS cache system at repo root for workspace sharing
- Build scripts reference and workflows
- App Group containers and UUID lifecycle (when data persists vs lost)
- Troubleshooting guides: localhost error, builds not updating, wrong architecture
Also documents:
- Native WKWebView integration
- Pull-to-refresh with hold gesture
- In-app icon button styling
- Per-profile database files
- Cross-reference to docs/profiles.md
chore: remove accidentally committed backup and cache files
- Remove device-backup-20260204-143925/ directory (backup is in ~/sync/peek-backups/)
- Add device-backup-*/ to .gitignore to prevent future accidents
- Removes ~140 binary WebKit cache files that were accidentally committed
Enhances the browser extension popup with:
- Success/error feedback for all commands (green checkmark or red X)
- Sync status bar showing last sync time, pending count, manual sync button
- New commands: server, key, sync for managing sync configuration
Commands:
- tag <tags>: Tag current URL with comma-separated tags
- note <text>: Save text note attached to current page
- search <query>: Search saved items
- server [url]: Show or set sync server URL
- key [apikey]: Show (masked) or set API key
- sync: Trigger sync, shows pull/push counts
UI auto-closes after success (600ms), stays open on error.
- Add scripts/check-native-modules.js to ensure better-sqlite3 is compiled for Electron's Node ABI
- Add engines field requiring Node >=24.0.0
- Add check:native script and integrate into start:electron and test:electron
- Add trackNavigation API to preload.js datastore
- Import detectModeFromUrl in ipc.ts and set window mode based on URL
- Extract actual URLs from peek://app/page containers in window-list handler
- Fix webview plugin by using direct Tauri commands instead of plugin routing
- Add open_native_webview and close_native_webview commands in lib.rs
- These call Swift FFI functions directly, bypassing iOS plugin system issues
- Frontend now invokes open_native_webview instead of plugin:webview|open_webview
- Restore iOS icon to new single-file format
- Convert AppIcon.appiconset to use single 1024x1024 universal icon
- Use custom Peek clouds icon instead of Tauri default
- Add sim:log:webview script for debugging webview-related logs
- Simplify webview plugin to stub commands (actual work done via direct FFI)
Add two icons to URL items in the mobile app:
- Open in Browser: Opens URL in system default browser (Safari)
- Open in Webview: Opens URL in embedded native WKWebView
Plus: Track visits when URLs are loaded/navigated in the webview.
Changes:
- Add item_visits table to database schema for tracking URL visits
- Create tauri-plugin-webview plugin (stub for cargo, native for Xcode)
- Add record_visit Tauri command
- Add openInBrowser and openInWebview handlers to frontend
- Listen for webview:navigated events to record subsequent visits
- Add card-action-btn styles for the new icons
Delete handlers that were never called from the codebase:
- dark-mode:toggle, dark-mode:system (legacy, replaced by theme:setColorScheme)
- window-set-always-on-top, window-is-always-on-top (never wired up)
- window-state-save, window-state-load (never wired up)
- get-target-window-info (never wired up)
155 lines removed.
- Delete app/cmd/ (24 files) - vestigial, replaced by extensions/cmd/
- Delete app/scripts/ (2 files) - disabled feature with missing HTML files
- Remove dead functions from app/index.js (uninitFeature, initIframeFeature)
- Remove Cmd and Scripts entries from app/config.js (non-existent URLs)
- Simplify app/features.js (all features are now extensions)
- Remove commented-out sendToWindow/onMessage APIs from preload.js
- Fix ESC handling for Active mode windows (IZUI policy compliance)
- Add getFocusedVisibleWindowId and trackNavigation APIs to preload.js
- Update test to use peek://ext/cmd/panel.html and proper ArrowDown interaction
- Add tests/fixtures/extension-app.ts with Chrome/Firefox support
- launchExtension() launches browser with extension loaded
- getSharedExtension()/closeSharedExtension() for shared instance
- Chrome uses --load-extension flag, Firefox uses temp profile
- Auto-detects extension ID from service worker URL
- Add tests/helpers/extension-utils.ts with popup/options helpers
- waitForCommandInput(), typeCommand(), tabComplete(), executeCommand()
- waitForOptionsInit(), fillSyncConfig(), getDiagnostics()
- Toggle helpers for bookmark/tab/history sync features
- Add tests/extension/popup.spec.ts with 15 command bar tests
- Placeholder text, autocomplete suggestions
- Tab completion, Enter execution, Escape close
- Case preservation, args mode behavior
- Add tests/extension/options.spec.ts with 26 options page tests
- Page structure, sync config, diagnostics display
- Refresh button, feature toggles
- Update playwright.config.ts with extension-chrome/firefox projects
- Add package.json scripts:
- test:extension:browser (Chrome)
- test:extension:browser:firefox (Firefox)
- test:extension:browser:visible (headed mode)
- Remove unused "Features" section from core settings pane
- Update datastore viewer stats to show Items/Tags/Visits instead of
old Addresses/Visits/Content
- Add totalItems and totalTags to DatastoreStats type
- Remove outdated localStorage migration tools from diagnostic pane
(kept backup tools which are still useful)
Update getValidURL in both open.js files to:
- Add optional path pattern (\/.*)?$ to domain regex so URLs like
"google.com/maps" are recognized
- Add localhost pattern with optional port and path support
The panel.js files already had this fix, but the open.js command
validators were rejecting URLs with paths because the regex ended
at the TLD without allowing a path component.
- Add auto-save when editing notes (debounced 500ms, silent save)
- Change Save button to "Done" for note editing
- Restore original content when canceling after auto-save
- More space at top of editor (5rem padding)
- Hide clear button when editing existing items
- Add swipe-down-to-close with dedicated drag handle
- Counter iOS visual viewport scrolling on input focus
- Use fixed keyboard height (350px) for layout stability
- Add empty tags messages ("Add some tags!", "No matching tags")
- Collapsed state for tag section when no tags exist
- Add version marker in header for build verification (v458)
- Native Objective-C swizzle disables input accessory view
- Add comprehensive e2e tests for browser extension sync:
- Browser imports (history, bookmarks, tabs) stay local (syncSource prevents push)
- User-added items (notes, URLs) sync bidirectionally
- Deduplication behavior
- Tags and metadata handling
- Preserve original timestamps as createdAt during imports:
- History: uses earliest visit time
- Bookmarks: uses dateAdded
- User items: use current time (default)
- DataEngine.addItem() now accepts optional createdAt parameter for imports
Tests: 129 passing (all extension tests)
- Minimal UI matching desktop cmd panel style (dark, transparent input)
- Commands: tag, note, search with inline autocomplete
- UI closes immediately on Enter (fire-and-forget)
- Backend saves async via datastore, decoupled from UI
- Removed dropdown/results for now - just input field
- Uses Playwright for dev launch (Google Chrome blocks --load-extension)
Usage: yarn extension:chrome
Then Option+P or click extension icon
Type: "tag foo, bar" or "note my note" and Enter
Add a simple command bar popup to the browser extension with three
basic commands:
- tag: Add tags to the current page (comma-separated)
- note: Save a text note about the current page
- search: Search saved items by title, URL, or content
The popup UI follows the same patterns as the desktop cmd panel:
- Dark/light mode support via prefers-color-scheme
- Keyboard navigation (arrows, tab, enter, escape)
- Results list with icons and descriptions
Backend handlers in background.js:
- handleSaveTags: Creates/updates URL item and applies tags
- handleSaveNote: Creates text item with optional source URL metadata
- handleSearchItems: Filters items by title/content/URL match
- Create config.js with loadConfig() for mode detection
- Create auth.js with middleware factory (single-user/multi-user)
- Update db.js with getProfileDir() for mode-aware path logic
- Update index.js to use auth middleware factory
- Skip multi-user migrations/backups in single-user mode
Single-user mode path: DATA_DIR/profiles/{profileId}/
Multi-user mode path: DATA_DIR/{userId}/profiles/{profileId}/
Environment variables:
- SINGLE_USER_MODE=true to enable
- SINGLE_USER_ID=<id> (default: 'default')
- SINGLE_USER_TOKEN=<token> for optional auth
docs(server): add ARCHITECTURE.md for portability abstractions
- Create storage/types.js with StorageAdapter interface
- Create storage/filesystem-adapter.js with sync/async methods
- Create storage/index.js factory for storage adapter selection
- Refactor db.js saveImage/getImageData to use storage adapter
- Refactor index.js image serving to use getImageData
- Hash-based storage keys for content-addressable storage
Prepares for future R2/S3 backend switching.
resolve: regenerate schema files
Phase 1 of server portability plan. Abstracts better-sqlite3 behind
SqlAdapter interface to enable future support for Cloudflare Durable
Objects SQLite.
- Create sql/types.js with SqlAdapter/SqlAdapterFactory interfaces
- Create sql/better-sqlite3-adapter.js implementing the interface
- Create sql/index.js factory for adapter selection
- Refactor db.js to use adapter instead of direct better-sqlite3 calls
- Refactor users.js to use adapter for system.db
- Update backup.js with TODO for DO SQLite backup strategy
- Update tests to use adapter methods (conn.all/get vs conn.prepare)
All 110 tests pass.
- Web pages (http/https) now load in a fullscreen transparent BrowserWindow
- Webview is positioned at caller-specified coordinates on the invisible canvas
- Floating navbar positioned ABOVE webview with 8px gap
- Navbar shows on hover (trigger zone above webview)
- Webview is draggable via navbar and resizable via corner handle
- Removed old overlay extension (navbar.html/js/css, background.js)
- IZUI integration for ESC key handling (go back or close)
- Added dock.hide() in headless mode to prevent focus stealing
- Added background test scripts to package.json
Implemented comprehensive ESC key handling for the navigation stack:
- Changed default escapeMode from 'close' to 'auto':
- Transient windows (hotkey-opened) close on ESC
- Active windows (app-focused) trigger IZUI navigation instead
- Added 'navigate' and 'ignore' escape modes:
- 'navigate': ESC triggers internal navigation, never closes
- 'ignore': ESC passes through (for non-focusable overlays)
- Preload API wiring:
- Added api.window.getWindowId() for window identification
- Default ESC handler publishes izui:child-escape and closes
- Custom handlers can override via api.escape.onEscape()
- IZUI navigation stack in app/izui.js:
- Tracks child windows via navigationStack
- setupChildEscapeListener() handles child escape events
- Parent windows regain focus when children close
- Fixed overlay ESC interception:
- Overlays now use focusable: false + escapeMode: 'ignore'
- ESC events pass through to underlying web pages
Windows now behave based on how they were opened:
- Transient windows (opened without app focus) close on ESC
- Active windows (opened while app focused) trigger IZUI navigation
This removes the need for explicit escapeMode: 'navigate' on parent
windows like settings and groups - they get the right behavior by
default since they're always opened while the app is focused.