mac-helper 0.9.0 → 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/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,4 +1,4 @@
1
- import { run } from '@jxa/run';
1
+ import { runJxa } from 'run-jxa';
2
2
  import { isAppRunning } from './app.js';
3
3
  export var BrowserAppName;
4
4
  (function (BrowserAppName) {
@@ -11,8 +11,8 @@ export class ChromiumBrowserHelper {
11
11
  this.appName = appName;
12
12
  }
13
13
  isRunning = () => isChromiumAppRunning(this.appName);
14
- isFrontmost = () => getChromiumIsFrontmost(this.appName);
15
- getActiveTab = () => getChromiumActiveTab(this.appName);
14
+ isFrontmost = (signal) => getChromiumIsFrontmost(this.appName, signal);
15
+ getActiveTab = (signal) => getChromiumActiveTab(this.appName, signal);
16
16
  }
17
17
  export const Chrome = new ChromiumBrowserHelper(BrowserAppName.Chrome);
18
18
  export const Brave = new ChromiumBrowserHelper(BrowserAppName.Brave);
@@ -20,10 +20,10 @@ export const Brave = new ChromiumBrowserHelper(BrowserAppName.Brave);
20
20
  export function isChromiumAppRunning(appName = BrowserAppName.Chrome) {
21
21
  return isAppRunning(appName);
22
22
  }
23
- export async function getChromiumActiveTab(appName = BrowserAppName.Chrome) {
23
+ export async function getChromiumActiveTab(appName = BrowserAppName.Chrome, signal) {
24
24
  if (!(await isChromiumAppRunning(appName)))
25
25
  return;
26
- return await run((appName) => {
26
+ return await runJxa((appName) => {
27
27
  const app = Application(appName);
28
28
  app.includeStandardAdditions = true;
29
29
  const win = (app.windows() || []).find((w) => !w.minimized());
@@ -32,15 +32,15 @@ export async function getChromiumActiveTab(appName = BrowserAppName.Chrome) {
32
32
  url: tab?.url(),
33
33
  title: tab?.title(),
34
34
  };
35
- }, appName);
35
+ }, [appName], { signal });
36
36
  }
37
- export async function getChromiumIsFrontmost(appName = BrowserAppName.Chrome) {
37
+ export async function getChromiumIsFrontmost(appName = BrowserAppName.Chrome, signal) {
38
38
  if (!isChromiumAppRunning(appName))
39
39
  return false;
40
- return await run((appName) => {
40
+ return await runJxa((appName) => {
41
41
  const app = Application(appName);
42
42
  app.includeStandardAdditions = true;
43
43
  return app.frontmost();
44
- }, appName);
44
+ }, [appName], { signal });
45
45
  }
46
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,32 +1,32 @@
1
- import { run } from '@jxa/run';
1
+ import { runJxa } from 'run-jxa';
2
2
  export const dialog = {
3
3
  prompt,
4
4
  chooseFromList,
5
5
  chooseMultipleFromList,
6
6
  };
7
- async function prompt(label, defaultValue = '') {
7
+ async function prompt(label, defaultValue = '', signal) {
8
8
  // if cancel dialog, will throw error
9
9
  try {
10
- return ((await run((label) => {
10
+ return ((await runJxa((label) => {
11
11
  const app = Application.currentApplication();
12
12
  app.includeStandardAdditions = true;
13
13
  const response = app.displayDialog(label, { defaultAnswer: '' });
14
14
  return response.textReturned;
15
- }, label)) || defaultValue);
15
+ }, [label], { signal })) || defaultValue);
16
16
  }
17
17
  catch {
18
18
  return defaultValue;
19
19
  }
20
20
  }
21
- async function runOnCurrentApp(method, ...args) {
22
- return await run((method, ...args) => {
21
+ async function runOnCurrentApp(method, args, signal) {
22
+ return await runJxa((method, ...args) => {
23
23
  const app = Application.currentApplication();
24
24
  app.includeStandardAdditions = true;
25
25
  // @ts-ignore
26
26
  return app[method]?.(...args);
27
- }, method, ...args);
27
+ }, [method, ...(args || [])], { signal });
28
28
  }
29
- async function chooseFromList(list, options = {}) {
29
+ async function chooseFromList(list, options = {}, signal) {
30
30
  let usingOption = false;
31
31
  let showList = [];
32
32
  if (typeof list[0] === 'object') {
@@ -36,10 +36,7 @@ async function chooseFromList(list, options = {}) {
36
36
  else {
37
37
  showList = list;
38
38
  }
39
- const selected = await runOnCurrentApp('chooseFromList', showList, {
40
- multipleSelectionsAllowed: false,
41
- ...options,
42
- });
39
+ const selected = await runOnCurrentApp('chooseFromList', [showList, { multipleSelectionsAllowed: false, ...options }], signal);
43
40
  if (!selected)
44
41
  return;
45
42
  const label = selected[0];
@@ -51,7 +48,7 @@ async function chooseFromList(list, options = {}) {
51
48
  return val;
52
49
  }
53
50
  }
54
- async function chooseMultipleFromList(list, options = {}) {
51
+ async function chooseMultipleFromList(list, options = {}, signal) {
55
52
  let usingOption = false;
56
53
  let showList = [];
57
54
  if (typeof list[0] === 'object') {
@@ -61,10 +58,7 @@ async function chooseMultipleFromList(list, options = {}) {
61
58
  else {
62
59
  showList = list;
63
60
  }
64
- const selected = await runOnCurrentApp('chooseFromList', showList, {
65
- multipleSelectionsAllowed: true,
66
- ...options,
67
- });
61
+ const selected = await runOnCurrentApp('chooseFromList', [showList, { multipleSelectionsAllowed: true, ...options }], signal);
68
62
  if (!selected)
69
63
  return [];
70
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,123 +1,130 @@
1
1
  import { availableParallelism } from 'node:os';
2
2
  import path from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
- import { run as jxaRun } from '@jxa/run';
4
+ import dedent from 'dedent';
5
5
  import { uniq } from 'es-toolkit';
6
6
  import pmap from 'promise.map';
7
+ import { runAppleScript } from 'run-applescript';
8
+ import { runJxa } from 'run-jxa';
7
9
  import { isAppRunning } from './app.js';
8
10
  /**
9
11
  * `App` named epxorts
10
12
  */
11
13
  export const PathFinder = {
12
- allSelected() {
13
- return PathFinder_allSelected();
14
- },
15
- singleSelected() {
16
- return PathFinder_singleSelected();
17
- },
18
- setSelected(filePaths) {
19
- return PathFinder_setSelected(filePaths);
20
- },
14
+ allSelected: PathFinder_allSelected,
15
+ singleSelected: PathFinder_singleSelected,
16
+ setSelected: PathFinder_setSelected,
21
17
  getActiveTabDir: PathFinder_getActiveTabDir,
22
18
  };
23
19
  export const Finder = {
24
- allSelected() {
25
- return Finder_allSelected();
26
- },
27
- async singleSelected() {
28
- return (await Finder_allSelected())[0];
29
- },
30
- setSelected(filePaths) {
31
- return Finder_setSelected(filePaths);
20
+ allSelected: Finder_allSelected,
21
+ async singleSelected(signal) {
22
+ return (await Finder_allSelected(signal))[0];
32
23
  },
24
+ setSelected: Finder_setSelected,
25
+ showInfoWindow: Finder_showInfoWindow,
33
26
  };
34
27
  export const QSpace = {
35
- allSelected() {
36
- return QSpace_allSelected();
37
- },
38
- async singleSelected() {
39
- return (await QSpace_allSelected())[0];
28
+ allSelected: QSpace_allSelected,
29
+ async singleSelected(signal) {
30
+ return (await QSpace_allSelected(signal))[0];
40
31
  },
41
32
  };
42
33
  /**
43
34
  * impls
44
35
  */
45
36
  /* #region PathFinder */
46
- async function PathFinder_allSelected() {
37
+ async function PathFinder_allSelected(signal) {
47
38
  if (!(await isAppRunning('Path Finder'))) {
48
39
  return [];
49
40
  }
50
- const filePaths = await jxaRun(() => {
41
+ const filePaths = await runJxa(() => {
51
42
  const app = Application('Path Finder');
52
43
  return (app.selection() || []).map((x) => x.posixPath());
53
- });
44
+ }, undefined, { signal });
54
45
  return filePaths;
55
46
  }
56
- async function PathFinder_singleSelected() {
47
+ async function PathFinder_singleSelected(signal) {
57
48
  if (!(await isAppRunning('Path Finder'))) {
58
49
  return;
59
50
  }
60
- const firstFilePath = await jxaRun(() => {
51
+ const firstFilePath = await runJxa(() => {
61
52
  const app = Application('Path Finder');
62
53
  return (app.selection() || [])[0]?.posixPath();
63
- });
54
+ }, undefined, { signal });
64
55
  return firstFilePath;
65
56
  }
66
- async function PathFinder_setSelected(filePaths) {
67
- await jxaRun((filePaths) => {
57
+ async function PathFinder_setSelected(filePaths, signal) {
58
+ await runJxa((filePaths) => {
68
59
  const app = Application('Path Finder');
69
60
  app.select(filePaths);
70
- }, filePaths);
61
+ return null;
62
+ }, [filePaths], { signal });
71
63
  }
72
64
  /**
73
65
  * 获取 activeTab dir, 当没有 selection 时可用,
74
66
  * 要求只能有一个 visible window, 从 jxa 这一层无法区分 window z-index
75
67
  */
76
- async function PathFinder_getActiveTabDir() {
68
+ async function PathFinder_getActiveTabDir(signal) {
77
69
  if (!(await isAppRunning('Path Finder')))
78
70
  return;
79
- const visibleWindowCount = await jxaRun(() => {
71
+ const visibleWindowCount = await runJxa(() => {
80
72
  const app = Application('Path Finder');
81
73
  return app.finderWindows.whose({ visible: true }).length;
82
- });
74
+ }, undefined, { signal });
83
75
  if (!visibleWindowCount)
84
76
  return;
85
77
  if (visibleWindowCount > 1) {
86
78
  throw new Error('Multiple visible PathFinder window not supported in PathFinder_getActiveTabDir');
87
79
  }
88
- const dir = await jxaRun(() => {
80
+ const dir = await runJxa(() => {
89
81
  const app = Application('Path Finder');
90
82
  const w = app.finderWindows.whose({ visible: true })[0];
91
83
  return w.target.posixPath();
92
- });
84
+ }, undefined, { signal });
93
85
  return dir;
94
86
  }
95
87
  /* #endregion */
96
88
  /* #region Finder */
97
- async function Finder_allSelected() {
98
- const urls = await jxaRun(() => {
89
+ async function Finder_allSelected(signal) {
90
+ const urls = await runJxa(() => {
99
91
  const app = Application('Finder');
100
92
  const selection = app.selection();
101
93
  return (selection || []).map((x) => x.url());
102
- });
94
+ }, undefined, { signal });
103
95
  return urls.map((u) => fileURLToPath(u));
104
96
  }
105
- async function Finder_setSelected(filePaths) {
106
- await jxaRun((filePaths) => {
97
+ async function Finder_setSelected(filePaths, signal) {
98
+ await runJxa((filePaths) => {
107
99
  const app = Application('Finder');
108
100
  app.select(filePaths.map((f) => Path(f)));
109
101
  app.activate();
110
- }, 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 });
111
118
  }
112
- async function QSpace_allSelected() {
119
+ async function QSpace_allSelected(signal) {
113
120
  if (!(await isAppRunning('QSpace Pro'))) {
114
121
  return [];
115
122
  }
116
- const urls = await jxaRun(() => {
123
+ const urls = await runJxa(() => {
117
124
  const app = Application('QSpace Pro');
118
125
  const selection = (app.selectedItems() || []).map((x) => x.urlstr());
119
126
  return selection;
120
- });
127
+ }, undefined, { signal });
121
128
  return urls.map((u) => fileURLToPath(u));
122
129
  }
123
130
  /* #endregion */
@@ -128,6 +135,8 @@ export function isRepresentPF(file) {
128
135
  export function isRepresentQS(file) {
129
136
  return /^[@$]?qs$/i.test(file);
130
137
  }
138
+ export const isRepresentPFDescription = '@pf or $pf or pf, ignore case';
139
+ export const isRepresentQSDescription = '@qs or $qs or qs, ignore case';
131
140
  /**
132
141
  * 处理 `@pf` `@qs`, ignore case
133
142
  */
package/lib/iterm.js CHANGED
@@ -1,6 +1,6 @@
1
- import { run } from '@jxa/run';
1
+ import { runJxa } from 'run-jxa';
2
2
  export async function runInIterm(command, reuse = true) {
3
- const result = await run((command, reuse) => {
3
+ const result = await runJxa((command, reuse) => {
4
4
  const app = Application('iTerm');
5
5
  app.includeStandardAdditions = true;
6
6
  app.activate();
@@ -21,7 +21,8 @@ export async function runInIterm(command, reuse = true) {
21
21
  if (!usingSession)
22
22
  return 'no-session';
23
23
  app.write(usingSession, { text: command });
24
- }, command, reuse);
24
+ return null;
25
+ }, [command, reuse]);
25
26
  if (result === 'no-session') {
26
27
  console.error('No iTerm session found');
27
28
  }
@@ -1,20 +1,20 @@
1
- import { run } from '@jxa/run';
1
+ import { runJxa } from 'run-jxa';
2
2
  /**
3
3
  * 不太准确, 比如 vscode 进程 `Code` 在运行, 检测不到
4
4
  */
5
- async function isAppRunning(appName) {
6
- return await run((appName) => {
5
+ async function isAppRunning(appName, signal) {
6
+ return await runJxa((appName) => {
7
7
  const app = Application('System Events');
8
8
  app.includeStandardAdditions = true;
9
9
  const runningAppNames = app.processes().map((p) => p.name());
10
10
  return runningAppNames.includes(appName);
11
- }, appName);
11
+ }, [appName], { signal });
12
12
  }
13
- async function getRunningAppNames() {
14
- return await run(() => {
13
+ async function getRunningAppNames(signal) {
14
+ return await runJxa(() => {
15
15
  const app = Application('System Events');
16
16
  app.includeStandardAdditions = true;
17
17
  const runningAppNames = app.processes().map((p) => p.name());
18
18
  return runningAppNames;
19
- });
19
+ }, undefined, { signal });
20
20
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "mac-helper",
3
3
  "type": "module",
4
- "version": "0.9.0",
5
- "packageManager": "pnpm@11.5.0+sha512.dbfcc4f81cf48597afd4bc391ffdf12c11f1a9fb83a395bfa6b0a2d9cc2fd8ffebafdb1ccbd529632153f793904c2615b7f09fe1a345473fd1c35845172a8eb1",
4
+ "version": "0.10.0",
5
+ "packageManager": "pnpm@11.8.0+sha512.c1f5e7c4cb241c8f174b743851d82f42b802324afc8b0f116b96adb15aa06664948dde36960a3ba1079ba5b4b29dd0140135b94b5b5f5263592249d68e555f26",
6
6
  "description": "useful helper for macOS",
7
7
  "author": "magicdawn",
8
8
  "license": "MIT",
@@ -42,35 +42,41 @@
42
42
  "prepublishOnly": "pnpm test && pnpm build"
43
43
  },
44
44
  "dependencies": {
45
- "@jxa/run": "^1.4.0",
46
- "es-toolkit": "^1.47.0",
47
- "promise.map": "^1.2.0"
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"
48
50
  },
49
51
  "devDependencies": {
50
52
  "@jxa/global-type": "^1.4.0",
51
53
  "@jxa/sdef-to-dts": "^1.4.0",
52
54
  "@jxa/types": "^1.4.0",
53
- "@magicdawn/eslint-config": "github:magicdawn/eslint-config#dist",
55
+ "@magicdawn/eslint-config": "^1.6.8",
54
56
  "@magicdawn/prettier-config": "^0.1.0",
55
57
  "@types/fs-extra": "^11.0.4",
56
- "@types/node": "^25.9.1",
57
- "@typescript/native-preview": "7.0.0-dev.20260527.2",
58
- "@vitest/coverage-v8": "^4.1.7",
58
+ "@types/node": "^25.9.4",
59
+ "@typescript/native-preview": "7.0.0-dev.20260620.1",
60
+ "@vitest/coverage-v8": "^4.1.9",
59
61
  "delay": "^7.0.0",
60
- "eslint": "^10.4.1",
62
+ "eslint": "^10.5.0",
61
63
  "eslint-config-prettier": "^10.1.8",
62
64
  "fs-extra": "^11.3.5",
63
65
  "husky": "^9.1.7",
64
66
  "jxa-common-used": "^0.0.3",
65
- "lint-staged": "^17.0.5",
66
- "prettier": "^3.8.3",
67
+ "lint-staged": "^17.0.7",
68
+ "prettier": "^3.8.4",
67
69
  "should": "^13.2.3",
68
70
  "typescript": "^6.0.3",
69
- "vitest": "^4.1.7"
71
+ "vitest": "^4.1.9"
70
72
  },
71
73
  "lint-staged": {
72
- "*.{js,jsx,ts,tsx,less,md}": [
73
- "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"
74
80
  ]
75
81
  }
76
82
  }