Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6db3217
fix(zoom): improve error handling and fallback for zoom backend initi…
daiv05 Jul 18, 2026
f725318
fix(dxgi): implement UnmapGuard for safe D3D11 resource unmapping
daiv05 Jul 18, 2026
279d110
fix(window): implement mode locking for safe window rebuild and visib…
daiv05 Jul 18, 2026
eaad776
fix(zoom): convert stop_zoom_stream and freeze_zoom to async for impr…
daiv05 Jul 18, 2026
a244e16
fix(magnifier): add error handling for SetWindowRgn in set_circle fun…
daiv05 Jul 18, 2026
1f7e709
fix(canvas): clear autoErase timers and maps on cleanup
daiv05 Jul 18, 2026
1d235e3
fix(monitor): use scaleFactor from monitorContext for coordinate tran…
daiv05 Jul 18, 2026
2ee163b
fix(zoom): clamp zoom region size to MAX_ZOOM_REGION in capture_zoom_…
daiv05 Jul 18, 2026
0f85f67
fix(zoom): clear zoom window handle on mode destruction and validate …
daiv05 Jul 18, 2026
1ed0d62
fix(gdi): implement GdiDcGuard for resource management in GDI screens…
daiv05 Jul 18, 2026
a28121c
fix(monitor): manage fallback timer for monitor context retrieval
daiv05 Jul 18, 2026
c0e1e1f
fix(dependencies): add devtools feature to tauri and update CSP settings
daiv05 Jul 18, 2026
693cf06
feat(capabilities): update default capabilities and add mode-windows …
daiv05 Jul 18, 2026
463eb79
fix(persistence): implement queuePersist function for overlay and set…
daiv05 Jul 18, 2026
ba6f535
fix(cursor): ensure message loop runs correctly in start_mouse_hook f…
daiv05 Jul 18, 2026
c3ba11a
refactor(zoom): remove capture_viewport function and streamline dupli…
daiv05 Jul 18, 2026
39486f0
fix(zoom): ensure sourceBuffer is set before updating canvas with dec…
daiv05 Jul 18, 2026
de9ecb7
docs(zoom): add documentation for ZOOM_BACKEND_STATE initialization
daiv05 Jul 18, 2026
8d9d159
fix(overlay): rename handleHideDock to handleCloseOverlay and update …
daiv05 Jul 19, 2026
f31a580
fix(window): reassert transparent background for WebView2 and improve…
daiv05 Jul 19, 2026
c89966a
feat(onboarding): implement onboarding modal and related functionality
daiv05 Jul 19, 2026
cf356c4
feat(cursor): add border width and fill opacity settings for cursor h…
daiv05 Jul 19, 2026
183b584
feat: refactor and change default app color
daiv05 Jul 19, 2026
44e5741
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ crate-type = ["staticlib", "cdylib", "rlib"]
tauri-build = { version = "2", features = [] }

[dependencies]
tauri = { version = "2", features = ["tray-icon"] }
tauri = { version = "2", features = ["tray-icon", "devtools"] }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

set -euo pipefail

printf '\n## Cargo.toml\n'
sed -n '1,80p' src-tauri/Cargo.toml

printf '\n## lib.rs\n'
sed -n '1,220p' src-tauri/src/lib.rs

printf '\n## Search for devtools usage\n'
rg -n "devtools|open_devtools|Inspect Element|debug_assertions" src-tauri -S

Repository: daiv05/vynta

Length of output: 10441


🏁 Script executed:

set -euo pipefail

echo '## Cargo.toml'
sed -n '1,80p' src-tauri/Cargo.toml

echo
echo '## lib.rs'
sed -n '1,220p' src-tauri/src/lib.rs

echo
echo '## Search'
rg -n "devtools|open_devtools|Inspect Element|debug_assertions" src-tauri -S

Repository: daiv05/vynta

Length of output: 10421


🌐 Web query:

Tauri 2 devtools feature release mode docs

💡 Result:

In Tauri 2.0, developer tools (DevTools) are enabled by default in development and debug builds, but they are disabled in production (release) builds by default [1][2][3]. To use DevTools in a release build, you must explicitly enable them. To enable DevTools for a release build, add the devtools Cargo feature to your project's src-tauri/Cargo.toml file [1][2]: [dependencies] tauri = { version = "2.0.0", features = ["devtools"] } Important considerations regarding this feature: 1. macOS Warning: The devtools API on macOS relies on private APIs [1]. Enabling this feature on macOS may cause your application to be rejected by the Apple App Store [1]. 2. Security and Best Practices: It is generally recommended not to ship applications with DevTools enabled in production [4]. The canonical approach for debugging production-like behavior without enabling DevTools in the final shipping binary is to run a debug build using the command: tauri build --debug This creates a build in src-tauri/target/debug/bundle that functions similarly to a release build but includes development features like the inspector [1][2]. If you find the --debug flag is not working as expected (e.g., due to specific environment configurations), ensure your package manager command is correctly passing the flag (e.g., npm run tauri build -- --debug) [5]. 3. Alternative Debugging: For production debugging, consider implementing dedicated logging solutions like the tauri-plugin-log, which allows you to capture logs to a file that users can share with you when issues occur [6]. Additionally, you can configure your bundler to generate source maps (e.g., in vite.config.ts) to make production errors easier to trace if you have access to the environment [6]. 4. Programmatic Control: You can also open or close DevTools programmatically within your Rust code using WebviewWindow::open_devtools and WebviewWindow::close_devtools, typically guarded by #[cfg(debug_assertions)] to ensure this code only runs in debug builds [1][2].

