@typecad/typecad
Version:
🤖programmatically 💥create 🛰️hardware
145 lines (116 loc) • 4.54 kB
JavaScript
import { exec } from 'child_process';
import os from 'os';
class WindowCapture {
constructor() {
this.platform = os.platform();
}
async getWindowTitles() {
return new Promise((resolve, reject) => {
let command;
if (this.platform === 'win32') {
// Windows: Use PowerShell to get window titles
command = `powershell "Get-Process | Where-Object {$_.MainWindowTitle -ne ''} | Select-Object MainWindowTitle | ForEach-Object {$_.MainWindowTitle}"`;
} else if (this.platform === 'linux') {
// Linux: Use wmctrl or xdotool (fallback to xwininfo)
command = `wmctrl -l | awk '{for(i=4;i<=NF;i++) printf "%s ", $i; print ""}' 2>/dev/null || xdotool search --name ".*" getwindowname %@ 2>/dev/null || xwininfo -root -tree | grep -E "^\s+0x" | grep -o '".*"' | sed 's/"//g'`;
} else if (this.platform === 'darwin') {
// macOS: Use osascript to get window titles
command = `osascript -e 'tell application "System Events" to get name of (processes whose background only is false and name is not "")' | tr ',' '\n' | sed 's/^[[:space:]]*//'`;
} else {
reject(new Error(`Unsupported platform: ${this.platform}`));
return;
}
exec(command, (error, stdout, stderr) => {
if (error) {
reject(new Error(`Command failed: ${error.message}`));
return;
}
const titles = stdout
.split('\n')
.map(line => line.trim())
.filter(line => line.length > 0)
.filter(title => title !== 'N/A' && title !== '');
resolve(titles);
});
});
}
filterTitles(titles, filterString) {
if (!filterString) return titles;
return titles.filter(title =>
title.toLowerCase().includes(filterString.toLowerCase())
);
}
async capture(filterString = null) {
try {
console.log(`🔍 Capturing window titles on ${this.platform}...`);
const allTitles = await this.getWindowTitles();
console.log(`📊 Found ${allTitles.length} windows total`);
const filteredTitles = this.filterTitles(allTitles, filterString);
if (filterString) {
console.log(`🎯 Filtered for "${filterString}": ${filteredTitles.length} matches\n`);
} else {
console.log(`📋 All window titles:\n`);
}
if (filteredTitles.length === 0) {
console.log('❌ No matching windows found');
return [];
}
filteredTitles.forEach((title, index) => {
console.log(`${index + 1}. ${title}`);
});
return filteredTitles;
} catch (error) {
console.error(`❌ Error: ${error.message}`);
if (this.platform === 'linux') {
console.log('\n💡 Install required tools:');
console.log(' Ubuntu/Debian: sudo apt-get install wmctrl xdotool');
console.log(' Fedora: sudo dnf install wmctrl xdotool');
console.log(' Arch: sudo pacman -S wmctrl xdotool');
}
return [];
}
}
}
// CLI Interface
function parseArgs() {
const args = process.argv.slice(2);
let filterString = null;
for (const arg of args) {
if (arg.startsWith('--filter=')) {
filterString = arg.split('=')[1].replace(/['"]/g, '');
} else if (arg === '--help' || arg === '-h') {
showHelp();
process.exit(0);
}
}
return { filterString };
}
function showHelp() {
console.log(`
🪟 Window Title Capture Tool
Usage:
node index.js # Show all window titles
node index.js --filter="Chrome" # Filter for windows containing "Chrome"
npm start # Show all window titles
npm test # Example with Chrome filter
Options:
--filter=<string> Filter window titles containing this string (case-insensitive)
--help, -h Show this help message
Examples:
node index.js --filter="Visual Studio"
node index.js --filter="firefox"
node index.js --filter="Terminal"
`);
}
// Main execution
async function main() {
const { filterString } = parseArgs();
const windowCapture = new WindowCapture();
await windowCapture.capture(filterString);
}
// Run if called directly
if (import.meta.url === `file://${process.argv[1]}`) {
main().catch(console.error);
}
export default WindowCapture;