simen-keyboard-listener 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,13 +1,16 @@
1
- type IGlobalKeyState = 'DOWN' | 'UP';
1
+ type IGlobalKeyState = 'DOWN' | 'UP' | 'PERMISSION_LOST';
2
2
  interface IGlobalKeyEvent {
3
3
  readonly name: string;
4
4
  readonly state: IGlobalKeyState;
5
5
  }
6
6
  type IGlobalKeyDownMap = Record<string, boolean>;
7
7
  type IGlobalKeyListener = (event: IGlobalKeyEvent, isDown: IGlobalKeyDownMap) => void;
8
+ type IPermissionLostListener = () => void;
8
9
  interface IGlobalKeyboardListener {
9
10
  addListener(listener: IGlobalKeyListener): void;
10
11
  removeListener(listener: IGlobalKeyListener): void;
12
+ onPermissionLost(listener: IPermissionLostListener): void;
13
+ removePermissionLostListener(listener: IPermissionLostListener): void;
11
14
  kill(): void;
12
15
  }
13
16
  /**
@@ -32,5 +35,7 @@ declare function createGlobalKeyboardListener(): IGlobalKeyboardListener;
32
35
  * Call this before starting the listener to provide better UX when permission is missing.
33
36
  */
34
37
  declare function checkKeyboardPermission(): boolean;
38
+ declare function getFocusedInputValue(): string | null;
39
+ declare function getFocusedInputSelectedText(): string | null;
35
40
 
36
- export { type IGlobalKeyDownMap, type IGlobalKeyEvent, type IGlobalKeyListener, type IGlobalKeyState, type IGlobalKeyboardListener, checkKeyboardPermission, createGlobalKeyboardListener, getGlobalKeyboardListener };
41
+ export { type IGlobalKeyDownMap, type IGlobalKeyEvent, type IGlobalKeyListener, type IGlobalKeyState, type IGlobalKeyboardListener, type IPermissionLostListener, checkKeyboardPermission, createGlobalKeyboardListener, getFocusedInputSelectedText, getFocusedInputValue, getGlobalKeyboardListener };
package/dist/index.d.ts CHANGED
@@ -1,13 +1,16 @@
1
- type IGlobalKeyState = 'DOWN' | 'UP';
1
+ type IGlobalKeyState = 'DOWN' | 'UP' | 'PERMISSION_LOST';
2
2
  interface IGlobalKeyEvent {
3
3
  readonly name: string;
4
4
  readonly state: IGlobalKeyState;
5
5
  }
6
6
  type IGlobalKeyDownMap = Record<string, boolean>;
7
7
  type IGlobalKeyListener = (event: IGlobalKeyEvent, isDown: IGlobalKeyDownMap) => void;
8
+ type IPermissionLostListener = () => void;
8
9
  interface IGlobalKeyboardListener {
9
10
  addListener(listener: IGlobalKeyListener): void;
10
11
  removeListener(listener: IGlobalKeyListener): void;
12
+ onPermissionLost(listener: IPermissionLostListener): void;
13
+ removePermissionLostListener(listener: IPermissionLostListener): void;
11
14
  kill(): void;
12
15
  }
13
16
  /**
@@ -32,5 +35,7 @@ declare function createGlobalKeyboardListener(): IGlobalKeyboardListener;
32
35
  * Call this before starting the listener to provide better UX when permission is missing.
33
36
  */
34
37
  declare function checkKeyboardPermission(): boolean;
38
+ declare function getFocusedInputValue(): string | null;
39
+ declare function getFocusedInputSelectedText(): string | null;
35
40
 