Citations:


Keep devtools out of the release dependency set
tauri’s devtools feature is enabled unconditionally in src-tauri/Cargo.toml:18, so release builds will ship DevTools support too. The #[cfg(debug_assertions)] guard only skips open_devtools(); it doesn’t remove the capability from the binary. Move this behind a dev-only feature and enable it only for local debugging.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/Cargo.toml` at line 18, Update the tauri dependency declaration in
Cargo.toml to remove the unconditional devtools feature from the release
dependency set. Add a dev-only feature configuration that enables tauri’s
devtools capability only for local/debug builds, while preserving tray-icon for
all builds and the existing debug-only open_devtools behavior.

tauri-plugin-opener = "2"
tauri-plugin-global-shortcut = "2"
tauri-plugin-store = "2"
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/capabilities/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main", "overlay", "whiteboard", "cursor-highlight", "spotlight", "zoom"],
"windows": ["main"],
"permissions": [
"core:default",
"opener:default",
Expand Down
11 changes: 11 additions & 0 deletions src-tauri/capabilities/mode-windows.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "mode-windows",
"description": "Minimal capability for overlay mode render windows (no global shortcuts, no autostart)",
"windows": ["overlay", "whiteboard", "cursor-highlight", "spotlight", "zoom"],
"permissions": [
"core:default",
"opener:default",
"store:default"
]
}
4 changes: 2 additions & 2 deletions src-tauri/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,8 @@ pub fn set_zoom_capture_excluded(excluded: bool) -> Result<(), String> {
}

#[tauri::command]
pub fn freeze_zoom() -> Result<(), String> {
crate::zoom::stop_zoom_stream()?;
pub async fn freeze_zoom() -> Result<(), String> {
crate::zoom::stop_zoom_stream().await?;
#[cfg(target_os = "windows")]
{
crate::window::set_zoom_capture_exclusion(false);
Expand Down
4 changes: 2 additions & 2 deletions src-tauri/src/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ pub fn start_mouse_hook() {
);

let hook = SetWindowsHookExW(WH_MOUSE_LL, Some(mouse_hook_proc), None, 0);
let mut msg = std::mem::zeroed();
while GetMessageW(&mut msg, None, 0, 0).into() {}
if let Ok(hook) = hook {
let mut msg = std::mem::zeroed();
while GetMessageW(&mut msg, None, 0, 0).into() {}
if !hook.0.is_null() {
let _ = UnhookWindowsHookEx(hook);
}
Expand Down
26 changes: 21 additions & 5 deletions src-tauri/src/dxgi_capture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ use windows::Win32::Graphics::Dxgi::*;
const DXGI_ERROR_WAIT_TIMEOUT: i32 = 0x887A0027u32 as i32;
const DXGI_ERROR_ACCESS_LOST: i32 = 0x887A0026u32 as i32;

/**
* RAII guard that unmaps a mapped D3D11 subresource on drop, including on panic/unwind.
*/
struct UnmapGuard<'a> {
context: &'a ID3D11DeviceContext,
resource: &'a ID3D11Texture2D,
}

impl Drop for UnmapGuard<'_> {
fn drop(&mut self) {
unsafe {
self.context.Unmap(self.resource, 0);
}
}
}

pub struct DxgiDuplicator {
device: ID3D11Device,
context: ID3D11DeviceContext,
Expand Down Expand Up @@ -270,6 +286,10 @@ impl DxgiDuplicator {
self.context
.Map(staging, 0, D3D11_MAP_READ, 0, Some(&mut mapped))
.map_err(|e| format!("Map staging: {e}"))?;
let _unmap_guard = UnmapGuard {
context: &self.context,
resource: staging,
};
let row_pitch = mapped.RowPitch as usize;
let pixel_count = (region_w * region_h * 4) as usize;
let mut rgba = Vec::with_capacity(pixel_count);
Expand Down Expand Up @@ -297,13 +317,9 @@ impl DxgiDuplicator {
}
}

self.context.Unmap(staging, 0);
drop(_unmap_guard);

Ok((rgba, region_w, region_h))
}
}

pub fn capture_full_frame(&mut self) -> Result<(Vec<u8>, u32, u32), String> {
self.capture_region(0, 0, self.width, self.height)
}
}
39 changes: 27 additions & 12 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,41 @@ struct ZoomConfig {
backend: String,
}

