mac-helper 0.8.1 → 0.9.0
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/lib/app.js +3 -6
- package/lib/chrome.js +13 -20
- package/lib/dialog.js +4 -7
- package/lib/finder.js +32 -41
- package/lib/index.d.ts +5 -5
- package/lib/index.js +5 -21
- package/lib/iterm.js +4 -8
- package/lib/system-events.js +3 -5
- package/package.json +19 -14
- package/src/app.ts +0 -26
- package/src/chrome.ts +0 -47
- package/src/dialog.ts +0 -132
- package/src/finder.ts +0 -178
- package/src/index.ts +0 -8
- package/src/iterm.ts +0 -44
- package/src/system-events.ts +0 -24
package/lib/app.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isAppRunning = isAppRunning;
|
|
4
|
-
const node_child_process_1 = require("node:child_process");
|
|
1
|
+
import { exec } from 'node:child_process';
|
|
5
2
|
/**
|
|
6
3
|
* file path based process running detect.
|
|
7
4
|
* for vscode, process name = `Code`, file is `Visual Studio Code.app`, the `Visual Studio Code` should be passed
|
|
8
5
|
*/
|
|
9
|
-
function isAppRunning(appName) {
|
|
6
|
+
export function isAppRunning(appName) {
|
|
10
7
|
return new Promise((resolve, reject) => {
|
|
11
|
-
|
|
8
|
+
exec(`ps -ax -o command`, (err, stdout, stderr) => {
|
|
12
9
|
if (err)
|
|
13
10
|
return reject(err);
|
|
14
11
|
const lines = stdout
|
package/lib/chrome.js
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
exports.isChromiumAppRunning = isChromiumAppRunning;
|
|
5
|
-
exports.getChromiumActiveTab = getChromiumActiveTab;
|
|
6
|
-
exports.getChromiumIsFrontmost = getChromiumIsFrontmost;
|
|
7
|
-
const run_1 = require("@jxa/run");
|
|
8
|
-
const app_1 = require("./app");
|
|
9
|
-
var BrowserAppName;
|
|
1
|
+
import { run } from '@jxa/run';
|
|
2
|
+
import { isAppRunning } from './app.js';
|
|
3
|
+
export var BrowserAppName;
|
|
10
4
|
(function (BrowserAppName) {
|
|
11
5
|
BrowserAppName["Chrome"] = "Google Chrome";
|
|
12
6
|
BrowserAppName["Brave"] = "Brave Browser";
|
|
13
|
-
})(BrowserAppName || (
|
|
14
|
-
class ChromiumBrowserHelper {
|
|
7
|
+
})(BrowserAppName || (BrowserAppName = {}));
|
|
8
|
+
export class ChromiumBrowserHelper {
|
|
15
9
|
appName;
|
|
16
10
|
constructor(appName) {
|
|
17
11
|
this.appName = appName;
|
|
@@ -20,17 +14,16 @@ class ChromiumBrowserHelper {
|
|
|
20
14
|
isFrontmost = () => getChromiumIsFrontmost(this.appName);
|
|
21
15
|
getActiveTab = () => getChromiumActiveTab(this.appName);
|
|
22
16
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
exports.Brave = new ChromiumBrowserHelper(BrowserAppName.Brave);
|
|
17
|
+
export const Chrome = new ChromiumBrowserHelper(BrowserAppName.Chrome);
|
|
18
|
+
export const Brave = new ChromiumBrowserHelper(BrowserAppName.Brave);
|
|
26
19
|
/* #region implements */
|
|
27
|
-
function isChromiumAppRunning(appName = BrowserAppName.Chrome) {
|
|
28
|
-
return
|
|
20
|
+
export function isChromiumAppRunning(appName = BrowserAppName.Chrome) {
|
|
21
|
+
return isAppRunning(appName);
|
|
29
22
|
}
|
|
30
|
-
async function getChromiumActiveTab(appName = BrowserAppName.Chrome) {
|
|
23
|
+
export async function getChromiumActiveTab(appName = BrowserAppName.Chrome) {
|
|
31
24
|
if (!(await isChromiumAppRunning(appName)))
|
|
32
25
|
return;
|
|
33
|
-
return await
|
|
26
|
+
return await run((appName) => {
|
|
34
27
|
const app = Application(appName);
|
|
35
28
|
app.includeStandardAdditions = true;
|
|
36
29
|
const win = (app.windows() || []).find((w) => !w.minimized());
|
|
@@ -41,10 +34,10 @@ async function getChromiumActiveTab(appName = BrowserAppName.Chrome) {
|
|
|
41
34
|
};
|
|
42
35
|
}, appName);
|
|
43
36
|
}
|
|
44
|
-
async function getChromiumIsFrontmost(appName = BrowserAppName.Chrome) {
|
|
37
|
+
export async function getChromiumIsFrontmost(appName = BrowserAppName.Chrome) {
|
|
45
38
|
if (!isChromiumAppRunning(appName))
|
|
46
39
|
return false;
|
|
47
|
-
return await
|
|
40
|
+
return await run((appName) => {
|
|
48
41
|
const app = Application(appName);
|
|
49
42
|
app.includeStandardAdditions = true;
|
|
50
43
|
return app.frontmost();
|
package/lib/dialog.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.dialog = void 0;
|
|
4
|
-
const run_1 = require("@jxa/run");
|
|
5
|
-
exports.dialog = {
|
|
1
|
+
import { run } from '@jxa/run';
|
|
2
|
+
export const dialog = {
|
|
6
3
|
prompt,
|
|
7
4
|
chooseFromList,
|
|
8
5
|
chooseMultipleFromList,
|
|
@@ -10,7 +7,7 @@ exports.dialog = {
|
|
|
10
7
|
async function prompt(label, defaultValue = '') {
|
|
11
8
|
// if cancel dialog, will throw error
|
|
12
9
|
try {
|
|
13
|
-
return ((await
|
|
10
|
+
return ((await run((label) => {
|
|
14
11
|
const app = Application.currentApplication();
|
|
15
12
|
app.includeStandardAdditions = true;
|
|
16
13
|
const response = app.displayDialog(label, { defaultAnswer: '' });
|
|
@@ -22,7 +19,7 @@ async function prompt(label, defaultValue = '') {
|
|
|
22
19
|
}
|
|
23
20
|
}
|
|
24
21
|
async function runOnCurrentApp(method, ...args) {
|
|
25
|
-
return await
|
|
22
|
+
return await run((method, ...args) => {
|
|
26
23
|
const app = Application.currentApplication();
|
|
27
24
|
app.includeStandardAdditions = true;
|
|
28
25
|
// @ts-ignore
|
package/lib/finder.js
CHANGED
|
@@ -1,23 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
exports.isRepresentQS = isRepresentQS;
|
|
9
|
-
exports.normalizeInputFileList = normalizeInputFileList;
|
|
10
|
-
const node_os_1 = require("node:os");
|
|
11
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
-
const node_url_1 = require("node:url");
|
|
13
|
-
const run_1 = require("@jxa/run");
|
|
14
|
-
const es_toolkit_1 = require("es-toolkit");
|
|
15
|
-
const promise_map_1 = __importDefault(require("promise.map"));
|
|
16
|
-
const app_1 = require("./app");
|
|
1
|
+
import { availableParallelism } from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { run as jxaRun } from '@jxa/run';
|
|
5
|
+
import { uniq } from 'es-toolkit';
|
|
6
|
+
import pmap from 'promise.map';
|
|
7
|
+
import { isAppRunning } from './app.js';
|
|
17
8
|
/**
|
|
18
9
|
* `App` named epxorts
|
|
19
10
|
*/
|
|
20
|
-
|
|
11
|
+
export const PathFinder = {
|
|
21
12
|
allSelected() {
|
|
22
13
|
return PathFinder_allSelected();
|
|
23
14
|
},
|
|
@@ -29,7 +20,7 @@ exports.PathFinder = {
|
|
|
29
20
|
},
|
|
30
21
|
getActiveTabDir: PathFinder_getActiveTabDir,
|
|
31
22
|
};
|
|
32
|
-
|
|
23
|
+
export const Finder = {
|
|
33
24
|
allSelected() {
|
|
34
25
|
return Finder_allSelected();
|
|
35
26
|
},
|
|
@@ -40,7 +31,7 @@ exports.Finder = {
|
|
|
40
31
|
return Finder_setSelected(filePaths);
|
|
41
32
|
},
|
|
42
33
|
};
|
|
43
|
-
|
|
34
|
+
export const QSpace = {
|
|
44
35
|
allSelected() {
|
|
45
36
|
return QSpace_allSelected();
|
|
46
37
|
},
|
|
@@ -53,27 +44,27 @@ exports.QSpace = {
|
|
|
53
44
|
*/
|
|
54
45
|
/* #region PathFinder */
|
|
55
46
|
async function PathFinder_allSelected() {
|
|
56
|
-
if (!(await
|
|
47
|
+
if (!(await isAppRunning('Path Finder'))) {
|
|
57
48
|
return [];
|
|
58
49
|
}
|
|
59
|
-
const filePaths = await (
|
|
50
|
+
const filePaths = await jxaRun(() => {
|
|
60
51
|
const app = Application('Path Finder');
|
|
61
52
|
return (app.selection() || []).map((x) => x.posixPath());
|
|
62
53
|
});
|
|
63
54
|
return filePaths;
|
|
64
55
|
}
|
|
65
56
|
async function PathFinder_singleSelected() {
|
|
66
|
-
if (!(await
|
|
57
|
+
if (!(await isAppRunning('Path Finder'))) {
|
|
67
58
|
return;
|
|
68
59
|
}
|
|
69
|
-
const firstFilePath = await (
|
|
60
|
+
const firstFilePath = await jxaRun(() => {
|
|
70
61
|
const app = Application('Path Finder');
|
|
71
62
|
return (app.selection() || [])[0]?.posixPath();
|
|
72
63
|
});
|
|
73
64
|
return firstFilePath;
|
|
74
65
|
}
|
|
75
66
|
async function PathFinder_setSelected(filePaths) {
|
|
76
|
-
await (
|
|
67
|
+
await jxaRun((filePaths) => {
|
|
77
68
|
const app = Application('Path Finder');
|
|
78
69
|
app.select(filePaths);
|
|
79
70
|
}, filePaths);
|
|
@@ -83,9 +74,9 @@ async function PathFinder_setSelected(filePaths) {
|
|
|
83
74
|
* 要求只能有一个 visible window, 从 jxa 这一层无法区分 window z-index
|
|
84
75
|
*/
|
|
85
76
|
async function PathFinder_getActiveTabDir() {
|
|
86
|
-
if (!(await
|
|
77
|
+
if (!(await isAppRunning('Path Finder')))
|
|
87
78
|
return;
|
|
88
|
-
const visibleWindowCount = await (
|
|
79
|
+
const visibleWindowCount = await jxaRun(() => {
|
|
89
80
|
const app = Application('Path Finder');
|
|
90
81
|
return app.finderWindows.whose({ visible: true }).length;
|
|
91
82
|
});
|
|
@@ -94,7 +85,7 @@ async function PathFinder_getActiveTabDir() {
|
|
|
94
85
|
if (visibleWindowCount > 1) {
|
|
95
86
|
throw new Error('Multiple visible PathFinder window not supported in PathFinder_getActiveTabDir');
|
|
96
87
|
}
|
|
97
|
-
const dir = await (
|
|
88
|
+
const dir = await jxaRun(() => {
|
|
98
89
|
const app = Application('Path Finder');
|
|
99
90
|
const w = app.finderWindows.whose({ visible: true })[0];
|
|
100
91
|
return w.target.posixPath();
|
|
@@ -104,51 +95,51 @@ async function PathFinder_getActiveTabDir() {
|
|
|
104
95
|
/* #endregion */
|
|
105
96
|
/* #region Finder */
|
|
106
97
|
async function Finder_allSelected() {
|
|
107
|
-
const urls = await (
|
|
98
|
+
const urls = await jxaRun(() => {
|
|
108
99
|
const app = Application('Finder');
|
|
109
100
|
const selection = app.selection();
|
|
110
101
|
return (selection || []).map((x) => x.url());
|
|
111
102
|
});
|
|
112
|
-
return urls.map((u) =>
|
|
103
|
+
return urls.map((u) => fileURLToPath(u));
|
|
113
104
|
}
|
|
114
105
|
async function Finder_setSelected(filePaths) {
|
|
115
|
-
await (
|
|
106
|
+
await jxaRun((filePaths) => {
|
|
116
107
|
const app = Application('Finder');
|
|
117
108
|
app.select(filePaths.map((f) => Path(f)));
|
|
118
109
|
app.activate();
|
|
119
110
|
}, filePaths);
|
|
120
111
|
}
|
|
121
112
|
async function QSpace_allSelected() {
|
|
122
|
-
if (!(await
|
|
113
|
+
if (!(await isAppRunning('QSpace Pro'))) {
|
|
123
114
|
return [];
|
|
124
115
|
}
|
|
125
|
-
const urls = await (
|
|
116
|
+
const urls = await jxaRun(() => {
|
|
126
117
|
const app = Application('QSpace Pro');
|
|
127
118
|
const selection = (app.selectedItems() || []).map((x) => x.urlstr());
|
|
128
119
|
return selection;
|
|
129
120
|
});
|
|
130
|
-
return urls.map((u) =>
|
|
121
|
+
return urls.map((u) => fileURLToPath(u));
|
|
131
122
|
}
|
|
132
123
|
/* #endregion */
|
|
133
124
|
/* #region Top Level Helper */
|
|
134
|
-
function isRepresentPF(file) {
|
|
125
|
+
export function isRepresentPF(file) {
|
|
135
126
|
return /^[@$]?pf$/i.test(file);
|
|
136
127
|
}
|
|
137
|
-
function isRepresentQS(file) {
|
|
128
|
+
export function isRepresentQS(file) {
|
|
138
129
|
return /^[@$]?qs$/i.test(file);
|
|
139
130
|
}
|
|
140
131
|
/**
|
|
141
132
|
* 处理 `@pf` `@qs`, ignore case
|
|
142
133
|
*/
|
|
143
|
-
async function normalizeInputFileList(fileList) {
|
|
144
|
-
return
|
|
134
|
+
export async function normalizeInputFileList(fileList) {
|
|
135
|
+
return uniq((await pmap(fileList, async (f) => {
|
|
145
136
|
if (process.platform === 'darwin') {
|
|
146
137
|
if (isRepresentPF(f))
|
|
147
|
-
return await
|
|
138
|
+
return await PathFinder.allSelected();
|
|
148
139
|
if (isRepresentQS(f))
|
|
149
|
-
return await
|
|
140
|
+
return await QSpace.allSelected();
|
|
150
141
|
}
|
|
151
|
-
return
|
|
152
|
-
},
|
|
142
|
+
return path.resolve(f);
|
|
143
|
+
}, availableParallelism())).flat());
|
|
153
144
|
}
|
|
154
145
|
/* #endregion */
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export * from './app';
|
|
2
|
-
export * from './chrome';
|
|
3
|
-
export * from './dialog';
|
|
4
|
-
export * from './finder';
|
|
5
|
-
export * from './iterm';
|
|
1
|
+
export * from './app.js';
|
|
2
|
+
export * from './chrome.js';
|
|
3
|
+
export * from './dialog.js';
|
|
4
|
+
export * from './finder.js';
|
|
5
|
+
export * from './iterm.js';
|
package/lib/index.js
CHANGED
|
@@ -1,23 +1,7 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/// <reference types='@jxa/global-type' />
|
|
3
2
|
/// <reference types='@jxa/types' />
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
Object.defineProperty(o, k2, desc);
|
|
11
|
-
}) : (function(o, m, k, k2) {
|
|
12
|
-
if (k2 === undefined) k2 = k;
|
|
13
|
-
o[k2] = m[k];
|
|
14
|
-
}));
|
|
15
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
16
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
17
|
-
};
|
|
18
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
__exportStar(require("./app"), exports);
|
|
20
|
-
__exportStar(require("./chrome"), exports);
|
|
21
|
-
__exportStar(require("./dialog"), exports);
|
|
22
|
-
__exportStar(require("./finder"), exports);
|
|
23
|
-
__exportStar(require("./iterm"), exports);
|
|
3
|
+
export * from './app.js';
|
|
4
|
+
export * from './chrome.js';
|
|
5
|
+
export * from './dialog.js';
|
|
6
|
+
export * from './finder.js';
|
|
7
|
+
export * from './iterm.js';
|
package/lib/iterm.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
exports.runInIterm = runInIterm;
|
|
5
|
-
const run_1 = require("@jxa/run");
|
|
6
|
-
async function runInIterm(command, reuse = true) {
|
|
7
|
-
const result = await (0, run_1.run)((command, reuse) => {
|
|
1
|
+
import { run } from '@jxa/run';
|
|
2
|
+
export async function runInIterm(command, reuse = true) {
|
|
3
|
+
const result = await run((command, reuse) => {
|
|
8
4
|
const app = Application('iTerm');
|
|
9
5
|
app.includeStandardAdditions = true;
|
|
10
6
|
app.activate();
|
|
@@ -30,7 +26,7 @@ async function runInIterm(command, reuse = true) {
|
|
|
30
26
|
console.error('No iTerm session found');
|
|
31
27
|
}
|
|
32
28
|
}
|
|
33
|
-
|
|
29
|
+
export const iTerm = {
|
|
34
30
|
run: runInIterm,
|
|
35
31
|
runInIterm,
|
|
36
32
|
};
|
package/lib/system-events.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const run_1 = require("@jxa/run");
|
|
1
|
+
import { run } from '@jxa/run';
|
|
4
2
|
/**
|
|
5
3
|
* 不太准确, 比如 vscode 进程 `Code` 在运行, 检测不到
|
|
6
4
|
*/
|
|
7
5
|
async function isAppRunning(appName) {
|
|
8
|
-
return await
|
|
6
|
+
return await run((appName) => {
|
|
9
7
|
const app = Application('System Events');
|
|
10
8
|
app.includeStandardAdditions = true;
|
|
11
9
|
const runningAppNames = app.processes().map((p) => p.name());
|
|
@@ -13,7 +11,7 @@ async function isAppRunning(appName) {
|
|
|
13
11
|
}, appName);
|
|
14
12
|
}
|
|
15
13
|
async function getRunningAppNames() {
|
|
16
|
-
return await
|
|
14
|
+
return await run(() => {
|
|
17
15
|
const app = Application('System Events');
|
|
18
16
|
app.includeStandardAdditions = true;
|
|
19
17
|
const runningAppNames = app.processes().map((p) => p.name());
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mac-helper",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.9.0",
|
|
5
|
+
"packageManager": "pnpm@11.5.0+sha512.dbfcc4f81cf48597afd4bc391ffdf12c11f1a9fb83a395bfa6b0a2d9cc2fd8ffebafdb1ccbd529632153f793904c2615b7f09fe1a345473fd1c35845172a8eb1",
|
|
5
6
|
"description": "useful helper for macOS",
|
|
6
7
|
"author": "magicdawn",
|
|
7
8
|
"license": "MIT",
|
|
@@ -18,11 +19,14 @@
|
|
|
18
19
|
"helper",
|
|
19
20
|
"mac-helper"
|
|
20
21
|
],
|
|
22
|
+
"exports": {
|
|
23
|
+
"types": "./lib/index.d.ts",
|
|
24
|
+
"default": "./lib/index.js"
|
|
25
|
+
},
|
|
21
26
|
"main": "lib/index.js",
|
|
22
27
|
"types": "lib/index.d.ts",
|
|
23
28
|
"files": [
|
|
24
|
-
"lib/"
|
|
25
|
-
"src/"
|
|
29
|
+
"lib/"
|
|
26
30
|
],
|
|
27
31
|
"publishConfig": {
|
|
28
32
|
"registry": "https://registry.npmjs.org"
|
|
@@ -32,27 +36,28 @@
|
|
|
32
36
|
"test": "vitest run --globals --testTimeout=30000",
|
|
33
37
|
"test:dev": "vitest --globals --testTimeout=30000",
|
|
34
38
|
"test-cover": "vitest run --globals --coverage --testTimeout=30000",
|
|
35
|
-
"dev": "
|
|
36
|
-
"typecheck": "
|
|
37
|
-
"build": "rm -rf lib;
|
|
39
|
+
"dev": "tsgo -w --incremental",
|
|
40
|
+
"typecheck": "tsgo --noEmit",
|
|
41
|
+
"build": "rm -rf lib; tsgo",
|
|
38
42
|
"prepublishOnly": "pnpm test && pnpm build"
|
|
39
43
|
},
|
|
40
44
|
"dependencies": {
|
|
41
45
|
"@jxa/run": "^1.4.0",
|
|
42
|
-
"es-toolkit": "^1.
|
|
43
|
-
"promise.map": "^1.
|
|
46
|
+
"es-toolkit": "^1.47.0",
|
|
47
|
+
"promise.map": "^1.2.0"
|
|
44
48
|
},
|
|
45
49
|
"devDependencies": {
|
|
46
50
|
"@jxa/global-type": "^1.4.0",
|
|
47
51
|
"@jxa/sdef-to-dts": "^1.4.0",
|
|
48
52
|
"@jxa/types": "^1.4.0",
|
|
49
|
-
"@magicdawn/eslint-config": "
|
|
53
|
+
"@magicdawn/eslint-config": "github:magicdawn/eslint-config#dist",
|
|
50
54
|
"@magicdawn/prettier-config": "^0.1.0",
|
|
51
55
|
"@types/fs-extra": "^11.0.4",
|
|
52
|
-
"@types/node": "^25.
|
|
53
|
-
"@
|
|
56
|
+
"@types/node": "^25.9.1",
|
|
57
|
+
"@typescript/native-preview": "7.0.0-dev.20260527.2",
|
|
58
|
+
"@vitest/coverage-v8": "^4.1.7",
|
|
54
59
|
"delay": "^7.0.0",
|
|
55
|
-
"eslint": "^10.4.
|
|
60
|
+
"eslint": "^10.4.1",
|
|
56
61
|
"eslint-config-prettier": "^10.1.8",
|
|
57
62
|
"fs-extra": "^11.3.5",
|
|
58
63
|
"husky": "^9.1.7",
|
|
@@ -61,7 +66,7 @@
|
|
|
61
66
|
"prettier": "^3.8.3",
|
|
62
67
|
"should": "^13.2.3",
|
|
63
68
|
"typescript": "^6.0.3",
|
|
64
|
-
"vitest": "^4.1.
|
|
69
|
+
"vitest": "^4.1.7"
|
|
65
70
|
},
|
|
66
71
|
"lint-staged": {
|
|
67
72
|
"*.{js,jsx,ts,tsx,less,md}": [
|
package/src/app.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { exec } from 'node:child_process'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* file path based process running detect.
|
|
5
|
-
* for vscode, process name = `Code`, file is `Visual Studio Code.app`, the `Visual Studio Code` should be passed
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export function isAppRunning(appName: string): Promise<boolean> {
|
|
9
|
-
return new Promise((resolve, reject) => {
|
|
10
|
-
exec(`ps -ax -o command`, (err, stdout, stderr) => {
|
|
11
|
-
if (err) return reject(err)
|
|
12
|
-
|
|
13
|
-
const lines = stdout
|
|
14
|
-
.trim()
|
|
15
|
-
.split('\n')
|
|
16
|
-
.map((x) => x.trim())
|
|
17
|
-
.filter(Boolean)
|
|
18
|
-
const searchText = `${appName}.app/Contents/MacOS/`
|
|
19
|
-
|
|
20
|
-
const running = !!lines.find((line) => line.includes(searchText))
|
|
21
|
-
resolve(running)
|
|
22
|
-
})
|
|
23
|
-
})
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// isAppRunning('Visual Studio Code').then(console.log)
|
package/src/chrome.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { run } from '@jxa/run'
|
|
2
|
-
import { isAppRunning } from './app'
|
|
3
|
-
import type { GoogleChrome } from 'jxa-common-used'
|
|
4
|
-
|
|
5
|
-
export type ActiveTab = { url: string; title: string }
|
|
6
|
-
|
|
7
|
-
export enum BrowserAppName {
|
|
8
|
-
Chrome = 'Google Chrome',
|
|
9
|
-
Brave = 'Brave Browser',
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export class ChromiumBrowserHelper {
|
|
13
|
-
constructor(public appName: string) {}
|
|
14
|
-
isRunning = (): Promise<boolean> => isChromiumAppRunning(this.appName)
|
|
15
|
-
isFrontmost = (): Promise<boolean> => getChromiumIsFrontmost(this.appName)
|
|
16
|
-
getActiveTab = (): Promise<ActiveTab | undefined> => getChromiumActiveTab(this.appName)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const Chrome = new ChromiumBrowserHelper(BrowserAppName.Chrome)
|
|
20
|
-
export const Brave = new ChromiumBrowserHelper(BrowserAppName.Brave)
|
|
21
|
-
|
|
22
|
-
/* #region implements */
|
|
23
|
-
export function isChromiumAppRunning(appName: string = BrowserAppName.Chrome): Promise<boolean> {
|
|
24
|
-
return isAppRunning(appName)
|
|
25
|
-
}
|
|
26
|
-
export async function getChromiumActiveTab(appName: string = BrowserAppName.Chrome): Promise<ActiveTab | undefined> {
|
|
27
|
-
if (!(await isChromiumAppRunning(appName))) return
|
|
28
|
-
return await run((appName: string) => {
|
|
29
|
-
const app = Application<GoogleChrome>(appName)
|
|
30
|
-
app.includeStandardAdditions = true
|
|
31
|
-
const win = (app.windows() || []).find((w) => !w.minimized())
|
|
32
|
-
const tab = win?.activeTab()
|
|
33
|
-
return {
|
|
34
|
-
url: tab?.url(),
|
|
35
|
-
title: tab?.title(),
|
|
36
|
-
}
|
|
37
|
-
}, appName)
|
|
38
|
-
}
|
|
39
|
-
export async function getChromiumIsFrontmost(appName: string = BrowserAppName.Chrome): Promise<boolean> {
|
|
40
|
-
if (!isChromiumAppRunning(appName)) return false
|
|
41
|
-
return await run((appName: string) => {
|
|
42
|
-
const app = Application<GoogleChrome>(appName)
|
|
43
|
-
app.includeStandardAdditions = true
|
|
44
|
-
return app.frontmost()
|
|
45
|
-
}, appName)
|
|
46
|
-
}
|
|
47
|
-
/* #endregion */
|
package/src/dialog.ts
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { run } from '@jxa/run'
|
|
2
|
-
import type { StandardAdditions } from '@jxa/types'
|
|
3
|
-
|
|
4
|
-
export const dialog = {
|
|
5
|
-
prompt,
|
|
6
|
-
chooseFromList,
|
|
7
|
-
chooseMultipleFromList,
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
async function prompt(label: string, defaultValue = ''): Promise<string> {
|
|
11
|
-
// if cancel dialog, will throw error
|
|
12
|
-
try {
|
|
13
|
-
return (
|
|
14
|
-
(await run((label) => {
|
|
15
|
-
const app = Application.currentApplication()
|
|
16
|
-
app.includeStandardAdditions = true
|
|
17
|
-
const response = app.displayDialog(label, { defaultAnswer: '' })
|
|
18
|
-
return response.textReturned
|
|
19
|
-
}, label)) || defaultValue
|
|
20
|
-
)
|
|
21
|
-
} catch {
|
|
22
|
-
return defaultValue
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
type AppMethod = 'chooseFromList'
|
|
27
|
-
|
|
28
|
-
async function runOnCurrentApp(method: AppMethod, ...args: any[]) {
|
|
29
|
-
return await run(
|
|
30
|
-
(method: AppMethod, ...args: any[]) => {
|
|
31
|
-
const app = Application.currentApplication()
|
|
32
|
-
app.includeStandardAdditions = true
|
|
33
|
-
// @ts-ignore
|
|
34
|
-
return app[method]?.(...args)
|
|
35
|
-
},
|
|
36
|
-
method,
|
|
37
|
-
...args,
|
|
38
|
-
)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
type ChooseOption = { label: string; value: string }
|
|
42
|
-
|
|
43
|
-
async function chooseFromList(
|
|
44
|
-
list: string[] | ChooseOption[],
|
|
45
|
-
options: StandardAdditions.StandardAdditions.ChooseFromListOptionalParameter = {},
|
|
46
|
-
): Promise<string | undefined> {
|
|
47
|
-
let usingOption = false
|
|
48
|
-
let showList: string[] = []
|
|
49
|
-
if (typeof list[0] === 'object') {
|
|
50
|
-
usingOption = true
|
|
51
|
-
showList = (list as ChooseOption[]).map((l) => l.label)
|
|
52
|
-
} else {
|
|
53
|
-
showList = list as string[]
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const selected = await runOnCurrentApp('chooseFromList', showList, {
|
|
57
|
-
multipleSelectionsAllowed: false,
|
|
58
|
-
...options,
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
if (!selected) return
|
|
62
|
-
const label = (selected as any)[0]
|
|
63
|
-
|
|
64
|
-
if (!usingOption) {
|
|
65
|
-
return label
|
|
66
|
-
} else {
|
|
67
|
-
const val = (list as ChooseOption[]).find((item) => item.label === label)?.value
|
|
68
|
-
return val
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async function chooseMultipleFromList(
|
|
73
|
-
list: string[] | ChooseOption[],
|
|
74
|
-
options: StandardAdditions.StandardAdditions.ChooseFromListOptionalParameter = {},
|
|
75
|
-
): Promise<string[]> {
|
|
76
|
-
let usingOption = false
|
|
77
|
-
let showList: string[] = []
|
|
78
|
-
if (typeof list[0] === 'object') {
|
|
79
|
-
usingOption = true
|
|
80
|
-
showList = (list as ChooseOption[]).map((l) => l.label)
|
|
81
|
-
} else {
|
|
82
|
-
showList = list as string[]
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const selected = await runOnCurrentApp('chooseFromList', showList, {
|
|
86
|
-
multipleSelectionsAllowed: true,
|
|
87
|
-
...options,
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
if (!selected) return []
|
|
91
|
-
|
|
92
|
-
if (!usingOption) {
|
|
93
|
-
return selected as string[]
|
|
94
|
-
} else {
|
|
95
|
-
const valArr = (selected as string[]).map(
|
|
96
|
-
(label) => (list as ChooseOption[]).find((item) => item.label === label)!.value,
|
|
97
|
-
)
|
|
98
|
-
return valArr
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// dialog.chooseFromList(['hello', 'world']).then((selected) => {
|
|
103
|
-
// console.log(selected)
|
|
104
|
-
// })
|
|
105
|
-
|
|
106
|
-
// dialog
|
|
107
|
-
// .chooseFromList([
|
|
108
|
-
// { value: 'yes', label: '是吧~~~' },
|
|
109
|
-
// { value: 'halo', label: 'Halo 啊洒洒' },
|
|
110
|
-
// ])
|
|
111
|
-
// .then((selected) => {
|
|
112
|
-
// console.log(selected)
|
|
113
|
-
// })
|
|
114
|
-
|
|
115
|
-
// dialog
|
|
116
|
-
// .chooseMultipleFromList(
|
|
117
|
-
// [
|
|
118
|
-
// { value: 'yes', label: '是吧~~~' },
|
|
119
|
-
// { value: 'halo', label: 'Halo 啊洒洒' },
|
|
120
|
-
// ],
|
|
121
|
-
// {
|
|
122
|
-
// withTitle: 'title',
|
|
123
|
-
// withPrompt: 'prompt',
|
|
124
|
-
// cancelButtonName: '取消!',
|
|
125
|
-
// // @ts-ignore
|
|
126
|
-
// okButtonName: '酱紫吧~',
|
|
127
|
-
// }
|
|
128
|
-
// )
|
|
129
|
-
// .then((selected) => {
|
|
130
|
-
// console.log(selected)
|
|
131
|
-
// })
|
|
132
|
-
// dialog.prompt('hello', 'world').then(console.log)
|
package/src/finder.ts
DELETED
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
import { availableParallelism } from 'node:os'
|
|
2
|
-
import path from 'node:path'
|
|
3
|
-
import { fileURLToPath } from 'node:url'
|
|
4
|
-
import { run as jxaRun } from '@jxa/run'
|
|
5
|
-
import { uniq } from 'es-toolkit'
|
|
6
|
-
import pmap from 'promise.map'
|
|
7
|
-
import { isAppRunning } from './app'
|
|
8
|
-
import type { PathFinder as PathFinderType } from 'jxa-common-used'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* `App` named epxorts
|
|
12
|
-
*/
|
|
13
|
-
export const PathFinder = {
|
|
14
|
-
allSelected() {
|
|
15
|
-
return PathFinder_allSelected()
|
|
16
|
-
},
|
|
17
|
-
singleSelected() {
|
|
18
|
-
return PathFinder_singleSelected()
|
|
19
|
-
},
|
|
20
|
-
setSelected(filePaths: string[]) {
|
|
21
|
-
return PathFinder_setSelected(filePaths)
|
|
22
|
-
},
|
|
23
|
-
getActiveTabDir: PathFinder_getActiveTabDir,
|
|
24
|
-
}
|
|
25
|
-
export const Finder = {
|
|
26
|
-
allSelected() {
|
|
27
|
-
return Finder_allSelected()
|
|
28
|
-
},
|
|
29
|
-
async singleSelected(): Promise<string | undefined> {
|
|
30
|
-
return (await Finder_allSelected())[0]
|
|
31
|
-
},
|
|
32
|
-
setSelected(filePaths: string[]) {
|
|
33
|
-
return Finder_setSelected(filePaths)
|
|
34
|
-
},
|
|
35
|
-
}
|
|
36
|
-
export const QSpace = {
|
|
37
|
-
allSelected() {
|
|
38
|
-
return QSpace_allSelected()
|
|
39
|
-
},
|
|
40
|
-
async singleSelected(): Promise<string | undefined> {
|
|
41
|
-
return (await QSpace_allSelected())[0]
|
|
42
|
-
},
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* impls
|
|
47
|
-
*/
|
|
48
|
-
/* #region PathFinder */
|
|
49
|
-
async function PathFinder_allSelected() {
|
|
50
|
-
if (!(await isAppRunning('Path Finder'))) {
|
|
51
|
-
return []
|
|
52
|
-
}
|
|
53
|
-
const filePaths: string[] = await jxaRun(() => {
|
|
54
|
-
const app = Application<PathFinderType>('Path Finder')
|
|
55
|
-
return (app.selection() || []).map((x) => x.posixPath())
|
|
56
|
-
})
|
|
57
|
-
return filePaths
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async function PathFinder_singleSelected(): Promise<string | undefined> {
|
|
61
|
-
if (!(await isAppRunning('Path Finder'))) {
|
|
62
|
-
return
|
|
63
|
-
}
|
|
64
|
-
const firstFilePath: string | undefined = await jxaRun(() => {
|
|
65
|
-
const app = Application<PathFinderType>('Path Finder')
|
|
66
|
-
return (app.selection() || [])[0]?.posixPath()
|
|
67
|
-
})
|
|
68
|
-
return firstFilePath
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async function PathFinder_setSelected(filePaths: string[]) {
|
|
72
|
-
await jxaRun((filePaths: string[]) => {
|
|
73
|
-
const app = Application<PathFinderType>('Path Finder')
|
|
74
|
-
app.select(filePaths)
|
|
75
|
-
}, filePaths)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* 获取 activeTab dir, 当没有 selection 时可用,
|
|
80
|
-
* 要求只能有一个 visible window, 从 jxa 这一层无法区分 window z-index
|
|
81
|
-
*/
|
|
82
|
-
async function PathFinder_getActiveTabDir(): Promise<string | undefined> {
|
|
83
|
-
if (!(await isAppRunning('Path Finder'))) return
|
|
84
|
-
|
|
85
|
-
const visibleWindowCount: number = await jxaRun(() => {
|
|
86
|
-
const app = Application<PathFinderType>('Path Finder')
|
|
87
|
-
return app.finderWindows.whose({ visible: true }).length
|
|
88
|
-
})
|
|
89
|
-
if (!visibleWindowCount) return
|
|
90
|
-
if (visibleWindowCount > 1) {
|
|
91
|
-
throw new Error('Multiple visible PathFinder window not supported in PathFinder_getActiveTabDir')
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const dir: string = await jxaRun(() => {
|
|
95
|
-
const app = Application<PathFinderType>('Path Finder')
|
|
96
|
-
const w = app.finderWindows.whose({ visible: true })[0]
|
|
97
|
-
return w.target.posixPath()
|
|
98
|
-
})
|
|
99
|
-
return dir
|
|
100
|
-
}
|
|
101
|
-
/* #endregion */
|
|
102
|
-
|
|
103
|
-
/* #region Finder */
|
|
104
|
-
async function Finder_allSelected() {
|
|
105
|
-
const urls: string[] = await jxaRun(() => {
|
|
106
|
-
const app = Application('Finder')
|
|
107
|
-
const selection = app.selection()
|
|
108
|
-
return (selection || []).map((x) => x.url())
|
|
109
|
-
})
|
|
110
|
-
return urls.map((u) => fileURLToPath(u))
|
|
111
|
-
}
|
|
112
|
-
async function Finder_setSelected(filePaths: string[]) {
|
|
113
|
-
await jxaRun((filePaths: string[]) => {
|
|
114
|
-
const app = Application('Finder')
|
|
115
|
-
app.select(filePaths.map((f) => Path(f)))
|
|
116
|
-
app.activate()
|
|
117
|
-
}, filePaths)
|
|
118
|
-
}
|
|
119
|
-
/* #endregion */
|
|
120
|
-
|
|
121
|
-
/* #region QSpace */
|
|
122
|
-
// file item n [inh. item] : A file item.
|
|
123
|
-
// properties
|
|
124
|
-
// - id (text, r/o) : The unique identifier of the item.
|
|
125
|
-
// - name (text, r/o) : The name of the file.
|
|
126
|
-
// - urlstr (text, r/o) : The absolute string of URL of the file.
|
|
127
|
-
export type QSpaceFileItem = {
|
|
128
|
-
// the unique identifier of the item
|
|
129
|
-
id: () => string
|
|
130
|
-
// the name of the file
|
|
131
|
-
name: () => string
|
|
132
|
-
// the absolute string of URL of the file
|
|
133
|
-
urlstr: () => string
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
async function QSpace_allSelected() {
|
|
137
|
-
if (!(await isAppRunning('QSpace Pro'))) {
|
|
138
|
-
return []
|
|
139
|
-
}
|
|
140
|
-
const urls: string[] = await jxaRun(() => {
|
|
141
|
-
const app = Application('QSpace Pro')
|
|
142
|
-
const selection = (app.selectedItems() || []).map((x: QSpaceFileItem) => x.urlstr())
|
|
143
|
-
return selection
|
|
144
|
-
})
|
|
145
|
-
return urls.map((u) => fileURLToPath(u))
|
|
146
|
-
}
|
|
147
|
-
/* #endregion */
|
|
148
|
-
|
|
149
|
-
/* #region Top Level Helper */
|
|
150
|
-
export function isRepresentPF(file: string) {
|
|
151
|
-
return /^[@$]?pf$/i.test(file)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export function isRepresentQS(file: string) {
|
|
155
|
-
return /^[@$]?qs$/i.test(file)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* 处理 `@pf` `@qs`, ignore case
|
|
160
|
-
*/
|
|
161
|
-
export async function normalizeInputFileList(fileList: string[]) {
|
|
162
|
-
return uniq(
|
|
163
|
-
(
|
|
164
|
-
await pmap(
|
|
165
|
-
fileList,
|
|
166
|
-
async (f) => {
|
|
167
|
-
if (process.platform === 'darwin') {
|
|
168
|
-
if (isRepresentPF(f)) return await PathFinder.allSelected()
|
|
169
|
-
if (isRepresentQS(f)) return await QSpace.allSelected()
|
|
170
|
-
}
|
|
171
|
-
return path.resolve(f)
|
|
172
|
-
},
|
|
173
|
-
availableParallelism(),
|
|
174
|
-
)
|
|
175
|
-
).flat(),
|
|
176
|
-
)
|
|
177
|
-
}
|
|
178
|
-
/* #endregion */
|
package/src/index.ts
DELETED
package/src/iterm.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { run } from '@jxa/run'
|
|
2
|
-
import type { iTerm2 } from 'jxa-common-used'
|
|
3
|
-
|
|
4
|
-
export async function runInIterm(command: string, reuse = true) {
|
|
5
|
-
type RunResult = 'no-session' | undefined
|
|
6
|
-
const result: RunResult = await run(
|
|
7
|
-
(command, reuse): RunResult => {
|
|
8
|
-
const app = Application<iTerm2>('iTerm')
|
|
9
|
-
app.includeStandardAdditions = true
|
|
10
|
-
app.activate()
|
|
11
|
-
|
|
12
|
-
let usingSession: iTerm2.Session | undefined
|
|
13
|
-
|
|
14
|
-
// reuse when possible
|
|
15
|
-
if (reuse) {
|
|
16
|
-
const session: iTerm2.Session | undefined = app.currentWindow()?.currentSession()
|
|
17
|
-
const isIdle = session?.isAtShellPrompt()
|
|
18
|
-
if (isIdle) {
|
|
19
|
-
usingSession = session
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// create new tab when necessary
|
|
24
|
-
if (!usingSession) {
|
|
25
|
-
const tab = app.createTabWithDefaultProfile(app.currentWindow)
|
|
26
|
-
usingSession = tab?.currentSession() // TODO: iterm2 updated, this code no longer works
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (!usingSession) return 'no-session'
|
|
30
|
-
app.write(usingSession, { text: command })
|
|
31
|
-
},
|
|
32
|
-
command,
|
|
33
|
-
reuse,
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
if (result === 'no-session') {
|
|
37
|
-
console.error('No iTerm session found')
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export const iTerm = {
|
|
42
|
-
run: runInIterm,
|
|
43
|
-
runInIterm,
|
|
44
|
-
}
|
package/src/system-events.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { run } from '@jxa/run'
|
|
2
|
-
import type { SystemEvents } from '@jxa/types'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* 不太准确, 比如 vscode 进程 `Code` 在运行, 检测不到
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
async function isAppRunning(appName: string): Promise<boolean> {
|
|
9
|
-
return await run((appName) => {
|
|
10
|
-
const app = Application<SystemEvents.SystemEvents.Application>('System Events')
|
|
11
|
-
app.includeStandardAdditions = true
|
|
12
|
-
const runningAppNames: string[] = app.processes().map((p: SystemEvents.SystemEvents.Process) => p.name())
|
|
13
|
-
return runningAppNames.includes(appName)
|
|
14
|
-
}, appName)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
async function getRunningAppNames(): Promise<string[]> {
|
|
18
|
-
return await run(() => {
|
|
19
|
-
const app = Application<SystemEvents.SystemEvents.Application>('System Events')
|
|
20
|
-
app.includeStandardAdditions = true
|
|
21
|
-
const runningAppNames: string[] = app.processes().map((p: SystemEvents.SystemEvents.Process) => p.name())
|
|
22
|
-
return runningAppNames
|
|
23
|
-
})
|
|
24
|
-
}
|