You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Fix config save race condition by serializing writes through a background channel
- Fix click-through startup trap by auto-disabling locked mode on launch
- Fix tray quit to persist window geometry before exiting
- Fix navigation double-load by returning Ok(true) from navigate/navigate_home
- Fix media control NaN bug and track most-recently-interacted media element
- Fix bookmark limit enforcement (max 50) and fuzzy matching in Rust
- Fix opacity startup failure by deferring set_window_opacity 300ms
- Fix fragile error-page detection heuristic
- Fix tray fallback icon panic
- Add real clear_site_data command using WebView2 native API
- Add CropConfig persistence and set_crop/clear_crop commands
- Expand hotkey parser to support full alphabet, digits, F-keys, and punctuation
- Update AGENTS.md to reflect actual codebase structure
Copy file name to clipboardExpand all lines: AGENTS.md
+77-25Lines changed: 77 additions & 25 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@ Guide for AI agents working on the FloatView codebase.
4
4
5
5
## Project Overview
6
6
7
-
FloatView is a Tauri v2 application that provides a floating browser window for streaming media on secondary monitors. Key features include always-on-top, borderless resizable window, opacity control, click-through mode, persistent bookmarks, navigation controls, smart URL bar with DuckDuckGo search, window title tracking, crash recovery (config backup + periodic geometry auto-save), and clear site data.
7
+
FloatView is a Tauri v2 application that provides a floating browser window for streaming media on secondary monitors. Key features include always-on-top, borderless resizable window, opacity control, click-through mode, persistent bookmarks, navigation controls, smart URL bar with DuckDuckGo search, window title tracking, crash recovery (config backup + periodic geometry auto-save), crop/zoom region, and clear site data.
|`get_version`| Get app version string |`main.rs`|
117
+
|`check_for_updates`| Check for available updates |`main.rs`|
118
+
|`set_window_title`| Set window title (truncated to 256 chars) |`main.rs`|
119
+
|`add_bookmark`| Add URL to bookmarks (dedup, max 50) |`main.rs`|
120
+
|`remove_bookmark`| Remove URL from bookmarks (fuzzy match) |`main.rs`|
121
+
|`set_crop`| Persist crop region |`main.rs`|
122
+
|`clear_crop`| Clear persisted crop region |`main.rs`|
123
+
|`clear_site_data`| Clear all webview browsing data |`clear_site_data.rs`|
114
124
115
125
All commands require an auth token (`token` param) via `authorize_command()`.
116
126
127
+
### Config Serialization (Background Channel)
128
+
129
+
Config saves are performed by a dedicated background thread to avoid blocking the async runtime and to prevent races:
130
+
131
+
```rust
132
+
// In run():
133
+
let (save_tx, save_rx) =std::sync::mpsc::channel::<AppConfig>();
134
+
std::thread::spawn(move|| {
135
+
whileletOk(cfg) =save_rx.recv() {
136
+
do_save_config(&path, &cfg);
137
+
}
138
+
});
139
+
140
+
// Commands mutate the Mutex, then call:
141
+
save_config(&state, &config); // sends clone to channel
142
+
```
143
+
144
+
This ensures that:
145
+
1. The `Mutex` is never held during disk I/O.
146
+
2. All writes are serialized (no race between geometry thread and user actions).
147
+
3. The existing config file is never deleted on a failed rename.
148
+
117
149
### Config Struct (`config.rs`)
118
150
119
151
```rust
@@ -125,16 +157,26 @@ pub struct AppConfig {
125
157
pubhome_url:String,
126
158
pubfirst_run:bool,
127
159
pubauto_refresh_minutes:u32,
128
-
pubbookmarks:Vec<String>, // new in v1.1
160
+
pubbookmarks:Vec<String>,
161
+
pubcrop:Option<CropConfig>, // x, y, width, height (0-1 normalized)
129
162
}
130
163
```
131
164
165
+
### Tray Menu Dynamic Updates
166
+
167
+
The tray's **Exit Click-Through Mode** item is dynamically enabled/disabled to match the actual locked state. A closure setter is stored in `AppState` because `MenuItem` is generic over `Runtime`:
-**URL bar**: DuckDuckGo search fallback for non-URL input
@@ -169,6 +211,8 @@ Key injection.js features:
169
211
-**URL tracking**: `popstate` + 3s polling for address bar sync
170
212
-**Config sync**: `config-changed` event listener updates bookmarks in real-time
171
213
-**Dropdown mutual exclusion**: recent/bookmarks/snap dropdowns dismiss each other
214
+
-**Crop/Zoom**: Select region, persist via `set_crop`, restore on init and resize
215
+
-**Media hotkeys**: `window.__floatViewLastMedia` tracks the most recently interacted `<video>` or `<audio>` element
172
216
173
217
## Gotchas
174
218
@@ -178,27 +222,31 @@ Key injection.js features:
178
222
179
223
3.**Mutex lifetime in helpers** -- When using `app.state::<AppState>()` in block expressions, use `.lock().unwrap()` not `if let Ok(...)` to avoid lifetime issues with the State temporary.
180
224
181
-
4.**Click-through mode is a trap** -- When locked, the control strip is hidden AND mouse events pass through. Users can only exit via global hotkey or tray menu. Always ensure these escape hatches work.
225
+
4.**Click-through mode is a trap** -- When locked, the control strip is hidden AND mouse events pass through. Users can only exit via global hotkey or tray menu. Always ensure these escape hatches work. The app auto-disables locked mode on startup for safety.
182
226
183
227
5.**User Agent** -- Set to Edge UA string for Direct Play support with Emby/Plex. See `inject_script_on_document_created()`.
184
228
185
-
6.**Opacity on Windows** -- Uses `SetLayeredWindowAttributes` with `WS_EX_LAYERED`. The `transparent: true` Tauri config is required for this to work.
229
+
6.**Opacity on Windows** -- Uses `SetLayeredWindowAttributes` with `WS_EX_LAYERED`. The `transparent: true` Tauri config is required for this to work. Opacity is applied with a 300ms startup delay to ensure the native HWND is ready.
186
230
187
231
7.**Single Instance** -- `tauri-plugin-single-instance` brings existing window to front if user launches again.
188
232
189
-
8.**Config backup** -- `save_config()` creates a `.bak` copy before writing. Used for crash recovery.
233
+
8.**Config backup** -- `do_save_config()` creates a `.bak` copy before writing. Used for crash recovery. It does NOT delete the existing config if the atomic rename fails.
190
234
191
235
9.**Periodic geometry auto-save** -- Background thread saves window position/size every 30s (skips minimized/maximized). Prevents geometry loss on crash.
192
236
193
-
10.**Bookmark limits** -- Max 50 bookmarks, deduplication by normalized URL, sanitized via `sanitize_config()`.
237
+
10.**Bookmark limits** -- Max 50 bookmarks, deduplication by normalized URL and fuzzy `urls_match`, sanitized via `sanitize_config()`.
12.**Logging** -- Uses `tracing` crate (`warn!`, `error!`) instead of `eprintln!` for structured logging.
198
242
243
+
13.**Config save channel** -- All config mutations are serialized through a background `std::sync::mpsc` channel to eliminate races and keep the async runtime responsive.
244
+
245
+
14.**Error-page detection** -- The injected script detects browser error pages using multiple heuristics (requires at least 2 indicators or a definitive error title) to avoid false positives on tech blogs.
246
+
199
247
## Testing
200
248
201
-
5 unit tests in `src-tauri/` (run via `cargo test`). Test manually:
249
+
Run unit tests in `src-tauri/` (via `cargo test`). Test manually:
202
250
203
251
1.`npm run dev`
204
252
2. Test URL navigation (landing page + control strip URL bar + DDG search fallback)
@@ -213,3 +261,7 @@ Key injection.js features:
213
261
11. Test bookmark star (toggle on/off, right-click dropdown shows list)
214
262
12. Test clear site data (Settings button, verify cookies/storage cleared)
215
263
13. Test window title updates when navigating between pages
264
+
14. Test crop/zoom (select region, verify persist/restore across restarts)
265
+
15. Test tray quit preserves geometry
266
+
16. Test media hotkeys target the most recently interacted player
267
+
17. Test error-page redirect only fires on actual browser errors
0 commit comments