diff --git a/AUDIT_LOG.md b/AUDIT_LOG.md index db40a18..c14bb05 100644 --- a/AUDIT_LOG.md +++ b/AUDIT_LOG.md @@ -2,6 +2,20 @@ This log tracks all significant changes, updates, and versions in the PaperCache project. +## 2026-06-25 - (Uncommitted) +**Change:** fix: window state persistence and login-item toggle desync (v0.5.4) + +**Details/Why:** +Two bug fixes for window state and settings reliability: + +1. **Window position/size not persisting across restarts**: The `tauri-plugin-window-state` v2.4.1's `on_window_ready` fires before the macOS display server is ready, causing `available_monitors()` to return empty and the saved position to be silently discarded. Fixed with a two-pronged approach: (a) `lib.rs:107-140` spawns a background thread that sleeps 300ms then dispatches `restore_state` + direct file read via `run_on_main_thread` — ensures display server is ready; (b) `commands/system.rs:33-73` new `restore_window_state` command reads `.window-state.json` directly and calls `set_position`/`set_size`, bypassing the plugin's intersection check as a fallback. Both tray "Quit" and Settings "Quit" now call `app.save_window_state()` explicitly before `app.exit(0)`. + +2. **Launch-at-startup toggle desync with macOS System Settings**: The toggle only read from `localStorage`, so removing PaperCache from System Settings left it permanently stuck on. Fixed by adding `get_launch_at_startup` Tauri command (`system.rs:96-98`) that queries `app.autolaunch().is_enabled()`, bridged to frontend via `api.ts`/`types.d.ts`. `Settings.tsx:52-59` runs `getLaunchAtStartup()` on mount to sync toggle and `localStorage` with real OS state. + +**Files changed:** `src-tauri/src/commands/system.rs`, `src-tauri/src/tray.rs`, `src-tauri/src/lib.rs`, `src/App.tsx:56`, `src/Settings.tsx:48-61`, `src/Settings.test.tsx`, `src/types.d.ts`, `src/api.ts`, `CHANGELOG.md`. + +--- + ## 2026-06-24 - (Uncommitted) **Change:** feat: graph view rebuilt, Windows focus-loss fix, Cmd+/ shortcuts, welcome revamp (v0.5.3) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76a2e2f..4f1bc80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable, user-facing changes to PaperCache will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v0.5.4] - 2026-06-25 + +### Fixed +- **Window position/size now persists across restarts**: The window-state plugin's `on_window_ready` fires before the macOS display server is ready, causing `available_monitors()` to return empty and the saved position to be silently discarded. Fixed by deferring window-state restoration via a background thread + `run_on_main_thread` 300ms after `setup()` completes, bypassing the plugin's monitor-intersection check with a direct file read. Both the tray "Quit" and Settings "Quit" buttons now explicitly save window state before exit. +- **Login-item toggle stays in sync with macOS System Settings**: The launch-at-startup toggle only read from `localStorage`, so removing PaperCache from System Settings left the toggle permanently stuck in the checked state. Fixed by adding a `get_launch_at_startup` Tauri command that queries the actual OS login-item state via `app.autolaunch().is_enabled()`, and syncing the toggle with the real OS state on every Settings mount. + ## [v0.5.3] - 2026-06-24 ### Added diff --git a/package-lock.json b/package-lock.json index 5933482..abdc13c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -709,7 +709,6 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", - "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -722,7 +721,6 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", - "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -1526,14 +1524,14 @@ "license": "MIT" }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", - "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.2" + "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", @@ -1568,9 +1566,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.2.tgz", - "integrity": "sha512-2cZ+7xRS+DBcuJBJKnfzsbleumJhBqSlJVpuzHC0nTqfd3QQ7Vx2/x5YR/D7cBamKSeWplwo82Fn9lqYUDEMfA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz", + "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==", "cpu": [ "arm64" ], @@ -1585,9 +1583,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.2.tgz", - "integrity": "sha512-RkPMJnygxsgOYdkfqgpwY0/Fzm8d0VQe6HGU2/B00Xa9eqdLbrII+DOKAodbJAn3ZL1AJxGHkZRPYazgGY6Ljw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz", + "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==", "cpu": [ "arm64" ], @@ -1602,9 +1600,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.2.tgz", - "integrity": "sha512-Uiczh6vFhwyfd7WNe7Q7mCA4KxAiLdz7jPE/WGizfRpIieoyFuNVMmM8HqZ9HwudTkY6/AeMQwlNJ9NJijguWw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz", + "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==", "cpu": [ "x64" ], @@ -1619,9 +1617,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.2.tgz", - "integrity": "sha512-+TpdtTRgHiJFjCVFbw311SuLk3KfytPOQQn+VlAEv+gBxYPtL7E6JS9e/tk+8CwxhIZvemJKo4rTKgfWNsKkkA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz", + "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==", "cpu": [ "x64" ], @@ -1636,9 +1634,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.2.tgz", - "integrity": "sha512-4lv1/tkmi7ueIVHnyreaOeUpiZP26BH9rRy6hoYfR9310A2B9nUEVRDvBx69vx64Nr3eTPPRkyciqJJs+j9Jmw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz", + "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==", "cpu": [ "arm" ], @@ -1653,9 +1651,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.2.tgz", - "integrity": "sha512-gBSUVO0eaWgw1JMjK3gB8BMlX2Mk148s2lTiVT3e9vjVxbl7UDfMWWY8CfIaaqiXuM9fVTMxIpUz6CAo/B6Vlw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz", + "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==", "cpu": [ "arm64" ], @@ -1670,9 +1668,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.2.tgz", - "integrity": "sha512-LjQP/iZLBu8o8PjIfk4x3At0/mT6h282pvz8Z5LAyhGbu/kDezyO7ea62rF5uoqmgnIYqbN/MqJ3Si3Aymi7xQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz", + "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==", "cpu": [ "arm64" ], @@ -1687,9 +1685,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.2.tgz", - "integrity": "sha512-X/7bVLWelEsbyWDUSXt7zVsTniLLPIY2n1rH58qr78l9i7MNbbxBWD8gI2vRfBWf4NUXJCUuQnfZDsp32LqsfQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz", + "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==", "cpu": [ "ppc64" ], @@ -1704,9 +1702,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.2.tgz", - "integrity": "sha512-gb6dYKW/1KDorGXyy48glEBJs/sxVSC5pcVrox/pFGV4mvwSFeg2sK5L2tRkVsVlh7kueqOgg4GEcuipJcGuKg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz", + "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==", "cpu": [ "s390x" ], @@ -1721,9 +1719,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.2.tgz", - "integrity": "sha512-JY4w85pU3iAiJVMh5nuk4/Mh9GjMsupe8MrIN53rwxAZW64GKrWeJBuN6SxQg9QTU5uB1cxyhDzW8jqRn1EABw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz", + "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==", "cpu": [ "x64" ], @@ -1738,9 +1736,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.2.tgz", - "integrity": "sha512-xvpA7o5KCYLB0Rwscmuylb1/zHHSUx4g4xilm4prC5jP76pEUlzBmMbgpbh7bVDbId4NcfT96gN5i6mE6UDaiw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz", + "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==", "cpu": [ "x64" ], @@ -1755,9 +1753,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.2.tgz", - "integrity": "sha512-p/ts6KBLjuk49Bp21XH77poQGt02iNz7ChgHep7tudPOaLinR/De/RHdxF8w8Yj4r/bF/bqXwH6PZrB2sA+Nvw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz", + "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==", "cpu": [ "arm64" ], @@ -1772,9 +1770,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.2.tgz", - "integrity": "sha512-VMu/wmrZ9hJzYlRhbw7jK5PODlugyKZ5mOdX78+lS8OvuFkWNQdz1pFLrI2p3P0pjXOmUZ7B48o5VnMH9QOGtg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz", + "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==", "cpu": [ "wasm32" ], @@ -1784,16 +1782,16 @@ "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", - "@napi-rs/wasm-runtime": "^1.1.5" + "@napi-rs/wasm-runtime": "^1.1.6" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.2.tgz", - "integrity": "sha512-xtUJqs8qEkuSviS0n1tsohaPuz3a1SPhZywOji4Oo+sgrJs8daEDMZ0QtqL0OS7dx8PoVpg2J/ZZycPY5I2+Zg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz", + "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==", "cpu": [ "arm64" ], @@ -1808,9 +1806,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.2.tgz", - "integrity": "sha512-85YiLQqjUKgSO/Zjnf9e0XIn5Ymrh1fLDWBeAkZqpuBR/3R8TpfoHXuyblqyQrftSSgWO9qpcHN8mkyKsLraoA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz", + "integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==", "cpu": [ "x64" ], @@ -4590,9 +4588,9 @@ } }, "node_modules/listr2": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-10.2.1.tgz", - "integrity": "sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-10.2.2.tgz", + "integrity": "sha512-JtNtbZj8q5BnDMR7trpwvwk3RIrANtIVzEUm8w7amp6xelLgyuq+4WZoTH913XaQAoH/cNdYhaNzBPA2U3xbDw==", "dev": true, "license": "MIT", "dependencies": { @@ -4915,9 +4913,9 @@ "license": "BSD-3-Clause" }, "node_modules/node-releases": { - "version": "2.0.49", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.49.tgz", - "integrity": "sha512-f06bl1D+8ZDkn2oOQQKAh5/otFWqVnM1Q5oerA8Pex7UfT66Tx4IPHIqVVFKqFT3FUtaDstdgkM7yT7JWhqxfw==", + "version": "2.0.50", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.50.tgz", + "integrity": "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==", "dev": true, "license": "MIT", "engines": { @@ -5318,9 +5316,9 @@ "license": "MIT" }, "node_modules/rolldown": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.2.tgz", - "integrity": "sha512-x0CrQQqCXWGeI8dTvFfN/Dnv3yMKT9hv5jFjlOreKAx9wqLq9wz7VvLLHyaAXC90/CpggTu9SisSbsJJTPSjNQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz", + "integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==", "dev": true, "license": "MIT", "dependencies": { @@ -5334,21 +5332,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.1.2", - "@rolldown/binding-darwin-arm64": "1.1.2", - "@rolldown/binding-darwin-x64": "1.1.2", - "@rolldown/binding-freebsd-x64": "1.1.2", - "@rolldown/binding-linux-arm-gnueabihf": "1.1.2", - "@rolldown/binding-linux-arm64-gnu": "1.1.2", - "@rolldown/binding-linux-arm64-musl": "1.1.2", - "@rolldown/binding-linux-ppc64-gnu": "1.1.2", - "@rolldown/binding-linux-s390x-gnu": "1.1.2", - "@rolldown/binding-linux-x64-gnu": "1.1.2", - "@rolldown/binding-linux-x64-musl": "1.1.2", - "@rolldown/binding-openharmony-arm64": "1.1.2", - "@rolldown/binding-wasm32-wasi": "1.1.2", - "@rolldown/binding-win32-arm64-msvc": "1.1.2", - "@rolldown/binding-win32-x64-msvc": "1.1.2" + "@rolldown/binding-android-arm64": "1.1.3", + "@rolldown/binding-darwin-arm64": "1.1.3", + "@rolldown/binding-darwin-x64": "1.1.3", + "@rolldown/binding-freebsd-x64": "1.1.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.3", + "@rolldown/binding-linux-arm64-gnu": "1.1.3", + "@rolldown/binding-linux-arm64-musl": "1.1.3", + "@rolldown/binding-linux-ppc64-gnu": "1.1.3", + "@rolldown/binding-linux-s390x-gnu": "1.1.3", + "@rolldown/binding-linux-x64-gnu": "1.1.3", + "@rolldown/binding-linux-x64-musl": "1.1.3", + "@rolldown/binding-openharmony-arm64": "1.1.3", + "@rolldown/binding-wasm32-wasi": "1.1.3", + "@rolldown/binding-win32-arm64-msvc": "1.1.3", + "@rolldown/binding-win32-x64-msvc": "1.1.3" } }, "node_modules/saxes": { diff --git a/src-tauri/src/commands/system.rs b/src-tauri/src/commands/system.rs index f676f2a..74510c1 100644 --- a/src-tauri/src/commands/system.rs +++ b/src-tauri/src/commands/system.rs @@ -1,5 +1,6 @@ use tauri::{AppHandle, Manager, WebviewWindow}; use tauri_plugin_opener::OpenerExt; +use tauri_plugin_window_state::{AppHandleExt, StateFlags}; #[tauri::command] pub fn close_window(window: WebviewWindow) -> Result<(), String> { @@ -28,8 +29,44 @@ pub fn toggle_window(app: &AppHandle) { } } +#[tauri::command] +pub fn restore_window_state(app: AppHandle) -> Result<(), String> { + if let Some(window) = app.get_webview_window("main") { + if let Ok(app_dir) = app.path().app_config_dir() { + let state_path = app_dir.join(".window-state.json"); + if let Ok(content) = std::fs::read_to_string(&state_path) { + if let Ok(val) = serde_json::from_str::(&content) { + if let Some(main) = val.get("main") { + if let (Some(x), Some(y)) = ( + main.get("x").and_then(|v| v.as_i64()), + main.get("y").and_then(|v| v.as_i64()), + ) { + let _ = window.set_position(tauri::PhysicalPosition::new(x as i32, y as i32)); + } + if let (Some(w), Some(h)) = ( + main.get("width").and_then(|v| v.as_i64()), + main.get("height").and_then(|v| v.as_i64()), + ) { + let _ = window.set_size(tauri::PhysicalSize::new(w as u32, h as u32)); + } + } + } + } + } + #[cfg(target_os = "macos")] + { + if let Ok(mut pos) = window.outer_position() { + pos.y = pos.y.saturating_sub(28); + let _ = window.set_position(tauri::Position::Physical(pos)); + } + } + } + Ok(()) +} + #[tauri::command] pub fn quit_app(app: AppHandle) { + let _ = app.save_window_state(StateFlags::POSITION | StateFlags::SIZE); app.exit(0); } @@ -64,6 +101,11 @@ pub fn open_file(app: AppHandle, path: String) -> Result<(), String> { use tauri_plugin_autostart::ManagerExt; +#[tauri::command] +pub fn get_launch_at_startup(app: AppHandle) -> Result { + app.autolaunch().is_enabled().map_err(|e| e.to_string()) +} + #[tauri::command] pub fn set_launch_at_startup(app: AppHandle, enabled: bool) -> Result<(), String> { if enabled { diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 03c3ae7..63bdcf2 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -12,6 +12,7 @@ use commands::shortcuts::GlobalShortcutState; use commands::notifications::NotificationState; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::Arc; +use tauri_plugin_window_state::{StateFlags, WindowExt}; pub struct DialogState { pub is_open: Arc, @@ -54,18 +55,6 @@ pub fn run() { use tauri::Manager; if let Some(window) = app.get_webview_window("main") { - #[cfg(target_os = "macos")] - { - crate::macos::set_move_to_active_space(&window); - - // Fix for macOS frameless window walking down on restart - // tauri-plugin-window-state restores position with titlebar offset - if let Ok(mut pos) = window.outer_position() { - pos.y = pos.y.saturating_sub(28); - let _ = window.set_position(tauri::Position::Physical(pos)); - } - } - let dialog_state = app.state::(); let is_dialog_open = dialog_state.is_open.clone(); #[cfg(not(target_os = "macos"))] @@ -110,6 +99,45 @@ pub fn run() { _ => {} } }); + + #[cfg(target_os = "macos")] + crate::macos::set_move_to_active_space(&window); + + // Restore window state after event loop is running and display server is ready. + // Plugin's on_window_ready fires too early for available_monitors() on macOS. + let win = window.clone(); + std::thread::spawn(move || { + std::thread::sleep(std::time::Duration::from_millis(300)); + let _ = win.clone().run_on_main_thread(move || { + let _ = win.restore_state(StateFlags::POSITION | StateFlags::SIZE); + if let Ok(app_dir) = win.app_handle().path().app_config_dir() { + let state_path = app_dir.join(".window-state.json"); + if let Ok(content) = std::fs::read_to_string(&state_path) { + if let Ok(val) = serde_json::from_str::(&content) { + if let Some(main) = val.get("main") { + if let (Some(x), Some(y)) = ( + main.get("x").and_then(|v| v.as_i64()), + main.get("y").and_then(|v| v.as_i64()), + ) { + let _ = win.set_position(tauri::PhysicalPosition::new(x as i32, y as i32)); + } + if let (Some(w), Some(h)) = ( + main.get("width").and_then(|v| v.as_i64()), + main.get("height").and_then(|v| v.as_i64()), + ) { + let _ = win.set_size(tauri::PhysicalSize::new(w as u32, h as u32)); + } + } + } + } + } + #[cfg(target_os = "macos")] + if let Ok(mut pos) = win.outer_position() { + pos.y = pos.y.saturating_sub(28); + let _ = win.set_position(tauri::Position::Physical(pos)); + } + }); + }); } else { eprintln!("WARNING: 'main' window not found during setup"); } @@ -132,9 +160,11 @@ pub fn run() { commands::fs::set_dialog_open, commands::fs::remove_onboarding_files, commands::system::close_window, + commands::system::restore_window_state, commands::system::quit_app, commands::system::open_external, commands::system::open_file, + commands::system::get_launch_at_startup, commands::system::set_launch_at_startup, commands::system::check_for_updates, commands::system::is_hyprland, diff --git a/src-tauri/src/tray.rs b/src-tauri/src/tray.rs index b03c7c9..75d1dde 100644 --- a/src-tauri/src/tray.rs +++ b/src-tauri/src/tray.rs @@ -28,7 +28,7 @@ pub fn create_tray(app: &App) -> Result<(), Box> { if event.id == "show_hide" { crate::commands::system::toggle_window(app); } else if event.id == "quit" { - app.exit(0); + crate::commands::system::quit_app(app.clone()); } }) .on_tray_icon_event(|tray, event| { diff --git a/src/App.tsx b/src/App.tsx index 82abcc6..b00298f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -53,6 +53,7 @@ function App() { useEffect(() => { window.electronAPI.checkForUpdates() + window.electronAPI.restoreWindowState() window.electronAPI.isHyprland().then((isHyp) => { useAppStore.getState().setIsHyprland(isHyp) }) diff --git a/src/Settings.test.tsx b/src/Settings.test.tsx index ba72ca1..f8ff0cc 100644 --- a/src/Settings.test.tsx +++ b/src/Settings.test.tsx @@ -8,6 +8,7 @@ describe('Settings Component', () => { // Mock the electronAPI window.electronAPI = { ...window.electronAPI, + getLaunchAtStartup: vi.fn().mockResolvedValue(false), setLaunchAtStartup: vi.fn(), updateGlobalShortcut: vi.fn(), closeWindow: vi.fn(), diff --git a/src/Settings.tsx b/src/Settings.tsx index 670d6fd..ef608de 100644 --- a/src/Settings.tsx +++ b/src/Settings.tsx @@ -50,6 +50,14 @@ export default function Settings({ onClose }: { onClose?: () => void }) { localStorage.getItem(SETTINGS_KEYS.LAUNCH_STARTUP) === 'true' ) + // Sync launch-at-startup toggle with actual OS state on mount + useEffect(() => { + window.electronAPI.getLaunchAtStartup().then((enabled) => { + setLaunchAtStartup(enabled) + localStorage.setItem(SETTINGS_KEYS.LAUNCH_STARTUP, enabled.toString()) + }) + }, []) + // Appearance State const initialSettings = useSettingsStore.getState() const [fontFamily, setFontFamily] = useState(initialSettings.fontFamily) diff --git a/src/api.ts b/src/api.ts index 8a6877d..a50e7a0 100644 --- a/src/api.ts +++ b/src/api.ts @@ -33,10 +33,12 @@ export const tauriApi: ElectronAPI = { setApiKey: (key) => invoke('set_api_key', { key }), getApiKeyStatus: () => invoke('get_api_key_status'), checkForUpdates: () => invoke('check_for_updates'), + restoreWindowState: () => invoke('restore_window_state'), isHyprland: () => invoke('is_hyprland'), onSwipeGesture: () => { return () => {} }, + getLaunchAtStartup: () => invoke('get_launch_at_startup'), setLaunchAtStartup: (value) => { invoke('set_launch_at_startup', { enabled: value }) }, diff --git a/src/types.d.ts b/src/types.d.ts index f0ea233..6f26233 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -18,6 +18,7 @@ export interface ElectronAPI { setApiKey: (key: string) => Promise getApiKeyStatus: () => Promise checkForUpdates: () => Promise + restoreWindowState: () => Promise isHyprland: () => Promise readNote: (id: string) => Promise exportNote: (filename: string, content: string) => Promise @@ -32,6 +33,7 @@ export interface ElectronAPI { openExternal: (url: string) => void openFile: (path: string) => void onSwipeGesture: (callback: (direction: string) => void) => () => void + getLaunchAtStartup: () => Promise setLaunchAtStartup: (value: boolean) => void updateGlobalShortcut: (action: string, oldShortcut: string, newShortcut: string) => void onTriggerNewNote: (callback: () => void) => () => void