const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); const HOME = process.env.HOME; const LAYOUT_FILE = path.join(HOME, '.hammerspoon/saved_layout.json'); const CONFIG_FILE = path.join(HOME, '.hammerspoon/config.json'); const TEMP_JS_FILE = '/tmp/raycast_window_engine.js'; let stubbornApps = ["Gemini", "AFFiNE", "Terminal", "System Settings"]; let ignoreApps = ["TheBoringNotch", "Control Center", "Notification Center", "Dock", "BetterDisplay", "Stats", "DockDoor", "Bartender 6", "Bartender", "Arq", "Arq Agent", "TypeWhisper", "com.typewhisper.mac"]; if (fs.existsSync(CONFIG_FILE)) { try { const cfg = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); if (cfg.stubbornAppsList) stubbornApps = Object.keys(cfg.stubbornAppsList); if (cfg.ignoreListItems) ignoreApps = Object.keys(cfg.ignoreListItems); } catch (e) {} } const action = process.argv[2]; function getVisibleWindows() { // Queries the global System Events window array directly, replicating hs.window.allWindows() const captureScript = ` var sys = Application('System Events'); var out = []; try { var allWins = sys.windows(); for (var i = 0; i < allWins.length; i++) { var win = allWins[i]; var proc = win.process(); if (!proc) continue; var procName = proc.name(); var bundleID = proc.bundleIdentifier() || ""; var size = win.size(); var pos = win.position(); var title = win.title() || ""; if (size && size[0] > 10 && size[1] > 10 && pos && pos[0] !== undefined) { out.push({ appName: procName, bundleID: bundleID, winTitle: title, x: Math.floor(pos[0]), y: Math.floor(pos[1]), w: Math.floor(size[0]), h: Math.floor(size[1]) }); } } } catch(e) {} JSON.stringify(out); `; try { fs.writeFileSync(TEMP_JS_FILE, captureScript, 'utf8'); const rawOutput = execSync(`osascript -l JavaScript ${TEMP_JS_FILE}`).toString().trim(); if (fs.existsSync(TEMP_JS_FILE)) fs.unlinkSync(TEMP_JS_FILE); if (!rawOutput || rawOutput === "[]") return []; return JSON.parse(rawOutput); } catch (e) { if (fs.existsSync(TEMP_JS_FILE)) fs.unlinkSync(TEMP_JS_FILE); return []; } } function saveLayout() { const windows = getVisibleWindows().filter(w => w && !ignoreApps.includes(w.appName) && !ignoreApps.includes(w.bundleID)); if (windows.length === 0) { console.log("No active windows detected to save."); return; } const layout = { saveTime: new Date().toLocaleTimeString(), windows: windows }; if (!fs.existsSync(path.dirname(LAYOUT_FILE))) { fs.mkdirSync(path.dirname(LAYOUT_FILE), { recursive: true }); } fs.writeFileSync(LAYOUT_FILE, JSON.stringify(layout, null, 2)); console.log("Layout Saved Successfully"); } function restoreLayout() { if (!fs.existsSync(LAYOUT_FILE)) { console.log("Restore failed: No saved layout found."); return; } const data = JSON.parse(fs.readFileSync(LAYOUT_FILE, 'utf8')); if (!data.windows || data.windows.length === 0) return; const savedByApp = {}; data.windows.forEach(win => { if (ignoreApps.includes(win.appName) || ignoreApps.includes(win.bundleID)) return; if (!savedByApp[win.appName]) savedByApp[win.appName] = []; savedByApp[win.appName].push(win); }); Object.keys(savedByApp).forEach(appName => { savedByApp[appName].sort((a, b) => { if (a.y === b.y) return a.x - b.x; return a.y - b.y; }); }); let restoreScript = `var sys = Application('System Events');\n`; Object.keys(savedByApp).forEach(appName => { const savedEntries = savedByApp[appName]; restoreScript += ` try { if (sys.processes.names().includes("${appName}")) { var proc = sys.processes["${appName}"]; var physicalWins = []; var allWins = proc.windows(); for (var k = 0; k < allWins.length; k++) { try { var w = allWins[k]; var s = w.size(); if (s && s[0] > 10 && s[1] > 10) { physicalWins.push(w); } } catch(err) {} } physicalWins.sort(function(a, b) { var posA = a.position(); var posB = b.position(); if (!posA || !posB) return 0; if (posA[1] === posB[1]) return posA[0] - posB[0]; return posA[1] - posB[1]; }); var savedEntriesForApp = ${JSON.stringify(savedEntries)}; for (var i = 0; i < Math.min(savedEntriesForApp.length, physicalWins.length); i++) { try { var winData = savedEntriesForApp[i]; var targetWin = physicalWins[i]; targetWin.position = [winData.x, winData.y]; targetWin.size = [winData.w, winData.h]; } catch(err) {} } } } catch(e) {} `; }); try { fs.writeFileSync(TEMP_JS_FILE, restoreScript, 'utf8'); execSync(`osascript -l JavaScript ${TEMP_JS_FILE} 2>/dev/null`); if (data.windows.some(w => stubbornApps.includes(w.appName))) { setTimeout(() => { try { execSync(`osascript -l JavaScript ${TEMP_JS_FILE} 2>/dev/null`); } catch(e){} if (fs.existsSync(TEMP_JS_FILE)) fs.unlinkSync(TEMP_JS_FILE); }, 500); } else { if (fs.existsSync(TEMP_JS_FILE)) fs.unlinkSync(TEMP_JS_FILE); } } catch (e) {} console.log("Layout Restored Successfully"); } function rescueWindows() { let rescueScript = ` var sys = Application('System Events'); var allWins = sys.windows(); var stagger = 0; for (var i = 0; i < allWins.length; i++) { try { var win = allWins[i]; var proc = win.process(); if (proc && proc.name() !== "Finder" && proc.name() !== "Dock") { var s = win.size(); if (s && s[0] > 10) { win.position = [50 + stagger, 50 + stagger]; stagger += 30; if (stagger > 150) stagger = 0; } } } catch(e) {} } `; try { fs.writeFileSync(TEMP_JS_FILE, rescueScript, 'utf8'); execSync(`osascript -l JavaScript ${TEMP_JS_FILE} 2>/dev/null`); } catch(e){} if (fs.existsSync(TEMP_JS_FILE)) fs.unlinkSync(TEMP_JS_FILE); console.log("Windows Cascaded to Laptop"); } if (action === 'save') saveLayout(); if (action === 'restore') restoreLayout(); if (action === 'rescue') rescueWindows();