Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"build:windows": "node scripts/build.mjs win32",
"postinstall": "node scripts/postinstall.mjs",
"test": "node test/test.mjs && node test/open-links.test.mjs && node test/test-status-item.mjs",
"test:keyboard": "node test/cmd-w-close.test.mjs",
"test:platform": "node test/platform.mjs",
"publish:check": "./scripts/publish.sh --dry-run",
"publish:npm": "./scripts/publish.sh"
Expand Down
5 changes: 5 additions & 0 deletions src/glimpse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,11 @@ class GlimpsePanel: NSWindow {
return false
}

if modifiers == cmd && chars == "w" {
close()
return true
}

let action: Selector?
switch (chars, modifiers) {
case ("c", cmd): action = #selector(NSText.copy(_:))
Expand Down
97 changes: 97 additions & 0 deletions test/cmd-w-close.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { spawnSync } from 'node:child_process';
import { open } from '../src/glimpse.mjs';

const TIMEOUT_MS = 10_000;
const TITLE = `Glimpse Cmd-W Test ${process.pid}`;

const HTML = `<!doctype html>
<html>
<body style="font-family: system-ui; padding: 24px;">
<h1>Cmd-W close test</h1>
<p>This window should close when the test sends ⌘W.</p>
</body>
</html>`;

function waitFor(emitter, event, timeoutMs = TIMEOUT_MS) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error(`Timeout waiting for '${event}' after ${timeoutMs}ms`));
}, timeoutMs);

emitter.once(event, (...args) => {
clearTimeout(timer);
resolve(args);
});

emitter.once('error', (err) => {
clearTimeout(timer);
reject(err);
});
});
}

function sendCommandWToWindow(title) {
const script = `
tell application "System Events"
repeat with appProcess in processes
if name of appProcess is "glimpse" then
repeat with appWindow in windows of appProcess
if name of appWindow is "${title.replaceAll('"', '\\"')}" then
set frontmost of appProcess to true
perform action "AXRaise" of appWindow
delay 0.2
keystroke "w" using command down
return
end if
end repeat
end if
end repeat
error "No Glimpse window named ${title.replaceAll('"', '\\"')}"
end tell
`;

return spawnSync('osascript', ['-e', script], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
});
}

if (process.platform !== 'darwin') {
console.log('Cmd-W close test skipped: macOS-only keyboard shortcut');
process.exit(0);
}

console.log('glimpse Cmd-W close test\n');

let win;
try {
win = open(HTML, {
title: TITLE,
width: 520,
height: 260,
});

await waitFor(win, 'ready');
console.log(' ✓ window ready');

const result = sendCommandWToWindow(TITLE);
if (result.status !== 0) {
throw new Error([
'Failed to send ⌘W with osascript.',
'This test requires macOS Accessibility permission for the terminal running npm.',
result.stderr.trim(),
result.stdout.trim(),
].filter(Boolean).join('\n'));
}
console.log(' ✓ sent ⌘W');

await waitFor(win, 'closed');
console.log(' ✓ closed event received');

console.log('\nCmd-W close test passed');
process.exit(0);
} catch (err) {
console.error(`\n ✗ ${err.message}`);
win?.close();
process.exit(1);
}