mac-helper 0.8.1 → 0.10.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 CHANGED
@@ -1,14 +1,11 @@
1
- "use strict";
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
- (0, node_child_process_1.exec)(`ps -ax -o command`, (err, stdout, stderr) => {
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.d.ts CHANGED
@@ -10,11 +10,11 @@ export declare class ChromiumBrowserHelper {
10
10
  appName: string;
11
11
  constructor(appName: string);
12
12
  isRunning: () => Promise<boolean>;
13
- isFrontmost: () => Promise<boolean>;
14
- getActiveTab: () => Promise<ActiveTab | undefined>;
13
+ isFrontmost: (signal?: AbortSignal) => Promise<boolean>;
14
+ getActiveTab: (signal?: AbortSignal) => Promise<ActiveTab | undefined>;
15
15
  }
16
16
  export declare const Chrome: ChromiumBrowserHelper;
17
17
  export declare const Brave: ChromiumBrowserHelper;
18
18
  export declare function isChromiumAppRunning(appName?: string): Promise<boolean>;
19
- export declare function getChromiumActiveTab(appName?: string): Promise<ActiveTab | undefined>;
20
- export declare function getChromiumIsFrontmost(appName?: string): Promise<boolean>;
19
+ export declare function getChromiumActiveTab(appName?: string, signal?: AbortSignal): Promise<ActiveTab | undefined>;
20
+ export declare function getChromiumIsFrontmost(appName?: string, signal?: AbortSignal): Promise<boolean>;
package/lib/chrome.js CHANGED
@@ -1,36 +1,29 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Brave = exports.Chrome = exports.ChromiumBrowserHelper = exports.BrowserAppName = void 0;
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 { runJxa } from 'run-jxa';
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 || (exports.BrowserAppName = BrowserAppName = {}));
14
- class ChromiumBrowserHelper {
7
+ })(BrowserAppName || (BrowserAppName = {}));
8
+ export class ChromiumBrowserHelper {
15
9
  appName;
16
10
  constructor(appName) {
17
11
  this.appName = appName;
18
12
  }
19
13
  isRunning = () => isChromiumAppRunning(this.appName);
20
- isFrontmost = () => getChromiumIsFrontmost(this.appName);
21
- getActiveTab = () => getChromiumActiveTab(this.appName);
14
+ isFrontmost = (signal) => getChromiumIsFrontmost(this.appName, signal);
15
+ getActiveTab = (signal) => getChromiumActiveTab(this.appName, signal);
22
16
  }
23
- exports.ChromiumBrowserHelper = ChromiumBrowserHelper;
24
- exports.Chrome = new ChromiumBrowserHelper(BrowserAppName.Chrome);
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 (0, app_1.isAppRunning)(appName);
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, signal) {
31
24
  if (!(await isChromiumAppRunning(appName)))
32
25
  return;
33
- return await (0, run_1.run)((appName) => {
26
+ return await runJxa((appName) => {
34
27
  const app = Application(appName);
35
28
  app.includeStandardAdditions = true;
36
29
  const win = (app.windows() || []).find((w) => !w.minimized());
@@ -39,15 +32,15 @@ async function getChromiumActiveTab(appName = BrowserAppName.Chrome) {
39
32
  url: tab?.url(),
40
33
  title: tab?.title(),
41
34
  };
42
- }, appName);
35
+ }, [appName], { signal });
43
36
  }
