simen-keyboard-listener 1.1.10 → 1.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +371 -41
- package/dist/index.d.ts +371 -41
- package/dist/index.js +886 -6
- package/dist/index.mjs +888 -4
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -61,6 +61,7 @@ __export(index_exports, {
|
|
|
61
61
|
getSystemContext: () => getSystemContext,
|
|
62
62
|
isAppRunning: () => isAppRunning,
|
|
63
63
|
isOsascriptAvailable: () => isOsascriptAvailable,
|
|
64
|
+
powershell: () => powershell_exports,
|
|
64
65
|
readFileContent: () => readFileContent,
|
|
65
66
|
readMultipleFiles: () => readMultipleFiles,
|
|
66
67
|
setBlockSystemHotkeys: () => setBlockSystemHotkeys
|
|
@@ -68,7 +69,7 @@ __export(index_exports, {
|
|
|
68
69
|
module.exports = __toCommonJS(index_exports);
|
|
69
70
|
var path = __toESM(require("path"));
|
|
70
71
|
var fs = __toESM(require("fs"));
|
|
71
|
-
var
|
|
72
|
+
var import_node_child_process3 = require("child_process");
|
|
72
73
|
var import_node_module = require("module");
|
|
73
74
|
var import_url = require("url");
|
|
74
75
|
|
|
@@ -1407,10 +1408,888 @@ async function getSystemContext(options) {
|
|
|
1407
1408
|
};
|
|
1408
1409
|
}
|
|
1409
1410
|
|
|
1411
|
+
// src/powershell/index.ts
|
|
1412
|
+
var powershell_exports = {};
|
|
1413
|
+
__export(powershell_exports, {
|
|
1414
|
+
escapeForPowerShell: () => escapeForPowerShell,
|
|
1415
|
+
executeAndParse: () => executeAndParse2,
|
|
1416
|
+
executeMultilineAndParse: () => executeMultilineAndParse2,
|
|
1417
|
+
executePowerShell: () => executePowerShell,
|
|
1418
|
+
executePowerShellLines: () => executePowerShellLines,
|
|
1419
|
+
getAgentContext: () => getAgentContext2,
|
|
1420
|
+
getClipboardContent: () => getClipboardContent2,
|
|
1421
|
+
getClipboardText: () => getClipboardText2,
|
|
1422
|
+
getExplorerCurrentFolder: () => getExplorerCurrentFolder,
|
|
1423
|
+
getExplorerSelection: () => getExplorerSelection,
|
|
1424
|
+
getExplorerWindows: () => getExplorerWindows,
|
|
1425
|
+
getFrontmostApp: () => getFrontmostApp2,
|
|
1426
|
+
getSystemContext: () => getSystemContext2,
|
|
1427
|
+
isPowerShellAvailable: () => isPowerShellAvailable
|
|
1428
|
+
});
|
|
1429
|
+
|
|
1430
|
+
// src/powershell/executor.ts
|
|
1431
|
+
var import_node_child_process2 = require("child_process");
|
|
1432
|
+
var import_node_util2 = require("util");
|
|
1433
|
+
var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
|
|
1434
|
+
var IS_WINDOWS = process.platform === "win32";
|
|
1435
|
+
var POWERSHELL_PATHS = [
|
|
1436
|
+
"pwsh.exe",
|
|
1437
|
+
// PowerShell Core (faster, recommended)
|
|
1438
|
+
"powershell.exe"
|
|
1439
|
+
// Windows PowerShell (legacy)
|
|
1440
|
+
];
|
|
1441
|
+
var DEFAULT_TIMEOUT2 = 3e4;
|
|
1442
|
+
var cachedPowerShellPath = null;
|
|
1443
|
+
async function findPowerShell() {
|
|
1444
|
+
if (cachedPowerShellPath) {
|
|
1445
|
+
return cachedPowerShellPath;
|
|
1446
|
+
}
|
|
1447
|
+
for (const psPath of POWERSHELL_PATHS) {
|
|
1448
|
+
try {
|
|
1449
|
+
await execFileAsync2(psPath, ["-NoProfile", "-Command", "exit 0"], { timeout: 5e3 });
|
|
1450
|
+
cachedPowerShellPath = psPath;
|
|
1451
|
+
return psPath;
|
|
1452
|
+
} catch {
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
return null;
|
|
1456
|
+
}
|
|
1457
|
+
function isPowerShellAvailable() {
|
|
1458
|
+
return IS_WINDOWS;
|
|
1459
|
+
}
|
|
1460
|
+
async function executePowerShell(script, options) {
|
|
1461
|
+
if (!IS_WINDOWS) {
|
|
1462
|
+
return {
|
|
1463
|
+
success: false,
|
|
1464
|
+
error: "PowerShell is only available on Windows"
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
const psPath = await findPowerShell();
|
|
1468
|
+
if (!psPath) {
|
|
1469
|
+
return {
|
|
1470
|
+
success: false,
|
|
1471
|
+
error: "PowerShell not found. Please install PowerShell Core or Windows PowerShell."
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
const timeout = options?.timeout ?? DEFAULT_TIMEOUT2;
|
|
1475
|
+
const startTime = Date.now();
|
|
1476
|
+
try {
|
|
1477
|
+
const { stdout, stderr } = await execFileAsync2(
|
|
1478
|
+
psPath,
|
|
1479
|
+
[
|
|
1480
|
+
"-NoProfile",
|
|
1481
|
+
// Don't load user profile (faster)
|
|
1482
|
+
"-NonInteractive",
|
|
1483
|
+
// Non-interactive mode
|
|
1484
|
+
"-ExecutionPolicy",
|
|
1485
|
+
"Bypass",
|
|
1486
|
+
// Bypass execution policy
|
|
1487
|
+
"-Command",
|
|
1488
|
+
script
|
|
1489
|
+
],
|
|
1490
|
+
{
|
|
1491
|
+
timeout,
|
|
1492
|
+
// Set output encoding to UTF-8 to handle Chinese characters
|
|
1493
|
+
encoding: "utf8"
|
|
1494
|
+
}
|
|
1495
|
+
);
|
|
1496
|
+
const executionTime = Date.now() - startTime;
|
|
1497
|
+
if (stderr && stderr.trim() && !stdout) {
|
|
1498
|
+
return {
|
|
1499
|
+
success: false,
|
|
1500
|
+
error: stderr.trim(),
|
|
1501
|
+
executionTime
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1504
|
+
return {
|
|
1505
|
+
success: true,
|
|
1506
|
+
data: stdout.trim(),
|
|
1507
|
+
executionTime
|
|
1508
|
+
};
|
|
1509
|
+
} catch (error) {
|
|
1510
|
+
const executionTime = Date.now() - startTime;
|
|
1511
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1512
|
+
return {
|
|
1513
|
+
success: false,
|
|
1514
|
+
error: errorMessage,
|
|
1515
|
+
executionTime
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
async function executePowerShellLines(lines, options) {
|
|
1520
|
+
const script = lines.join("; ");
|
|
1521
|
+
return executePowerShell(script, options);
|
|
1522
|
+
}
|
|
1523
|
+
async function executeAndParse2(script, parser, options) {
|
|
1524
|
+
const result = await executePowerShell(script, options);
|
|
1525
|
+
if (!result.success) {
|
|
1526
|
+
return {
|
|
1527
|
+
success: false,
|
|
1528
|
+
error: result.error,
|
|
1529
|
+
executionTime: result.executionTime
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
try {
|
|
1533
|
+
const parsed = parser(result.data ?? "");
|
|
1534
|
+
return {
|
|
1535
|
+
success: true,
|
|
1536
|
+
data: parsed,
|
|
1537
|
+
executionTime: result.executionTime
|
|
1538
|
+
};
|
|
1539
|
+
} catch (parseError) {
|
|
1540
|
+
const errorMessage = parseError instanceof Error ? parseError.message : String(parseError);
|
|
1541
|
+
return {
|
|
1542
|
+
success: false,
|
|
1543
|
+
error: `Failed to parse output: ${errorMessage}`,
|
|
1544
|
+
executionTime: result.executionTime
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
async function executeMultilineAndParse2(lines, parser, options) {
|
|
1549
|
+
const result = await executePowerShellLines(lines, options);
|
|
1550
|
+
if (!result.success) {
|
|
1551
|
+
return {
|
|
1552
|
+
success: false,
|
|
1553
|
+
error: result.error,
|
|
1554
|
+
executionTime: result.executionTime
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
try {
|
|
1558
|
+
const parsed = parser(result.data ?? "");
|
|
1559
|
+
return {
|
|
1560
|
+
success: true,
|
|
1561
|
+
data: parsed,
|
|
1562
|
+
executionTime: result.executionTime
|
|
1563
|
+
};
|
|
1564
|
+
} catch (parseError) {
|
|
1565
|
+
const errorMessage = parseError instanceof Error ? parseError.message : String(parseError);
|
|
1566
|
+
return {
|
|
1567
|
+
success: false,
|
|
1568
|
+
error: `Failed to parse output: ${errorMessage}`,
|
|
1569
|
+
executionTime: result.executionTime
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
function escapeForPowerShell(str) {
|
|
1574
|
+
return str.replace(/`/g, "``").replace(/"/g, '`"').replace(/\$/g, "`$").replace(/\n/g, "`n").replace(/\r/g, "`r").replace(/\t/g, "`t");
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
// src/powershell/scripts/batchContext.ts
|
|
1578
|
+
async function getAgentContext2(options) {
|
|
1579
|
+
const includeClipboard = options?.includeClipboard ?? true;
|
|
1580
|
+
const includeExplorerWindows = options?.includeExplorerWindows ?? true;
|
|
1581
|
+
const maxSelectedItems = options?.maxSelectedItems ?? 50;
|
|
1582
|
+
const script = `
|
|
1583
|
+
# Add required types
|
|
1584
|
+
try {
|
|
1585
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
1586
|
+
} catch {
|
|
1587
|
+
# Assembly may already be loaded, ignore error
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
try {
|
|
1591
|
+
Add-Type @"
|
|
1592
|
+
using System;
|
|
1593
|
+
using System.Runtime.InteropServices;
|
|
1594
|
+
using System.Text;
|
|
1595
|
+
|
|
1596
|
+
public class Win32 {
|
|
1597
|
+
[DllImport("user32.dll")]
|
|
1598
|
+
public static extern IntPtr GetForegroundWindow();
|
|
1599
|
+
|
|
1600
|
+
[DllImport("user32.dll")]
|
|
1601
|
+
public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
|
|
1602
|
+
|
|
1603
|
+
[DllImport("user32.dll")]
|
|
1604
|
+
public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int processId);
|
|
1605
|
+
}
|
|
1606
|
+
"@
|
|
1607
|
+
} catch {
|
|
1608
|
+
# Type may already be loaded, ignore error
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
# Initialize result object
|
|
1612
|
+
$result = @{
|
|
1613
|
+
frontmostApp = $null
|
|
1614
|
+
explorerSelection = $null
|
|
1615
|
+
explorerCurrentFolder = $null
|
|
1616
|
+
explorerWindows = @()
|
|
1617
|
+
desktopPath = ""
|
|
1618
|
+
clipboard = @{
|
|
1619
|
+
text = $null
|
|
1620
|
+
hasFiles = $false
|
|
1621
|
+
filePaths = @()
|
|
1622
|
+
hasImage = $false
|
|
1623
|
+
}
|
|
1624
|
+
timestamp = (Get-Date -Format "o")
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
# ============================================================================
|
|
1628
|
+
# 1. Get Frontmost Application
|
|
1629
|
+
# ============================================================================
|
|
1630
|
+
try {
|
|
1631
|
+
$hwnd = [Win32]::GetForegroundWindow()
|
|
1632
|
+
if ($hwnd -ne [IntPtr]::Zero) {
|
|
1633
|
+
$processId = 0
|
|
1634
|
+
[Win32]::GetWindowThreadProcessId($hwnd, [ref]$processId) | Out-Null
|
|
1635
|
+
|
|
1636
|
+
$process = Get-Process -Id $processId -ErrorAction SilentlyContinue
|
|
1637
|
+
if ($process) {
|
|
1638
|
+
$windowTitle = New-Object System.Text.StringBuilder 256
|
|
1639
|
+
[Win32]::GetWindowText($hwnd, $windowTitle, 256) | Out-Null
|
|
1640
|
+
|
|
1641
|
+
$processName = $process.ProcessName
|
|
1642
|
+
$path = ""
|
|
1643
|
+
try {
|
|
1644
|
+
$path = $process.Path
|
|
1645
|
+
if (-not $path) {
|
|
1646
|
+
$path = $process.MainModule.FileName
|
|
1647
|
+
}
|
|
1648
|
+
} catch {
|
|
1649
|
+
$path = ""
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
$name = $process.MainWindowTitle
|
|
1653
|
+
if (-not $name -or $name -eq "") {
|
|
1654
|
+
$name = $processName
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
$isExplorer = $processName -eq "explorer"
|
|
1658
|
+
|
|
1659
|
+
$result.frontmostApp = @{
|
|
1660
|
+
name = $name
|
|
1661
|
+
processName = $processName
|
|
1662
|
+
path = $path
|
|
1663
|
+
processId = $processId
|
|
1664
|
+
windowTitle = $windowTitle.ToString()
|
|
1665
|
+
isExplorer = $isExplorer
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
} catch {
|
|
1670
|
+
# Frontmost app detection failed
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
# ============================================================================
|
|
1674
|
+
# 2. Get Explorer Context (if frontmost app is Explorer)
|
|
1675
|
+
# ============================================================================
|
|
1676
|
+
if ($result.frontmostApp -and $result.frontmostApp.isExplorer) {
|
|
1677
|
+
try {
|
|
1678
|
+
$shell = New-Object -ComObject Shell.Application
|
|
1679
|
+
$windows = $shell.Windows()
|
|
1680
|
+
|
|
1681
|
+
$activeHwnd = [Win32]::GetForegroundWindow()
|
|
1682
|
+
$selectedItems = @()
|
|
1683
|
+
$currentFolder = $null
|
|
1684
|
+
$explorerWindows = @()
|
|
1685
|
+
$foundActiveWindow = $false
|
|
1686
|
+
|
|
1687
|
+
foreach ($window in $windows) {
|
|
1688
|
+
if ($window.Name -eq "File Explorer" -or $window.FullName -like "*explorer.exe") {
|
|
1689
|
+
try {
|
|
1690
|
+
$path = $window.Document.Folder.Self.Path
|
|
1691
|
+
if ($path) {
|
|
1692
|
+
# Check if this is the active window
|
|
1693
|
+
$isActive = $false
|
|
1694
|
+
try {
|
|
1695
|
+
$hwnd = $window.HWND
|
|
1696
|
+
if ($hwnd -eq $activeHwnd.ToInt64()) {
|
|
1697
|
+
$isActive = $true
|
|
1698
|
+
|
|
1699
|
+
# Get current folder for active window
|
|
1700
|
+
$currentFolder = $path
|
|
1701
|
+
|
|
1702
|
+
# Get selected items for active window
|
|
1703
|
+
$items = $window.Document.SelectedItems()
|
|
1704
|
+
if ($items -and $items.Count -gt 0) {
|
|
1705
|
+
foreach ($item in $items) {
|
|
1706
|
+
if ($selectedItems.Count -ge ${maxSelectedItems}) {
|
|
1707
|
+
break
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
$isFolder = $false
|
|
1711
|
+
try {
|
|
1712
|
+
$isFolder = $item.IsFolder
|
|
1713
|
+
} catch {
|
|
1714
|
+
try {
|
|
1715
|
+
if (Test-Path $item.Path -PathType Container) {
|
|
1716
|
+
$isFolder = $true
|
|
1717
|
+
}
|
|
1718
|
+
} catch {
|
|
1719
|
+
$isFolder = $false
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
$selectedItems += @{
|
|
1724
|
+
path = if ($item.Path) { $item.Path } else { "" }
|
|
1725
|
+
name = if ($item.Name) { $item.Name } else { "" }
|
|
1726
|
+
isFolder = $isFolder
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
} catch {
|
|
1732
|
+
# HWND may not be accessible
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
# Add to explorer windows list (if enabled)
|
|
1736
|
+
if (${includeExplorerWindows}) {
|
|
1737
|
+
$title = $window.LocationName
|
|
1738
|
+
if (-not $title) {
|
|
1739
|
+
$title = Split-Path $path -Leaf
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
$explorerWindows += @{
|
|
1743
|
+
title = if ($title) { $title } else { "" }
|
|
1744
|
+
path = if ($path) { $path } else { "" }
|
|
1745
|
+
isActive = $isActive
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
} catch {
|
|
1750
|
+
# Window may not have a valid path
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
# Set explorer selection
|
|
1756
|
+
if ($selectedItems.Count -gt 0) {
|
|
1757
|
+
$result.explorerSelection = @{
|
|
1758
|
+
items = $selectedItems
|
|
1759
|
+
count = $selectedItems.Count
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
# Set current folder
|
|
1764
|
+
$result.explorerCurrentFolder = $currentFolder
|
|
1765
|
+
|
|
1766
|
+
# Set explorer windows
|
|
1767
|
+
$result.explorerWindows = $explorerWindows
|
|
1768
|
+
|
|
1769
|
+
} catch {
|
|
1770
|
+
# Explorer context detection failed
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
# ============================================================================
|
|
1775
|
+
# 3. Get Desktop Path
|
|
1776
|
+
# ============================================================================
|
|
1777
|
+
try {
|
|
1778
|
+
$result.desktopPath = [Environment]::GetFolderPath("Desktop")
|
|
1779
|
+
} catch {
|
|
1780
|
+
$result.desktopPath = ""
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
# ============================================================================
|
|
1784
|
+
# 4. Get Clipboard Content (if enabled)
|
|
1785
|
+
# ============================================================================
|
|
1786
|
+
if (${includeClipboard}) {
|
|
1787
|
+
try {
|
|
1788
|
+
$clipboardData = [System.Windows.Forms.Clipboard]::GetDataObject()
|
|
1789
|
+
if ($clipboardData) {
|
|
1790
|
+
# Check for text
|
|
1791
|
+
if ($clipboardData.GetDataPresent([System.Windows.Forms.DataFormats]::Text)) {
|
|
1792
|
+
$result.clipboard.text = $clipboardData.GetData([System.Windows.Forms.DataFormats]::Text)
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
# Check for files
|
|
1796
|
+
if ($clipboardData.GetDataPresent([System.Windows.Forms.DataFormats]::FileDrop)) {
|
|
1797
|
+
$result.clipboard.hasFiles = $true
|
|
1798
|
+
$files = $clipboardData.GetData([System.Windows.Forms.DataFormats]::FileDrop)
|
|
1799
|
+
if ($files) {
|
|
1800
|
+
$result.clipboard.filePaths = @($files)
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
# Check for image
|
|
1805
|
+
if ($clipboardData.GetDataPresent([System.Windows.Forms.DataFormats]::Bitmap)) {
|
|
1806
|
+
$result.clipboard.hasImage = $true
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
} catch {
|
|
1810
|
+
# Clipboard access failed
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
# ============================================================================
|
|
1815
|
+
# Output JSON
|
|
1816
|
+
# ============================================================================
|
|
1817
|
+
$result | ConvertTo-Json -Compress -Depth 4
|
|
1818
|
+
`;
|
|
1819
|
+
const result = await executeAndParse2(
|
|
1820
|
+
script,
|
|
1821
|
+
(output) => {
|
|
1822
|
+
if (!output) {
|
|
1823
|
+
throw new Error("No output from PowerShell script");
|
|
1824
|
+
}
|
|
1825
|
+
const parsed = JSON.parse(output);
|
|
1826
|
+
return {
|
|
1827
|
+
frontmostApp: parsed.frontmostApp || {
|
|
1828
|
+
name: "",
|
|
1829
|
+
processName: "",
|
|
1830
|
+
path: "",
|
|
1831
|
+
processId: 0,
|
|
1832
|
+
windowTitle: "",
|
|
1833
|
+
isExplorer: false
|
|
1834
|
+
},
|
|
1835
|
+
explorerSelection: parsed.explorerSelection || null,
|
|
1836
|
+
explorerCurrentFolder: parsed.explorerCurrentFolder || null,
|
|
1837
|
+
explorerWindows: parsed.explorerWindows || [],
|
|
1838
|
+
desktopPath: parsed.desktopPath || "",
|
|
1839
|
+
clipboard: {
|
|
1840
|
+
text: parsed.clipboard?.text || null,
|
|
1841
|
+
hasFiles: parsed.clipboard?.hasFiles || false,
|
|
1842
|
+
filePaths: parsed.clipboard?.filePaths || [],
|
|
1843
|
+
hasImage: parsed.clipboard?.hasImage || false
|
|
1844
|
+
},
|
|
1845
|
+
timestamp: parsed.timestamp || (/* @__PURE__ */ new Date()).toISOString()
|
|
1846
|
+
};
|
|
1847
|
+
},
|
|
1848
|
+
options
|
|
1849
|
+
);
|
|
1850
|
+
if (!result.success) {
|
|
1851
|
+
return null;
|
|
1852
|
+
}
|
|
1853
|
+
return result.data ?? null;
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
// src/powershell/scripts/frontmostApp.ts
|
|
1857
|
+
async function getFrontmostApp2(options) {
|
|
1858
|
+
const script = `
|
|
1859
|
+
try {
|
|
1860
|
+
Add-Type @"
|
|
1861
|
+
using System;
|
|
1862
|
+
using System.Runtime.InteropServices;
|
|
1863
|
+
using System.Text;
|
|
1864
|
+
|
|
1865
|
+
public class Win32 {
|
|
1866
|
+
[DllImport("user32.dll")]
|
|
1867
|
+
public static extern IntPtr GetForegroundWindow();
|
|
1868
|
+
|
|
1869
|
+
[DllImport("user32.dll")]
|
|
1870
|
+
public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
|
|
1871
|
+
|
|
1872
|
+
[DllImport("user32.dll")]
|
|
1873
|
+
public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int processId);
|
|
1874
|
+
}
|
|
1875
|
+
"@
|
|
1876
|
+
} catch {
|
|
1877
|
+
# Type may already be loaded, ignore error
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
$hwnd = [Win32]::GetForegroundWindow()
|
|
1881
|
+
if ($hwnd -eq [IntPtr]::Zero) {
|
|
1882
|
+
Write-Output "{}"
|
|
1883
|
+
exit
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
$processId = 0
|
|
1887
|
+
[Win32]::GetWindowThreadProcessId($hwnd, [ref]$processId) | Out-Null
|
|
1888
|
+
|
|
1889
|
+
$process = Get-Process -Id $processId -ErrorAction SilentlyContinue
|
|
1890
|
+
if (-not $process) {
|
|
1891
|
+
Write-Output "{}"
|
|
1892
|
+
exit
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
$windowTitle = New-Object System.Text.StringBuilder 256
|
|
1896
|
+
[Win32]::GetWindowText($hwnd, $windowTitle, 256) | Out-Null
|
|
1897
|
+
|
|
1898
|
+
$processName = $process.ProcessName
|
|
1899
|
+
$path = ""
|
|
1900
|
+
try {
|
|
1901
|
+
$path = $process.Path
|
|
1902
|
+
if (-not $path) {
|
|
1903
|
+
$path = $process.MainModule.FileName
|
|
1904
|
+
}
|
|
1905
|
+
} catch {
|
|
1906
|
+
$path = ""
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
$name = $process.MainWindowTitle
|
|
1910
|
+
if (-not $name -or $name -eq "") {
|
|
1911
|
+
$name = $processName
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
$isExplorer = ($processName -eq "explorer")
|
|
1915
|
+
|
|
1916
|
+
$result = @{
|
|
1917
|
+
name = if ($name) { $name } else { "" }
|
|
1918
|
+
processName = if ($processName) { $processName } else { "" }
|
|
1919
|
+
path = if ($path) { $path } else { "" }
|
|
1920
|
+
processId = $processId
|
|
1921
|
+
windowTitle = $windowTitle.ToString()
|
|
1922
|
+
isExplorer = $isExplorer
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
$result | ConvertTo-Json -Compress
|
|
1926
|
+
`;
|
|
1927
|
+
const result = await executeAndParse2(
|
|
1928
|
+
script,
|
|
1929
|
+
(output) => {
|
|
1930
|
+
if (!output || output === "{}") {
|
|
1931
|
+
throw new Error("No frontmost window found");
|
|
1932
|
+
}
|
|
1933
|
+
return JSON.parse(output);
|
|
1934
|
+
},
|
|
1935
|
+
options
|
|
1936
|
+
);
|
|
1937
|
+
if (!result.success) {
|
|
1938
|
+
return null;
|
|
1939
|
+
}
|
|
1940
|
+
return result.data ?? null;
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
// src/powershell/scripts/explorerSelection.ts
|
|
1944
|
+
async function getExplorerSelection(options) {
|
|
1945
|
+
const maxItems = 50;
|
|
1946
|
+
const script = `
|
|
1947
|
+
try {
|
|
1948
|
+
$shell = New-Object -ComObject Shell.Application
|
|
1949
|
+
$windows = $shell.Windows()
|
|
1950
|
+
|
|
1951
|
+
$selectedItems = @()
|
|
1952
|
+
$foundSelection = $false
|
|
1953
|
+
|
|
1954
|
+
foreach ($window in $windows) {
|
|
1955
|
+
if ($foundSelection) { break }
|
|
1956
|
+
|
|
1957
|
+
# Check if this is an Explorer window
|
|
1958
|
+
if ($window.Name -eq "File Explorer" -or $window.FullName -like "*explorer.exe") {
|
|
1959
|
+
try {
|
|
1960
|
+
$items = $window.Document.SelectedItems()
|
|
1961
|
+
if ($items -and $items.Count -gt 0) {
|
|
1962
|
+
foreach ($item in $items) {
|
|
1963
|
+
if ($selectedItems.Count -ge ${maxItems}) {
|
|
1964
|
+
break
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
$isFolder = $false
|
|
1968
|
+
try {
|
|
1969
|
+
$isFolder = $item.IsFolder
|
|
1970
|
+
} catch {
|
|
1971
|
+
# If IsFolder fails, check if it's a directory
|
|
1972
|
+
try {
|
|
1973
|
+
if (Test-Path $item.Path -PathType Container) {
|
|
1974
|
+
$isFolder = $true
|
|
1975
|
+
}
|
|
1976
|
+
} catch {
|
|
1977
|
+
$isFolder = $false
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
$selectedItems += @{
|
|
1982
|
+
path = if ($item.Path) { $item.Path } else { "" }
|
|
1983
|
+
name = if ($item.Name) { $item.Name } else { "" }
|
|
1984
|
+
isFolder = $isFolder
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
$foundSelection = $true
|
|
1988
|
+
}
|
|
1989
|
+
} catch {
|
|
1990
|
+
# Window may not support selection, continue to next window
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
$result = @{
|
|
1996
|
+
items = $selectedItems
|
|
1997
|
+
count = $selectedItems.Count
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
$result | ConvertTo-Json -Compress -Depth 3
|
|
2001
|
+
} catch {
|
|
2002
|
+
# COM object creation failed or other error
|
|
2003
|
+
Write-Output '{"items":[],"count":0}'
|
|
2004
|
+
}
|
|
2005
|
+
`;
|
|
2006
|
+
const result = await executeAndParse2(
|
|
2007
|
+
script,
|
|
2008
|
+
(output) => {
|
|
2009
|
+
if (!output) {
|
|
2010
|
+
return { items: [], count: 0 };
|
|
2011
|
+
}
|
|
2012
|
+
const parsed = JSON.parse(output);
|
|
2013
|
+
return {
|
|
2014
|
+
items: parsed.items || [],
|
|
2015
|
+
count: parsed.count || 0
|
|
2016
|
+
};
|
|
2017
|
+
},
|
|
2018
|
+
options
|
|
2019
|
+
);
|
|
2020
|
+
if (!result.success) {
|
|
2021
|
+
return null;
|
|
2022
|
+
}
|
|
2023
|
+
const data = result.data;
|
|
2024
|
+
if (!data || data.count === 0) {
|
|
2025
|
+
return null;
|
|
2026
|
+
}
|
|
2027
|
+
return data;
|
|
2028
|
+
}
|
|
2029
|
+
async function getExplorerCurrentFolder(options) {
|
|
2030
|
+
const script = `
|
|
2031
|
+
$shell = New-Object -ComObject Shell.Application
|
|
2032
|
+
$windows = $shell.Windows()
|
|
2033
|
+
|
|
2034
|
+
foreach ($window in $windows) {
|
|
2035
|
+
# Check if this is an Explorer window
|
|
2036
|
+
if ($window.Name -eq "File Explorer" -or $window.FullName -like "*explorer.exe") {
|
|
2037
|
+
try {
|
|
2038
|
+
$path = $window.Document.Folder.Self.Path
|
|
2039
|
+
if ($path) {
|
|
2040
|
+
Write-Output $path
|
|
2041
|
+
exit
|
|
2042
|
+
}
|
|
2043
|
+
} catch {
|
|
2044
|
+
# Window may not have a valid path
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
`;
|
|
2049
|
+
const result = await executeAndParse2(
|
|
2050
|
+
script,
|
|
2051
|
+
(output) => output || null,
|
|
2052
|
+
options
|
|
2053
|
+
);
|
|
2054
|
+
if (!result.success) {
|
|
2055
|
+
return null;
|
|
2056
|
+
}
|
|
2057
|
+
return result.data ?? null;
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
// src/powershell/scripts/explorerWindows.ts
|
|
2061
|
+
async function getExplorerWindows(options) {
|
|
2062
|
+
const script = `
|
|
2063
|
+
try {
|
|
2064
|
+
Add-Type @"
|
|
2065
|
+
using System;
|
|
2066
|
+
using System.Runtime.InteropServices;
|
|
2067
|
+
|
|
2068
|
+
public class Win32 {
|
|
2069
|
+
[DllImport("user32.dll")]
|
|
2070
|
+
public static extern IntPtr GetForegroundWindow();
|
|
2071
|
+
}
|
|
2072
|
+
"@
|
|
2073
|
+
} catch {
|
|
2074
|
+
# Type may already be loaded, ignore error
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
try {
|
|
2078
|
+
$shell = New-Object -ComObject Shell.Application
|
|
2079
|
+
$windows = $shell.Windows()
|
|
2080
|
+
|
|
2081
|
+
$activeHwnd = [Win32]::GetForegroundWindow()
|
|
2082
|
+
|
|
2083
|
+
$explorerWindows = @()
|
|
2084
|
+
|
|
2085
|
+
foreach ($window in $windows) {
|
|
2086
|
+
# Check if this is an Explorer window
|
|
2087
|
+
if ($window.Name -eq "File Explorer" -or $window.FullName -like "*explorer.exe") {
|
|
2088
|
+
try {
|
|
2089
|
+
$path = $window.Document.Folder.Self.Path
|
|
2090
|
+
if ($path) {
|
|
2091
|
+
$title = $window.LocationName
|
|
2092
|
+
if (-not $title) {
|
|
2093
|
+
$title = Split-Path $path -Leaf
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
# Check if this is the active window
|
|
2097
|
+
$isActive = $false
|
|
2098
|
+
try {
|
|
2099
|
+
$hwnd = $window.HWND
|
|
2100
|
+
if ($hwnd -eq $activeHwnd.ToInt64()) {
|
|
2101
|
+
$isActive = $true
|
|
2102
|
+
}
|
|
2103
|
+
} catch {
|
|
2104
|
+
# HWND may not be accessible
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
$explorerWindows += @{
|
|
2108
|
+
title = if ($title) { $title } else { "" }
|
|
2109
|
+
path = if ($path) { $path } else { "" }
|
|
2110
|
+
isActive = $isActive
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
} catch {
|
|
2114
|
+
# Window may not have a valid path, continue to next window
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
$result = @{
|
|
2120
|
+
windows = $explorerWindows
|
|
2121
|
+
count = $explorerWindows.Count
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
$result | ConvertTo-Json -Compress -Depth 3
|
|
2125
|
+
} catch {
|
|
2126
|
+
# COM object creation failed or other error
|
|
2127
|
+
Write-Output '{"windows":[],"count":0}'
|
|
2128
|
+
}
|
|
2129
|
+
`;
|
|
2130
|
+
const result = await executeAndParse2(
|
|
2131
|
+
script,
|
|
2132
|
+
(output) => {
|
|
2133
|
+
if (!output) {
|
|
2134
|
+
return { windows: [], count: 0 };
|
|
2135
|
+
}
|
|
2136
|
+
const parsed = JSON.parse(output);
|
|
2137
|
+
return {
|
|
2138
|
+
windows: parsed.windows || [],
|
|
2139
|
+
count: parsed.count || 0
|
|
2140
|
+
};
|
|
2141
|
+
},
|
|
2142
|
+
options
|
|
2143
|
+
);
|
|
2144
|
+
if (!result.success) {
|
|
2145
|
+
return null;
|
|
2146
|
+
}
|
|
2147
|
+
return result.data ?? null;
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
// src/powershell/scripts/clipboard.ts
|
|
2151
|
+
async function getClipboardText2(options) {
|
|
2152
|
+
const script = `
|
|
2153
|
+
try {
|
|
2154
|
+
$text = Get-Clipboard -Format Text -ErrorAction SilentlyContinue
|
|
2155
|
+
if ($text) {
|
|
2156
|
+
Write-Output $text
|
|
2157
|
+
}
|
|
2158
|
+
} catch {
|
|
2159
|
+
# Clipboard may be empty or contain non-text data
|
|
2160
|
+
}
|
|
2161
|
+
`;
|
|
2162
|
+
const result = await executeAndParse2(
|
|
2163
|
+
script,
|
|
2164
|
+
(output) => output || null,
|
|
2165
|
+
options
|
|
2166
|
+
);
|
|
2167
|
+
if (!result.success) {
|
|
2168
|
+
return null;
|
|
2169
|
+
}
|
|
2170
|
+
return result.data ?? null;
|
|
2171
|
+
}
|
|
2172
|
+
async function getClipboardContent2(options) {
|
|
2173
|
+
const script = `
|
|
2174
|
+
try {
|
|
2175
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
2176
|
+
} catch {
|
|
2177
|
+
# Assembly may already be loaded, ignore error
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
try {
|
|
2181
|
+
$clipboard = [System.Windows.Forms.Clipboard]::GetDataObject()
|
|
2182
|
+
if (-not $clipboard) {
|
|
2183
|
+
Write-Output '{"text":null,"hasFiles":false,"filePaths":[],"hasImage":false}'
|
|
2184
|
+
exit
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
$text = $null
|
|
2188
|
+
$hasFiles = $false
|
|
2189
|
+
$filePaths = @()
|
|
2190
|
+
$hasImage = $false
|
|
2191
|
+
|
|
2192
|
+
# Check for text
|
|
2193
|
+
try {
|
|
2194
|
+
if ($clipboard.GetDataPresent([System.Windows.Forms.DataFormats]::Text)) {
|
|
2195
|
+
$text = $clipboard.GetData([System.Windows.Forms.DataFormats]::Text)
|
|
2196
|
+
}
|
|
2197
|
+
} catch {
|
|
2198
|
+
# Text access failed, continue
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
# Check for files
|
|
2202
|
+
try {
|
|
2203
|
+
if ($clipboard.GetDataPresent([System.Windows.Forms.DataFormats]::FileDrop)) {
|
|
2204
|
+
$hasFiles = $true
|
|
2205
|
+
$files = $clipboard.GetData([System.Windows.Forms.DataFormats]::FileDrop)
|
|
2206
|
+
if ($files) {
|
|
2207
|
+
$filePaths = @($files)
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
} catch {
|
|
2211
|
+
# File access failed, continue
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
# Check for image
|
|
2215
|
+
try {
|
|
2216
|
+
if ($clipboard.GetDataPresent([System.Windows.Forms.DataFormats]::Bitmap)) {
|
|
2217
|
+
$hasImage = $true
|
|
2218
|
+
}
|
|
2219
|
+
} catch {
|
|
2220
|
+
# Image check failed, continue
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
$result = @{
|
|
2224
|
+
text = $text
|
|
2225
|
+
hasFiles = $hasFiles
|
|
2226
|
+
filePaths = $filePaths
|
|
2227
|
+
hasImage = $hasImage
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
$result | ConvertTo-Json -Compress
|
|
2231
|
+
} catch {
|
|
2232
|
+
# Clipboard access completely failed
|
|
2233
|
+
Write-Output '{"text":null,"hasFiles":false,"filePaths":[],"hasImage":false}'
|
|
2234
|
+
}
|
|
2235
|
+
`;
|
|
2236
|
+
const result = await executeAndParse2(
|
|
2237
|
+
script,
|
|
2238
|
+
(output) => {
|
|
2239
|
+
if (!output || output === "{}") {
|
|
2240
|
+
return {
|
|
2241
|
+
text: null,
|
|
2242
|
+
hasFiles: false,
|
|
2243
|
+
filePaths: [],
|
|
2244
|
+
hasImage: false
|
|
2245
|
+
};
|
|
2246
|
+
}
|
|
2247
|
+
const parsed = JSON.parse(output);
|
|
2248
|
+
return {
|
|
2249
|
+
text: parsed.text || null,
|
|
2250
|
+
hasFiles: parsed.hasFiles || false,
|
|
2251
|
+
filePaths: parsed.filePaths || [],
|
|
2252
|
+
hasImage: parsed.hasImage || false
|
|
2253
|
+
};
|
|
2254
|
+
},
|
|
2255
|
+
options
|
|
2256
|
+
);
|
|
2257
|
+
if (!result.success) {
|
|
2258
|
+
return null;
|
|
2259
|
+
}
|
|
2260
|
+
return result.data ?? null;
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
// src/powershell/index.ts
|
|
2264
|
+
async function getSystemContext2(options) {
|
|
2265
|
+
if (!isPowerShellAvailable()) {
|
|
2266
|
+
return null;
|
|
2267
|
+
}
|
|
2268
|
+
const frontmostApp = await getFrontmostApp2(options);
|
|
2269
|
+
if (!frontmostApp) {
|
|
2270
|
+
return null;
|
|
2271
|
+
}
|
|
2272
|
+
let explorerSelection = null;
|
|
2273
|
+
let explorerCurrentFolder = null;
|
|
2274
|
+
if (frontmostApp.isExplorer) {
|
|
2275
|
+
[explorerSelection, explorerCurrentFolder] = await Promise.all([
|
|
2276
|
+
getExplorerSelection(options),
|
|
2277
|
+
getExplorerCurrentFolder(options)
|
|
2278
|
+
]);
|
|
2279
|
+
}
|
|
2280
|
+
const clipboard = await getClipboardContent2(options);
|
|
2281
|
+
return {
|
|
2282
|
+
frontmostApp,
|
|
2283
|
+
explorerSelection,
|
|
2284
|
+
explorerCurrentFolder,
|
|
2285
|
+
clipboard: clipboard ?? { text: null, hasFiles: false, filePaths: [], hasImage: false }
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
|
|
1410
2289
|
// src/index.ts
|
|
1411
2290
|
var import_meta = {};
|
|
1412
2291
|
var IS_MACOS2 = process.platform === "darwin";
|
|
1413
|
-
var
|
|
2292
|
+
var IS_WINDOWS2 = process.platform === "win32";
|
|
1414
2293
|
var PLATFORM_PACKAGES = {
|
|
1415
2294
|
"darwin-arm64": "@simen-keyboard-listener/darwin-arm64",
|
|
1416
2295
|
"win32-x64": "@simen-keyboard-listener/win32-x64"
|
|
@@ -1563,7 +2442,7 @@ var NativeKeyboardListener = class _NativeKeyboardListener {
|
|
|
1563
2442
|
}
|
|
1564
2443
|
};
|
|
1565
2444
|
function getGlobalKeyboardListener() {
|
|
1566
|
-
if (!IS_MACOS2 && !
|
|
2445
|
+
if (!IS_MACOS2 && !IS_WINDOWS2) {
|
|
1567
2446
|
throw new Error(`Unsupported platform for global keyboard listener: ${process.platform}`);
|
|
1568
2447
|
}
|
|
1569
2448
|
return NativeKeyboardListener.getInstance();
|
|
@@ -1572,7 +2451,7 @@ function createGlobalKeyboardListener() {
|
|
|
1572
2451
|
return getGlobalKeyboardListener();
|
|
1573
2452
|
}
|
|
1574
2453
|
function checkKeyboardPermission() {
|
|
1575
|
-
if (!IS_MACOS2 && !
|
|
2454
|
+
if (!IS_MACOS2 && !IS_WINDOWS2) {
|
|
1576
2455
|
return false;
|
|
1577
2456
|
}
|
|
1578
2457
|
try {
|
|
@@ -1593,7 +2472,7 @@ function openMacAccessibilitySettings() {
|
|
|
1593
2472
|
}
|
|
1594
2473
|
lastMacAccessibilitySettingsOpenTs = now;
|
|
1595
2474
|
try {
|
|
1596
|
-
(0,
|
|
2475
|
+
(0, import_node_child_process3.execFileSync)("open", ["x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"], {
|
|
1597
2476
|
stdio: "ignore"
|
|
1598
2477
|
});
|
|
1599
2478
|
} catch {
|
|
@@ -1658,7 +2537,7 @@ function getSelectedTextSmart() {
|
|
|
1658
2537
|
}
|
|
1659
2538
|
}
|
|
1660
2539
|
function setBlockSystemHotkeys(block) {
|
|
1661
|
-
if (!
|
|
2540
|
+
if (!IS_WINDOWS2) {
|
|
1662
2541
|
return;
|
|
1663
2542
|
}
|
|
1664
2543
|
try {
|
|
@@ -1700,6 +2579,7 @@ function setBlockSystemHotkeys(block) {
|
|
|
1700
2579
|
getSystemContext,
|
|
1701
2580
|
isAppRunning,
|
|
1702
2581
|
isOsascriptAvailable,
|
|
2582
|
+
powershell,
|
|
1703
2583
|
readFileContent,
|
|
1704
2584
|
readMultipleFiles,
|
|
1705
2585
|
setBlockSystemHotkeys
|