36
- export { type IGlobalKeyDownMap, type IGlobalKeyEvent, type IGlobalKeyListener, type IGlobalKeyState, type IGlobalKeyboardListener, checkKeyboardPermission, createGlobalKeyboardListener, getGlobalKeyboardListener };
41
+ export { type IGlobalKeyDownMap, type IGlobalKeyEvent, type IGlobalKeyListener, type IGlobalKeyState, type IGlobalKeyboardListener, type IPermissionLostListener, checkKeyboardPermission, createGlobalKeyboardListener, getFocusedInputSelectedText, getFocusedInputValue, getGlobalKeyboardListener };
package/dist/index.js CHANGED
@@ -32,11 +32,14 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  checkKeyboardPermission: () => checkKeyboardPermission,
34
34
  createGlobalKeyboardListener: () => createGlobalKeyboardListener,
35
+ getFocusedInputSelectedText: () => getFocusedInputSelectedText,
36
+ getFocusedInputValue: () => getFocusedInputValue,
35
37
  getGlobalKeyboardListener: () => getGlobalKeyboardListener
36
38
  });
37
39
  module.exports = __toCommonJS(index_exports);
38
40
  var path = __toESM(require("path"));
39
41
  var fs = __toESM(require("fs"));
42
+ var import_node_child_process = require("child_process");
40
43
  var import_node_module = require("module");
41
44
  var import_url = require("url");
42
45
  var import_meta = {};
