···9191 "dragHoldDelay": {
9292 "description": "How long (in seconds) to hold the mouse still before a window becomes draggable. Click and hold without moving — the cursor changes to a hand when ready, then drag to move. This allows normal text selection and clicking without accidental window drags. Set to 0 for instant drag. Takes effect on new windows.",
9393 "type": "number",
9494- "default": 2
9494+ "default": 1
9595 },
9696 },
9797 "required": [ "shortcutKey", "startupFeature", "enableTrayIcon", "showInDockAndSwitcher", "quitShortcut" ]
···168168 sessionAutosaveInterval: 5,
169169 pageWidth: 800,
170170 pageHeight: 600,
171171- dragHoldDelay: 2
171171+ dragHoldDelay: 1
172172 },
173173 items: [
174174 { id: '82de735f-a4b7-4fe6-a458-ec29939ae00d',
+15-5
app/page/page.js
···1010 * The URL params (x, y, width, height) store screen coordinates of the webview
1111 * for session save/restore. Internally, elements use window-relative positions.
1212 *
1313- * - Custom drag: instant on navbar background, hold-150ms-to-drag anywhere else
1313+ * - Custom drag: instant on navbar background, hold-to-drag anywhere else (configurable via dragHoldDelay pref, default 1s)
1414 * - Custom resize: pointer-captured resize handle (robust even if cursor leaves window)
1515 * - Hover trigger zone above webview reveals/hides navbar
1616 * - Cmd+L shows navbar with URL focus
···392392// --- Custom drag ---
393393// Two modes:
394394// 1. Navbar background: instant drag (no hold delay)
395395-// 2. Anywhere else: hold ~150ms then drag
395395+// 2. Anywhere else: hold for dragHoldDelay (default 1s) then drag
396396// Drag now moves the BrowserWindow itself via setBounds() IPC.
397397398398let isDragging = false;
···404404405405// Hold-to-drag state (configurable, read from prefs at init)
406406const JITTER_TOLERANCE = 3; // px of movement allowed during hold without cancelling
407407-let DRAG_HOLD_THRESHOLD = 2000; // ms, updated from prefs
407407+let DRAG_HOLD_THRESHOLD = 1000; // ms, updated from prefs
408408let holdDragTimer = null;
409409let holdDragPending = false;
410410let holdDragReady = false; // true when hold fires, cursor is 'grab', waiting for movement
···672672let mouseBridgeTimer = null;
673673674674function injectMouseBridge() {
675675- webview.executeJavaScript(WEBVIEW_MOUSE_BRIDGE_JS).catch(() => {});
675675+ webview.executeJavaScript(WEBVIEW_MOUSE_BRIDGE_JS)
676676+ .then(() => { DEBUG && console.log('[page] Mouse bridge injected'); })
677677+ .catch((err) => { console.warn('[page] Mouse bridge injection failed:', err.message); });
676678}
677679678680// Inject on dom-ready (fires on each full navigation)
679681webview.addEventListener('dom-ready', injectMouseBridge);
680682681681-// Also inject on did-navigate for robustness (some navigations may not fire dom-ready).
683683+// Also inject on did-navigate and did-finish-load for robustness.
684684+// Some navigations skip dom-ready; some sites run scripts that reset global state.
682685// Cancel any pending injection from a previous navigation to avoid listener buildup.
683686webview.addEventListener('did-navigate', () => {
684687 if (mouseBridgeTimer) clearTimeout(mouseBridgeTimer);
···686689 mouseBridgeTimer = null;
687690 injectMouseBridge();
688691 }, 50);
692692+});
693693+694694+webview.addEventListener('did-finish-load', () => {
695695+ // Re-inject after full load — catches cases where dom-ready injection was too early
696696+ // or the page's own scripts cleared global state. The bridge's __peekMouseBridgeInstalled
697697+ // guard prevents duplicate listeners if the bridge is already active.
698698+ injectMouseBridge();
689699});
690700691701dragOverlay.addEventListener('mouseup', () => {
+2-2
docs/feature-tour.md
···139139140140**How to try it:**
1411411. Open a page (`open example.com` in the command palette).
142142-2. The page appears as a floating window. Hold the mouse still on the page body for 2 seconds (configurable "drag hold delay" in Settings) -- the cursor changes to a hand, then drag to move the window.
142142+2. The page appears as a floating window. Hold the mouse still on the page body for 1 second (configurable "drag hold delay" in Settings) -- the cursor changes to a hand, then drag to move the window.
1431433. Use the corner resize handle to change dimensions.
144144145145### Navbar
···173173**Where in UI:** Settings > Core > "Window drag hold delay".
174174175175**How to try it:**
176176-1. Open Settings and set "Window drag hold delay" (default: 2 seconds).
176176+1. Open Settings and set "Window drag hold delay" (default: 1 second).
1771772. On a page, click and hold without moving. After the delay, the cursor changes to a hand.
1781783. Now move the mouse to drag the window.
1791794. On the navbar background, drag is instant with no delay.
+3-3
preload.js
···22072207// Design (v3 rewrite):
22082208// - Window ID is fetched once at init (it never changes).
22092209// - Window position is fetched eagerly on mousedown (overlaps with hold timer).
22102210-// - Hold delay is 120ms. Short enough to feel responsive.
22102210+// - Hold delay is configurable via dragHoldDelay pref (default 1s).
22112211// - NO movement threshold during hold period. Mouse can move freely while
22122212// waiting for the hold timer to fire. When it fires, the CURRENT mouse
22132213// position becomes the drag origin (no jump from early movement).
···22292229 const MOVE_THRESHOLD = 3; // px of movement required to engage drag after hold fires
22302230 const JITTER_TOLERANCE = 3; // px of movement allowed during hold without cancelling
2231223122322232- // Configurable hold delay (read from prefs at init, default 2s)
22332233- let HOLD_DELAY = 2000;
22322232+ // Configurable hold delay (read from prefs at init, default 1s)
22332233+ let HOLD_DELAY = 1000;
2234223422352235 // State
22362236 let isDragging = false;