44
- async function getChromiumIsFrontmost(appName = BrowserAppName.Chrome) {
37
+ export async function getChromiumIsFrontmost(appName = BrowserAppName.Chrome, signal) {
45
38
  if (!isChromiumAppRunning(appName))
46
39
  return false;
47
- return await (0, run_1.run)((appName) => {
40
+ return await runJxa((appName) => {
48
41
  const app = Application(appName);
49
42
  app.includeStandardAdditions = true;
50
43
  return app.frontmost();
51
- }, appName);
44
+ }, [appName], { signal });
52
45
  }
53
46
  /* #endregion */
package/lib/dialog.d.ts CHANGED
@@ -4,11 +4,11 @@ export declare const dialog: {
4
4
  chooseFromList: typeof chooseFromList;
5
5
  chooseMultipleFromList: typeof chooseMultipleFromList;
6
6
  };
7
- declare function prompt(label: string, defaultValue?: string): Promise<string>;
7
+ declare function prompt(label: string, defaultValue?: string, signal?: AbortSignal): Promise<string>;
8
8
  type ChooseOption = {
9
9
  label: string;
10
10
  value: string;
11
11
  };
12
- declare function chooseFromList(list: string[] | ChooseOption[], options?: StandardAdditions.StandardAdditions.ChooseFromListOptionalParameter): Promise<string | undefined>;
13
- declare function chooseMultipleFromList(list: string[] | ChooseOption[], options?: StandardAdditions.StandardAdditions.ChooseFromListOptionalParameter): Promise<string[]>;
12
+ declare function chooseFromList(list: string[] | ChooseOption[], options?: StandardAdditions.StandardAdditions.ChooseFromListOptionalParameter, signal?: AbortSignal): Promise<string | undefined>;
13
+ declare function chooseMultipleFromList(list: string[] | ChooseOption[], options?: StandardAdditions.StandardAdditions.ChooseFromListOptionalParameter, signal?: AbortSignal): Promise<string[]>;
14
14
  export {};
package/lib/dialog.js CHANGED
@@ -1,35 +1,32 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.dialog = void 0;
4
- const run_1 = require("@jxa/run");
5
- exports.dialog = {
1
+ import { runJxa } from 'run-jxa';
2
+ export const dialog = {
6
3
  prompt,
7
4
  chooseFromList,
8
5
  chooseMultipleFromList,
9
6
  };
10
- async function prompt(label, defaultValue = '') {
7
+ async function prompt(label, defaultValue = '', signal) {
11
8
  // if cancel dialog, will throw error
12
9
  try {
13
- return ((await (0, run_1.run)((label) => {
10
+ return ((await runJxa((label) => {
14
11
  const app = Application.currentApplication();
15
12
  app.includeStandardAdditions = true;
16
13
  const response = app.displayDialog(label, { defaultAnswer: '' });
17
14
  return response.textReturned;
18
- }, label)) || defaultValue);
15
+ }, [label], { signal })) || defaultValue);
19
16
  }
20
17
  catch {
21
18
  return defaultValue;
22
19
  }
23
20
  }
24
- async function runOnCurrentApp(method, ...args) {
25
- return await (0, run_1.run)((method, ...args) => {
21
+ async function runOnCurrentApp(method, args, signal) {
22
+ return await runJxa((method, ...args) => {
26
23
  const app = Application.currentApplication();
27
24
  app.includeStandardAdditions = true;
28
25
  // @ts-ignore
29
26
  return app[method]?.(...args);
30
- }, method, ...args);
27
+ }, [method, ...(args || [])], { signal });
31
28
  }
32
- async function chooseFromList(list, options = {}) {
29
+ async function chooseFromList(list, options = {}, signal) {
33
30
  let usingOption = false;
34
31
  let showList = [];
35
32
  if (typeof list[0] === 'object') {
@@ -39,10 +36,7 @@ async function chooseFromList(list, options = {}) {
39
36
  else {
40
37
  showList = list;
41
38
  }
42
- const selected = await runOnCurrentApp('chooseFromList', showList, {
43
- multipleSelectionsAllowed: false,
44
- ...options,
45
- });
39
+ const selected = await runOnCurrentApp('chooseFromList', [showList, { multipleSelectionsAllowed: false, ...options }], signal);
46
40
  if (!selected)
47
41
  return;
48
42
  const label = selected[0];
@@ -54,7 +48,7 @@ async function chooseFromList(list, options = {}) {
54
48
  return val;
55
49
  }
56
50
  }
57
- async function chooseMultipleFromList(list, options = {}) {
51
+ async function chooseMultipleFromList(list, options = {}, signal) {
58
52
  let usingOption = false;
59
53
  let showList = [];
60
54
  if (typeof list[0] === 'object') {
@@ -64,10 +58,7 @@ async function chooseMultipleFromList(list, options = {}) {
64
58
  else {
65
59
  showList = list;
66
60
  }
67
- const selected = await runOnCurrentApp('chooseFromList', showList, {
68
- multipleSelectionsAllowed: true,
69
- ...options,
70
- });
61
+ const selected = await runOnCurrentApp('chooseFromList', [showList, { multipleSelectionsAllowed: true, ...options }], signal);
71
62
  if (!selected)
72
63
  return [];
73
64
  if (!usingOption) {
package/lib/finder.d.ts CHANGED
@@ -2,32 +2,45 @@
2
2
  * `App` named epxorts
3
3
  */
4
4
  export declare const PathFinder: {
5
- allSelected(): Promise<string[]>;
6
- singleSelected(): Promise<string | undefined>;
7
- setSelected(filePaths: string[]): Promise<void>;
5
+ allSelected: typeof PathFinder_allSelected;
6
+ singleSelected: typeof PathFinder_singleSelected;
7
+ setSelected: typeof PathFinder_setSelected;
8
8
  getActiveTabDir: typeof PathFinder_getActiveTabDir;
9
9
  };
10
10
  export declare const Finder: {
11
- allSelected(): Promise<string[]>;
12
- singleSelected(): Promise<string | undefined>;
13
- setSelected(filePaths: string[]): Promise<void>;
11
+ allSelected: typeof Finder_allSelected;
12
+ singleSelected(signal?: AbortSignal): Promise<string | undefined>;
13
+ setSelected: typeof Finder_setSelected;
14
+ showInfoWindow: typeof Finder_showInfoWindow;
14
15
  };
15
16
  export declare const QSpace: {
16
- allSelected(): Promise<string[]>;
17
- singleSelected(): Promise<string | undefined>;
17
+ allSelected: typeof QSpace_allSelected;
18
+ singleSelected(signal?: AbortSignal): Promise<string | undefined>;
18
19
  };
20
+ /**
21
+ * impls
22
+ */
23
+ declare function PathFinder_allSelected(signal?: AbortSignal): Promise<string[]>;
24
+ declare function PathFinder_singleSelected(signal?: AbortSignal): Promise<string | undefined>;
25
+ declare function PathFinder_setSelected(filePaths: string[], signal?: AbortSignal): Promise<void>;
19
26
  /**
20
27
  * 获取 activeTab dir, 当没有 selection 时可用,
21
28
  * 要求只能有一个 visible window, 从 jxa 这一层无法区分 window z-index
22
29
  */
23
- declare function PathFinder_getActiveTabDir(): Promise<string | undefined>;
30
+ declare function PathFinder_getActiveTabDir(signal?: AbortSignal): Promise<string | undefined>;
31
+ declare function Finder_allSelected(signal?: AbortSignal): Promise<string[]>;
32
+ declare function Finder_setSelected(filePaths: string[], signal?: AbortSignal): Promise<void>;
33
+ declare function Finder_showInfoWindow(filePaths: string | string[], signal?: AbortSignal): Promise<void>;
24
34
  export type QSpaceFileItem = {
25
35
  id: () => string;
26
36
  name: () => string;
27
37
  urlstr: () => string;
28
38
  };
39
+ declare function QSpace_allSelected(signal?: AbortSignal): Promise<string[]>;
29
40
  export declare function isRepresentPF(file: string): boolean;
30
41
  export declare function isRepresentQS(file: string): boolean;
42
+ export declare const isRepresentPFDescription = "@pf or $pf or pf, ignore case";
43
+ export declare const isRepresentQSDescription = "@qs or $qs or qs, ignore case";
31
44
  /**
32
45
  * 处理 `@pf` `@qs`, ignore case
33
46
  */
package/lib/finder.js CHANGED
@@ -1,154 +1,154 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.QSpace = exports.Finder = exports.PathFinder = void 0;
7
- exports.isRepresentPF = isRepresentPF;
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 dedent from 'dedent';
5
+ import { uniq } from 'es-toolkit';
6
+ import pmap from 'promise.map';
7
+ import { runAppleScript } from 'run-applescript';
8
+ import { runJxa } from 'run-jxa';
9
+ import { isAppRunning } from './app.js';
17
10
  /**
18
11
  * `App` named epxorts
19
12
  */
20
- exports.PathFinder = {
21
- allSelected() {
22
- return PathFinder_allSelected();
23
- },
24
- singleSelected() {
25
- return PathFinder_singleSelected();
26
- },
27
- setSelected(filePaths) {
28
- return PathFinder_setSelected(filePaths);
29
- },
13
+ export const PathFinder = {
14
+ allSelected: PathFinder_allSelected,
15
+ singleSelected: PathFinder_singleSelected,
16
+ setSelected: PathFinder_setSelected,
30
17
  getActiveTabDir: PathFinder_getActiveTabDir,
31
18
  };
32
- exports.Finder = {
33
- allSelected() {
34
- return Finder_allSelected();
35
- },
36
- async singleSelected() {
37
- return (await Finder_allSelected())[0];
38
- },
39
- setSelected(filePaths) {
40
- return Finder_setSelected(filePaths);
19
+ export const Finder = {
20
+ allSelected: Finder_allSelected,
21
+ async singleSelected(signal) {
22
+ return (await Finder_allSelected(signal))[0];
41
23
  },
24
+ setSelected: Finder_setSelected,
25
+ showInfoWindow: Finder_showInfoWindow,
42
26
  };
43
- exports.QSpace = {
44
- allSelected() {
45
- return QSpace_allSelected();
46
- },
47
- async singleSelected() {
48
- return (await QSpace_allSelected())[0];
27
+ export const QSpace = {
28
+ allSelected: QSpace_allSelected,
29
+ async singleSelected(signal) {
30
+ return (await QSpace_allSelected(signal))[0];
49
31
  },
50
32
  };
51
33
  /**
52
34
  * impls
53
35
  */
54
36
  /* #region PathFinder */
55
- async function PathFinder_allSelected() {
56
- if (!(await (0, app_1.isAppRunning)('Path Finder'))) {
37
+ async function PathFinder_allSelected(signal) {
38
+ if (!(await isAppRunning('Path Finder'))) {
57
39
  return [];
58
40
  }
59
- const filePaths = await (0, run_1.run)(() => {
41
+ const filePaths = await runJxa(() => {
60
42
  const app = Application('Path Finder');
61
43
  return (app.selection() || []).map((x) => x.posixPath());
62
- });
44
+ }, undefined, { signal });
63
45
  return filePaths;
64
46
  }
65
- async function PathFinder_singleSelected() {
66
- if (!(await (0, app_1.isAppRunning)('Path Finder'))) {
47
+ async function PathFinder_singleSelected(signal) {
48
+ if (!(await isAppRunning('Path Finder'))) {
67
49
  return;
68
50
  }
69
- const firstFilePath = await (0, run_1.run)(() => {
51
+ const firstFilePath = await runJxa(() => {
70
52
  const app = Application('Path Finder');
71
53
  return (app.selection() || [])[0]?.posixPath();
72
- });
54
+ }, undefined, { signal });
73
55
  return firstFilePath;
74
56
  }
75
- async function PathFinder_setSelected(filePaths) {
76
- await (0, run_1.run)((filePaths) => {
57
+ async function PathFinder_setSelected(filePaths, signal) {
58
+ await runJxa((filePaths) => {
77
59
  const app = Application('Path Finder');
78
60
  app.select(filePaths);
79
- }, filePaths);
61
+ return null;
62
+ }, [filePaths], { signal });
80
63
  }
81
64
  /**
82
65
  * 获取 activeTab dir, 当没有 selection 时可用,
83
66
  * 要求只能有一个 visible window, 从 jxa 这一层无法区分 window z-index
84
67
  */
85
- async function PathFinder_getActiveTabDir() {
86
- if (!(await (0, app_1.isAppRunning)('Path Finder')))
68
+ async function PathFinder_getActiveTabDir(signal) {
69
+ if (!(await isAppRunning('Path Finder')))
87
70
  return;
88
- const visibleWindowCount = await (0, run_1.run)(() => {
71
+ const visibleWindowCount = await runJxa(() => {
89
72
  const app = Application('Path Finder');
90
73
  return app.finderWindows.whose({ visible: true }).length;
91
- });
74
+ }, undefined, { signal });
92
75
  if (!visibleWindowCount)
93
76
  return;
94
77
  if (visibleWindowCount > 1) {
95
78
  throw new Error('Multiple visible PathFinder window not supported in PathFinder_getActiveTabDir');
96
79
  }
97
- const dir = await (0, run_1.run)(() => {
80
+ const dir = await runJxa(() => {
98
81
  const app = Application('Path Finder');
99
82
  const w = app.finderWindows.whose({ visible: true })[0];
100
83
  return w.target.posixPath();
101
- });
84
+ }, undefined, { signal });
102
85
  return dir;
103
86
  }
104
87
  /* #endregion */
105
88
  /* #region Finder */
106
- async function Finder_allSelected() {
107
- const urls = await (0, run_1.run)(() => {
89
+ async function Finder_allSelected(signal) {
90
+ const urls = await runJxa(() => {
108
91
  const app = Application('Finder');
109
92
  const selection = app.selection();
110
93
  return (selection || []).map((x) => x.url());
111
- });
112
- return urls.map((u) => (0, node_url_1.fileURLToPath)(u));
94
+ }, undefined, { signal });
95
+ return urls.map((u) => fileURLToPath(u));
113
96
  }
114
- async function Finder_setSelected(filePaths) {
115
- await (0, run_1.run)((filePaths) => {
97
+ async function Finder_setSelected(filePaths, signal) {
98
+ await runJxa((filePaths) => {
116
99
  const app = Application('Finder');
117
100
  app.select(filePaths.map((f) => Path(f)));
118
101
  app.activate();
119
- }, filePaths);
102
+ return null;
103
+ }, [filePaths], { signal });
104
+ }
105
+ async function Finder_showInfoWindow(filePaths, signal) {
106
+ const inputPaths = [filePaths].flat().map((x) => path.resolve(x));
107
+ if (!inputPaths.length)
108
+ return;
109
+ // https://apple.stackexchange.com/questions/409243/use-the-finder-to-act-on-a-path-via-jxa
110
+ // no easy way to write this in JXA
111
+ const applescriptContent = dedent `
112
+ tell application "Finder"
113
+ ${inputPaths.map((p) => `open information window of (POSIX file "${p}" as alias)`).join('\n')}
114
+ activate
115
+ end tell
116
+ `;
117
+ await runAppleScript(applescriptContent, { signal });
120
118
  }
121
- async function QSpace_allSelected() {
122
- if (!(await (0, app_1.isAppRunning)('QSpace Pro'))) {
119
+ async function QSpace_allSelected(signal) {
120
+ if (!(await isAppRunning('QSpace Pro'))) {
123
121
  return [];
124
122
  }
125
- const urls = await (0, run_1.run)(() => {
123
+ const urls = await runJxa(() => {
126
124
  const app = Application('QSpace Pro');
127
125
  const selection = (app.selectedItems() || []).map((x) => x.urlstr());
128
126
  return selection;
129
- });
130
- return urls.map((u) => (0, node_url_1.fileURLToPath)(u));
127
+ }, undefined, { signal });
128
+ return urls.map((u) => fileURLToPath(u));
131
129
  }
132
130
  /* #endregion */
133
131
  /* #region Top Level Helper */
134
- function isRepresentPF(file) {
132
+ export function isRepresentPF(file) {
135
133
  return /^[@$]?pf$/i.test(file);
136
134
  }
137
- function isRepresentQS(file) {
135
+ export function isRepresentQS(file) {
138
136
  return /^[@$]?qs$/i.test(file);
139
137
  }
138
+ export const isRepresentPFDescription = '@pf or $pf or pf, ignore case';
139
+ export const isRepresentQSDescription = '@qs or $qs or qs, ignore case';
140
140
  /**
141
141
  * 处理 `@pf` `@qs`, ignore case
142
142
  */
143
- async function normalizeInputFileList(fileList) {
144
- return (0, es_toolkit_1.uniq)((await (0, promise_map_1.default)(fileList, async (f) => {
143
+ export async function normalizeInputFileList(fileList) {
144
+ return uniq((await pmap(fileList, async (f) => {
145
145
  if (process.platform === 'darwin') {
146
146
  if (isRepresentPF(f))
147
- return await exports.PathFinder.allSelected();
147
+ return await PathFinder.allSelected();
148
148
  if (isRepresentQS(f))
149
- return await exports.QSpace.allSelected();
149
+ return await QSpace.allSelected();
150
150
  }
151
- return node_path_1.default.resolve(f);
152
- }, (0, node_os_1.availableParallelism)())).flat());
151
+ return path.resolve(f);
152
+ }, availableParallelism())).flat());
153
153
  }
154
154
  /* #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
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
- if (k2 === undefined) k2 = k;
6
- var desc = Object.getOwnPropertyDescriptor(m, k);
7
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
- desc = { enumerable: true, get: function() { return m[k]; } };
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
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.iTerm = void 0;
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 { runJxa } from 'run-jxa';
2
+ export async function runInIterm(command, reuse = true) {
3
+ const result = await runJxa((command, reuse) => {
8
4
  const app = Application('iTerm');
9
5
  app.includeStandardAdditions = true;
10
6
  app.activate();
@@ -25,12 +21,13 @@ async function runInIterm(command, reuse = true) {
25
21
  if (!usingSession)
26
22
  return 'no-session';
27
23
  app.write(usingSession, { text: command });
28
- }, command, reuse);
24
+ return null;
25
+ }, [command, reuse]);
29
26
  if (result === 'no-session') {
30
27
  console.error('No iTerm session found');
31
28
  }
32
29
  }
33
- exports.iTerm = {
30
+ export const iTerm = {
34
31
  run: runInIterm,
35
32
  runInIterm,
36
33
  };
@@ -1,22 +1,20 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const run_1 = require("@jxa/run");
1
+ import { runJxa } from 'run-jxa';
4
2
  /**
5
3
  * 不太准确, 比如 vscode 进程 `Code` 在运行, 检测不到
6
4
  */
7
- async function isAppRunning(appName) {
8
- return await (0, run_1.run)((appName) => {
5
+ async function isAppRunning(appName, signal) {
6
+ return await runJxa((appName) => {
9
7
  const app = Application('System Events');
10
8
  app.includeStandardAdditions = true;
11
9
  const runningAppNames = app.processes().map((p) => p.name());
12
10
  return runningAppNames.includes(appName);
13
- }, appName);
11
+ }, [appName], { signal });
14
12
  }
15
- async function getRunningAppNames() {
16
- return await (0, run_1.run)(() => {
13
+ async function getRunningAppNames(signal) {
14
+ return await runJxa(() => {
17
15
  const app = Application('System Events');
18
16
  app.includeStandardAdditions = true;
19
17
  const runningAppNames = app.processes().map((p) => p.name());
20
18
  return runningAppNames;
21
- });
19
+ }, undefined, { signal });
22
20
  }
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "mac-helper",
3
- "version": "0.8.1",
4
- "packageManager": "pnpm@11.1.3+sha512.c85357fe17ca12dd23dd7071822666dfd7e3cb76fe214e3370b5ea2fb34f2a231185509b63e717f3cd0acb38dd3f8d82bcd5e8172400ae678b70ea4fbed0896d",
3
+ "type": "module",
4
+ "version": "0.10.0",
5
+ "packageManager": "pnpm@11.8.0+sha512.c1f5e7c4cb241c8f174b743851d82f42b802324afc8b0f116b96adb15aa06664948dde36960a3ba1079ba5b4b29dd0140135b94b5b5f5263592249d68e555f26",
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,40 +36,47 @@
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": "tsc -w --incremental",
36
- "typecheck": "tsc --noEmit",
37
- "build": "rm -rf lib; rm *.tsbuildinfo; tsc",
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
- "@jxa/run": "^1.4.0",
42
- "es-toolkit": "^1.46.1",
43
- "promise.map": "^1.1.3"
45
+ "dedent": "^1.7.2",
46
+ "es-toolkit": "^1.47.1",
47
+ "promise.map": "^1.2.0",
48
+ "run-applescript": "^7.1.0",
49
+ "run-jxa": "^4.0.0"
44
50
  },
45
51
  "devDependencies": {
46
52
  "@jxa/global-type": "^1.4.0",
47
53
  "@jxa/sdef-to-dts": "^1.4.0",
48
54
  "@jxa/types": "^1.4.0",
49
- "@magicdawn/eslint-config": "^1.6.5",
55
+ "@magicdawn/eslint-config": "^1.6.8",
50
56
  "@magicdawn/prettier-config": "^0.1.0",
51
57
  "@types/fs-extra": "^11.0.4",
52
- "@types/node": "^25.8.0",
53
- "@vitest/coverage-v8": "^4.1.6",
58
+ "@types/node": "^25.9.4",
59
+ "@typescript/native-preview": "7.0.0-dev.20260620.1",
60
+ "@vitest/coverage-v8": "^4.1.9",
54
61
  "delay": "^7.0.0",
55
- "eslint": "^10.4.0",
62
+ "eslint": "^10.5.0",
56
63
  "eslint-config-prettier": "^10.1.8",
57
64
  "fs-extra": "^11.3.5",
58
65
  "husky": "^9.1.7",
59
66
  "jxa-common-used": "^0.0.3",
60
- "lint-staged": "^17.0.5",
61
- "prettier": "^3.8.3",
67
+ "lint-staged": "^17.0.7",
68
+ "prettier": "^3.8.4",
62
69
  "should": "^13.2.3",
63
70
  "typescript": "^6.0.3",
64
- "vitest": "^4.1.6"
71
+ "vitest": "^4.1.9"
65
72
  },
66
73
  "lint-staged": {
67
- "*.{js,jsx,ts,tsx,less,md}": [
68
- "prettier --write"
74
+ "*.{?(c|m)(j|t)s?(x),json,y?(a)ml}": [
75
+ "eslint --fix --no-warn-ignored",
76
+ "prettier --write --ignore-unknown"
77
+ ],
78
+ "!*.{?(c|m)(j|t)s?(x),json,y?(a)ml}": [
79
+ "prettier --write --ignore-unknown"
69
80
  ]
70
81
  }
71
82
  }
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
@@ -1,8 +0,0 @@
1
- /// <reference types='@jxa/global-type' />
2
- /// <reference types='@jxa/types' />
3
-
4
- export * from './app'
5
- export * from './chrome'
6
- export * from './dialog'
7
- export * from './finder'
8
- export * from './iterm'
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
- }
@@ -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
- }