diff --git a/AGENT.md b/AGENT.md new file mode 100644 index 0000000..c7be090 --- /dev/null +++ b/AGENT.md @@ -0,0 +1,17 @@ +# Agent Rules + +## Git & PR — WAJIB + +### Tidak ada atribusi AI +- **DILARANG** mencantumkan "Generated with Claude Code", "Co-authored-by", "@claude", atau atribusi AI apapun di commit message, PR body, atau kode. +- Author commit = Aji Anaz ``. +- Co-author trailer = **selalu kosong**. +- Tidak boleh ada footer signature agent di output. + +### Commit message +- Konvensional: `fix:`, `feat:`, `chore:`, `refactor:`, `docs:`, `checkpoint:` +- Bahasa Inggris untuk commit, PR boleh Indonesia. + +### Branch +- Naming: `fix/`, `feat/`, `checkpoint/` +- Base PR default: `develop` diff --git a/bun.lock b/bun.lock index 08e0ae0..72e3c77 100644 --- a/bun.lock +++ b/bun.lock @@ -1,6 +1,5 @@ { "lockfileVersion": 1, - "configVersion": 1, "workspaces": { "": { "name": "drawover", @@ -9,6 +8,7 @@ "@tauri-apps/plugin-global-shortcut": "^2.0.0", }, "devDependencies": { + "@lucide/svelte": "^1.24.0", "@sveltejs/adapter-static": "^3.0.0", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0", @@ -84,6 +84,8 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + "@lucide/svelte": ["@lucide/svelte@1.24.0", "", { "peerDependencies": { "svelte": "^5" } }, "sha512-yXwewA7ANQ5hfaSDrvsecosWjZn5RglzeXUZRSnxeANBskpNwblOkEJTqD0ujDdNKIKL8E9eVc2U/P3ziJr7OA=="], + "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.62.2", "", { "os": "android", "cpu": "arm" }, "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg=="], diff --git a/package.json b/package.json index ae23f56..514c28d 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "tauri": "tauri" }, "devDependencies": { + "@lucide/svelte": "^1.24.0", "@sveltejs/adapter-static": "^3.0.0", "@sveltejs/kit": "^2.0.0", "@sveltejs/vite-plugin-svelte": "^4.0.0", diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 5232350..17884b6 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -9,6 +9,10 @@ "core:window:allow-hide", "core:window:allow-set-focus", "core:window:allow-start-dragging", + "core:window:allow-set-ignore-cursor-events", + "core:window:allow-set-position", + "core:window:allow-set-size", + "core:window:allow-current-monitor", "global-shortcut:allow-register", "global-shortcut:allow-unregister", "global-shortcut:allow-is-registered" diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 04477ca..8a2fcb0 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -7,7 +7,7 @@ use tauri::{ tray::TrayIconBuilder, Emitter, Manager, }; -use tauri_plugin_global_shortcut::{GlobalShortcutExt, Shortcut, ShortcutState}; +// global shortcut disabled during dev // --------------------------------------------------------------------------- // Data structures @@ -67,9 +67,24 @@ fn apply_click_through(app: &tauri::AppHandle, ignore: bool) { /// Show the overlay window and capture mouse events. fn show_overlay(app: &tauri::AppHandle) { if let Some(window) = app.get_webview_window("overlay") { + // Force window to cover the primary screen (fixes maximized/center conflict on macOS) + if let Ok(Some(monitor)) = window.current_monitor() { + let size = monitor.size(); + let pos = monitor.position(); + let _ = window.set_position(tauri::PhysicalPosition::new(pos.x, pos.y)); + let _ = window.set_size(tauri::PhysicalSize::new(size.width, size.height)); + } let _ = window.show(); let _ = window.set_focus(); - let _ = window.set_ignore_cursor_events(false); + // Re-assert always-on-top so the overlay sits above every app + let _ = window.set_always_on_top(true); + let r = window.set_ignore_cursor_events(false); + println!( + "[DrawOver] show_overlay — focus+ignore_cursor result: {:?}", + r + ); + } else { + eprintln!("[DrawOver] show_overlay — overlay window not found!"); } } @@ -86,11 +101,18 @@ fn do_toggle_draw_mode(state: &Mutex, app: &tauri::AppHandle) -> bool s.is_draw_mode = !s.is_draw_mode; s.is_draw_mode }; + println!("[DrawOver] do_toggle_draw_mode -> {}", mode); if mode { + // Become a regular app so the overlay can take focus & capture mouse + #[cfg(target_os = "macos")] + let _ = app.set_activation_policy(tauri::ActivationPolicy::Regular); show_overlay(app); } else { - apply_click_through(app, true); + // Keep clickable so FAB/tray can re-enter draw mode + apply_click_through(app, false); + #[cfg(target_os = "macos")] + let _ = app.set_activation_policy(tauri::ActivationPolicy::Regular); } let _ = app.emit("draw-mode-toggled", mode); @@ -278,21 +300,15 @@ pub fn run() { // ----- Tray icon ----- setup_tray(handle)?; - // ----- Global shortcut: Option+Shift+D (Alt+Shift+D) ----- - let shortcut: Shortcut = "Alt+Shift+D".parse()?; - - // Register + attach handler in one call - handle - .global_shortcut() - .on_shortcut(shortcut, move |app, _shortcut, event| { - if event.state == ShortcutState::Pressed { - let state = app.state::>(); - do_toggle_draw_mode(state.inner(), app); - } - })?; - - // ----- Initial state: overlay click-through ----- - apply_click_through(app.handle(), true); + // ----- Global shortcut: DISABLED (phantom toggle) ----- + // let shortcut: Shortcut = "Alt+Shift+D".parse()?; + // handle.global_shortcut().on_shortcut(shortcut, ...)?; + + // ----- Initial state: overlay clickable ----- + #[cfg(target_os = "macos")] + app.set_activation_policy(tauri::ActivationPolicy::Regular); + apply_click_through(app.handle(), false); + println!("[DrawOver] startup complete — overlay clickable"); Ok(()) }) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 3378c0f..ae04b93 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -15,11 +15,12 @@ { "label": "overlay", "title": "DrawOver", - "fullscreen": true, + "fullscreen": false, + "maximized": false, "alwaysOnTop": true, "decorations": false, "transparent": true, - "skipTaskbar": true, + "skipTaskbar": false, "resizable": false, "width": 1920, "height": 1080, diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 44abcbc..1eaea3b 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -15,6 +15,7 @@ let isDrawing = $state(false); let currentPoints: Point[] = []; let dpr = 1; + let pdown = $state(0); // Store subscription values for template reactivity let drawModeOn = $state(false); @@ -52,8 +53,9 @@ } function onPointerDown(e: PointerEvent): void { + pdown++; + console.log('[DrawOver] pointerdown', { drawModeOn, button: e.button, type: e.pointerType }); if (!drawModeOn) return; - // Only respond to primary button (left click or touch) if (e.button !== 0 && e.pointerType === 'mouse') return; e.preventDefault(); @@ -211,6 +213,16 @@ } // ─── Actions ─── + async function toggleDrawMode(): Promise { + try { + const result = await invoke('toggle_draw_mode'); + console.log('[DrawOver] toggle_draw_mode result:', result); + drawMode.set(result); + } catch (e) { + console.error('[DrawOver] toggle_draw_mode failed:', e); + } + } + function undo(): void { if (strokes.length === 0) return; strokes = strokes.slice(0, -1); @@ -241,6 +253,7 @@ let unlistenDrawToggle: UnlistenFn | null = null; listen('draw-mode-toggled', (event) => { + console.log('[DrawOver] draw-mode-toggled event:', event.payload); drawMode.set(event.payload); }) .then((un) => { @@ -250,6 +263,14 @@ // Not in Tauri context (e.g. browser dev) — ignore }); + // Sync initial state from Rust backend + invoke('is_draw_mode') + .then((v) => { + console.log('[DrawOver] initial is_draw_mode:', v); + drawMode.set(v); + }) + .catch(() => {}); + // Clean up on destroy return () => { window.removeEventListener('resize', onResize); @@ -273,6 +294,17 @@
+
+ mode:{drawModeOn} tool:{tool} cls:{drawModeOn ? 'CAPTURE' : 'pass'} pdown:{pdown} strokes:{strokes.length} +
+ + +