/// In-memory zoom backend state. Seeded once from the compiled-in
/// `config/zoom.json` factory default, then overwritten on every app start
/// by `hydrate()` (frontend) via `set_zoom_backend_cmd`
static ZOOM_BACKEND_STATE: OnceLock<RwLock<String>> = OnceLock::new();

const DEFAULT_ZOOM_BACKEND: &str = "dxgi";

fn init_zoom_backend_state() -> RwLock<String> {
let json_str = include_str!("../../config/zoom.json");
let backend = serde_json::from_str::<ZoomConfig>(json_str)
.map(|config| config.backend)
.unwrap_or_else(|e| {
eprintln!(
"Failed to parse zoom.json, falling back to default backend: {}",
e
);
DEFAULT_ZOOM_BACKEND.to_string()
});
RwLock::new(backend)
}

pub fn get_zoom_backend() -> String {
let state = ZOOM_BACKEND_STATE.get_or_init(|| {
let json_str = include_str!("../../config/zoom.json");
let config: ZoomConfig = serde_json::from_str(json_str).expect("Failed to parse zoom.json");
RwLock::new(config.backend)
});
let state = ZOOM_BACKEND_STATE.get_or_init(init_zoom_backend_state);

match state.read() {
Ok(guard) => guard.clone(),
Err(e) => {
eprintln!("Failed to read zoom backend state: {}", e);
"dxgi".to_string()
DEFAULT_ZOOM_BACKEND.to_string()
}
}
}

pub fn set_zoom_backend_internal(new_backend: String) {
let state = ZOOM_BACKEND_STATE.get_or_init(|| {
let json_str = include_str!("../../config/zoom.json");
let config: ZoomConfig = serde_json::from_str(json_str).expect("Failed to parse zoom.json");
RwLock::new(config.backend)
});
let state = ZOOM_BACKEND_STATE.get_or_init(init_zoom_backend_state);

if let Ok(mut guard) = state.write() {
*guard = new_backend;
Expand Down Expand Up @@ -73,6 +84,11 @@ pub fn run() {
))
.plugin(tauri_plugin_opener::init())
.setup(|app| {
#[cfg(debug_assertions)]
if let Some(window) = app.get_webview_window("main") {
window.open_devtools();
}

#[cfg(target_os = "windows")]
cursor::start_mouse_hook();

Expand Down Expand Up @@ -281,7 +297,6 @@ pub fn run() {
zoom::stop_zoom_stream,
commands::freeze_zoom,
commands::unfreeze_zoom,
zoom::capture_viewport_without_zoom,
commands::mag_zoom_show,
commands::mag_zoom_hide,
commands::mag_zoom_set_config,
Expand Down
4 changes: 3 additions & 1 deletion src-tauri/src/magnifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,9 @@ unsafe fn apply_transform(mag: HWND, zoom: f32) {

unsafe fn set_circle(hwnd: HWND, size: u32) {
let rgn = CreateEllipticRgn(0, 0, size as i32, size as i32);
SetWindowRgn(hwnd, Some(rgn), true);
if SetWindowRgn(hwnd, Some(rgn), true) == 0 {
let _ = DeleteObject(rgn.into());
}
}

unsafe fn set_square(hwnd: HWND) {
Expand Down
22 changes: 21 additions & 1 deletion src-tauri/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashMap;
use std::sync::atomic::AtomicBool;
use std::sync::{Arc, RwLock};
use std::sync::{Arc, Mutex, RwLock};
use tauri::async_runtime::JoinHandle;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -46,15 +46,35 @@ pub struct MonitorContext {
pub struct WindowRegistry {
pub mode_windows: RwLock<HashMap<Mode, Vec<String>>>,
pub current_snapshot: RwLock<String>,
mode_locks: HashMap<Mode, Mutex<()>>,
}

impl WindowRegistry {
pub fn new() -> Self {
let mut mode_locks = HashMap::new();
for mode in [Mode::Overlay, Mode::Spotlight, Mode::Highlight, Mode::Zoom] {
mode_locks.insert(mode, Mutex::new(()));
}

Self {
mode_windows: RwLock::new(HashMap::new()),
current_snapshot: RwLock::new(String::new()),
mode_locks,
}
}

/// Serializes the destroy→build→show sequence for a given [`Mode`] so
/// concurrent callers (monitor-change loop vs. visibility requests)
/// cannot interleave window mutations for the same mode.
pub fn with_mode_lock<T>(&self, mode: Mode, f: impl FnOnce() -> T) -> T {
let _guard = self
.mode_locks
.get(&mode)
.expect("mode_locks initialized for every Mode variant")
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
f()
}
}

#[derive(serde::Serialize, Clone)]
Expand Down
Loading
Loading