@@ -73,8 +76,11 @@ function getNativeAddon() {
73
76
  const localRequire = getRequire();
74
77
  if (packageName) {
75
78
  try {
76
- nativeAddon = localRequire(packageName);
77
- return nativeAddon;
79
+ const candidate = localRequire(packageName);
80
+ if (candidate && typeof candidate.start === "function" && typeof candidate.stop === "function" && typeof candidate.isRunning === "function" && typeof candidate.checkPermission === "function" && typeof candidate.getFocusedInputValue === "function" && typeof candidate.getFocusedInputSelectedText === "function") {
81
+ nativeAddon = candidate;
82
+ return nativeAddon;
83
+ }
78
84
  } catch {
79
85
  }
80
86
  }
@@ -103,6 +109,7 @@ var NativeKeyboardListener = class _NativeKeyboardListener {
103
109
  static _instance = null;
104
110
  started = false;
105
111
  listeners = /* @__PURE__ */ new Set();
112
+ permissionLostListeners = /* @__PURE__ */ new Set();
106
113
  static getInstance() {
107
114
  if (!_NativeKeyboardListener._instance) {
108
115
  const addon = getNativeAddon();
@@ -114,6 +121,10 @@ var NativeKeyboardListener = class _NativeKeyboardListener {
114
121
  this.listeners.add(listener);
115
122
  if (!this.started) {
116
123
  const ok = this.addon.start((event) => {
124
+ if (event.state === "PERMISSION_LOST") {
125
+ this.handlePermissionLost();
126
+ return;
127
+ }
117
128
  const listenersCopy = Array.from(this.listeners);
118
129
  for (const cb of listenersCopy) {
119
130
  try {
@@ -128,6 +139,22 @@ var NativeKeyboardListener = class _NativeKeyboardListener {
128
139
  this.started = true;
129
140
  }
130
141
  }
142
+ onPermissionLost(listener) {
143
+ this.permissionLostListeners.add(listener);
144
+ }
145
+ removePermissionLostListener(listener) {
146
+ this.permissionLostListeners.delete(listener);
147
+ }
148
+ handlePermissionLost() {
149
+ this.started = false;
150
+ const listenersCopy = Array.from(this.permissionLostListeners);
151
+ for (const cb of listenersCopy) {
152
+ try {
153
+ cb();
154
+ } catch {
155
+ }
156
+ }
157
+ }
131
158
  removeListener(listener) {
132
159
  this.listeners.delete(listener);
133
160
  if (this.listeners.size === 0 && this.started) {
@@ -160,6 +187,7 @@ var NativeKeyboardListener = class _NativeKeyboardListener {
160
187
  } finally {
161
188
  this.started = false;
162
189
  this.listeners.clear();
190
+ this.permissionLostListeners.clear();
163
191
  _NativeKeyboardListener._instance = null;
164
192
  }
165
193
  }
@@ -184,9 +212,66 @@ function checkKeyboardPermission() {
184
212
  return false;
185
213
  }
186
214
  }
215
+ var lastMacAccessibilitySettingsOpenTs = 0;
216
+ function openMacAccessibilitySettings() {
217
+ if (process.platform !== "darwin") {
218
+ return;
219
+ }
220
+ const now = Date.now();
221
+ if (now - lastMacAccessibilitySettingsOpenTs < 15e3) {
222
+ return;
223
+ }
224
+ lastMacAccessibilitySettingsOpenTs = now;
225
+ try {
226
+ (0, import_node_child_process.execFileSync)("open", ["x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"], {
227
+ stdio: "ignore"
228
+ });
229
+ } catch {
230
+ }
231
+ }
232
+ function ensureAccessibilityPermission(addon) {
233
+ try {
234
+ if (addon.checkPermission()) {
235
+ return true;
236
+ }
237
+ } catch {
238
+ }
239
+ openMacAccessibilitySettings();
240
+ return false;
241
+ }
242
+ function getFocusedInputValue() {
243
+ if (process.platform !== "darwin") {
244
+ return null;
245
+ }
246
+ const addon = getNativeAddon();
247
+ if (!ensureAccessibilityPermission(addon)) {
248
+ return null;
249
+ }
250
+ try {
251
+ return addon.getFocusedInputValue();
252
+ } catch {
253
+ return null;
254
+ }
255
+ }
256
+ function getFocusedInputSelectedText() {
257
+ if (process.platform !== "darwin") {
258
+ return null;
259
+ }
260
+ const addon = getNativeAddon();
261
+ if (!ensureAccessibilityPermission(addon)) {
262
+ return null;
263
+ }
264
+ try {
265
+ return addon.getFocusedInputSelectedText();
266
+ } catch {
267
+ return null;
268
+ }
269
+ }
187
270
  // Annotate the CommonJS export names for ESM import in node:
188
271
  0 && (module.exports = {
189
272
  checkKeyboardPermission,
190
273
  createGlobalKeyboardListener,
274
+ getFocusedInputSelectedText,
275
+ getFocusedInputValue,
191
276
  getGlobalKeyboardListener
192
277
  });
package/dist/index.mjs CHANGED
@@ -8,6 +8,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
8
8
  // src/index.ts
9
9
  import * as path from "path";
10
10
  import * as fs from "fs";
11
+ import { execFileSync } from "child_process";
11
12
  import { createRequire } from "module";
12
13
  import { fileURLToPath } from "url";
13
14
  var PLATFORM_PACKAGES = {
@@ -43,8 +44,11 @@ function getNativeAddon() {
43
44
  const localRequire = getRequire();
44
45
  if (packageName) {
45
46
  try {
46
- nativeAddon = localRequire(packageName);
47
- return nativeAddon;
47
+ const candidate = localRequire(packageName);
48
+ if (candidate && typeof candidate.start === "function" && typeof candidate.stop === "function" && typeof candidate.isRunning === "function" && typeof candidate.checkPermission === "function" && typeof candidate.getFocusedInputValue === "function" && typeof candidate.getFocusedInputSelectedText === "function") {
49
+ nativeAddon = candidate;
50
+ return nativeAddon;
51
+ }
48
52
  } catch {
49
53
  }
50
54
  }
@@ -73,6 +77,7 @@ var NativeKeyboardListener = class _NativeKeyboardListener {
73
77
  static _instance = null;
74
78
  started = false;
75
79
  listeners = /* @__PURE__ */ new Set();
80
+ permissionLostListeners = /* @__PURE__ */ new Set();
76
81
  static getInstance() {
77
82
  if (!_NativeKeyboardListener._instance) {
78
83
  const addon = getNativeAddon();
@@ -84,6 +89,10 @@ var NativeKeyboardListener = class _NativeKeyboardListener {
84
89
  this.listeners.add(listener);
85
90
  if (!this.started) {
86
91
  const ok = this.addon.start((event) => {
92
+ if (event.state === "PERMISSION_LOST") {
93
+ this.handlePermissionLost();
94
+ return;
95
+ }
87
96
  const listenersCopy = Array.from(this.listeners);
88
97
  for (const cb of listenersCopy) {
89
98
  try {
@@ -98,6 +107,22 @@ var NativeKeyboardListener = class _NativeKeyboardListener {
98
107
  this.started = true;
99
108
  }
100
109
  }
110
+ onPermissionLost(listener) {
111
+ this.permissionLostListeners.add(listener);
112
+ }
113
+ removePermissionLostListener(listener) {
114
+ this.permissionLostListeners.delete(listener);
115
+ }
116
+ handlePermissionLost() {
117
+ this.started = false;
118
+ const listenersCopy = Array.from(this.permissionLostListeners);
119
+ for (const cb of listenersCopy) {
120
+ try {
121
+ cb();
122
+ } catch {
123
+ }
124
+ }
125
+ }
101
126
  removeListener(listener) {
102
127
  this.listeners.delete(listener);
103
128
  if (this.listeners.size === 0 && this.started) {
@@ -130,6 +155,7 @@ var NativeKeyboardListener = class _NativeKeyboardListener {
130
155
  } finally {
131
156
  this.started = false;
132
157
  this.listeners.clear();
158
+ this.permissionLostListeners.clear();
133
159
  _NativeKeyboardListener._instance = null;
134
160
  }
135
161
  }
@@ -154,8 +180,65 @@ function checkKeyboardPermission() {
154
180
  return false;
155
181
  }
156
182
  }
183
+ var lastMacAccessibilitySettingsOpenTs = 0;
184
+ function openMacAccessibilitySettings() {
185
+ if (process.platform !== "darwin") {
186
+ return;
187
+ }
188
+ const now = Date.now();
189
+ if (now - lastMacAccessibilitySettingsOpenTs < 15e3) {
190
+ return;
191
+ }
192
+ lastMacAccessibilitySettingsOpenTs = now;
193
+ try {
194
+ execFileSync("open", ["x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"], {
195
+ stdio: "ignore"
196
+ });
197
+ } catch {
198
+ }
199
+ }
200
+ function ensureAccessibilityPermission(addon) {
201
+ try {
202
+ if (addon.checkPermission()) {
203
+ return true;
204
+ }
205
+ } catch {
206
+ }
207
+ openMacAccessibilitySettings();
208
+ return false;
209
+ }
210
+ function getFocusedInputValue() {
211
+ if (process.platform !== "darwin") {
212
+ return null;
213
+ }
214
+ const addon = getNativeAddon();
215
+ if (!ensureAccessibilityPermission(addon)) {
216
+ return null;
217
+ }
218
+ try {
219
+ return addon.getFocusedInputValue();
220
+ } catch {
221
+ return null;
222
+ }
223
+ }
224
+ function getFocusedInputSelectedText() {
225
+ if (process.platform !== "darwin") {
226
+ return null;
227
+ }
228
+ const addon = getNativeAddon();
229
+ if (!ensureAccessibilityPermission(addon)) {
230
+ return null;
231
+ }
232
+ try {
233
+ return addon.getFocusedInputSelectedText();
234
+ } catch {
235
+ return null;
236
+ }
237
+ }
157
238
  export {
158
239
  checkKeyboardPermission,
159
240
  createGlobalKeyboardListener,
241
+ getFocusedInputSelectedText,
242
+ getFocusedInputValue,
160
243
  getGlobalKeyboardListener
161
244
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simen-keyboard-listener",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Native global keyboard listener for macOS and Windows",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -45,8 +45,8 @@
45
45
  "node-addon-api": "^8.0.0"
46
46
  },
47
47
  "optionalDependencies": {
48
- "@simen-keyboard-listener/darwin-arm64": "1.0.8",
49
- "@simen-keyboard-listener/win32-x64": "1.0.8"
48
+ "@simen-keyboard-listener/darwin-arm64": "1.0.10",
49
+ "@simen-keyboard-listener/win32-x64": "1.0.10"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/node": "^20.0.0",