poi-plugin-mcp 0.2.16 → 0.2.21

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/poi-input.js CHANGED
@@ -1,302 +1,366 @@
1
- const CANONICAL_WIDTH = 1200
2
- const CANONICAL_HEIGHT = 720
3
- const DEFAULT_CLICK_DELAY_MS = 10
4
- const DRAG_MOVE_STEPS = 6
5
- const MAX_TEXT_LENGTH = 256
6
-
7
- const SUPPORTED_BUTTONS = new Set(['left', 'middle', 'right'])
8
- const SUPPORTED_KEY_EVENTS = new Set(['keyDown', 'keyUp'])
9
- const SUPPORTED_KEYS = new Set([
10
- 'Backspace',
11
- 'Delete',
12
- 'End',
13
- 'Enter',
14
- 'Escape',
15
- 'Home',
16
- 'PageDown',
17
- 'PageUp',
18
- 'Space',
19
- 'Tab',
20
- 'ArrowDown',
21
- 'ArrowLeft',
22
- 'ArrowRight',
23
- 'ArrowUp',
24
- ])
25
-
26
- function createPoiInputProvider(options = {}) {
27
- const getStore = options.getStore || defaultGetStore
28
- const resolveWebContents =
29
- options.resolveWebContents || defaultResolveWebContents
30
- const delay = options.delay || defaultDelay
31
- const clickDelayMs = options.clickDelayMs == null
32
- ? DEFAULT_CLICK_DELAY_MS
33
- : options.clickDelayMs
34
-
35
- if (
36
- !Number.isInteger(clickDelayMs) ||
37
- clickDelayMs < 1 ||
38
- clickDelayMs > 100
39
- ) {
40
- throw new Error('clickDelayMs must be an integer from 1 to 100')
41
- }
42
-
43
- return async function performPoiInput(operation) {
44
- validateOperationObject(operation)
45
- const layout = readLayout(getStore, resolveWebContents)
46
-
47
- switch (operation.operation) {
48
- case 'click':
49
- validateClick(operation)
50
- await sendClick(layout, operation, delay, clickDelayMs)
51
- return 'click'
52
- case 'drag':
53
- validateDrag(operation)
54
- await sendDrag(layout, operation, delay)
55
- return 'drag'
56
- case 'key':
57
- validateKey(operation)
58
- await layout.webContents.sendInputEvent({
59
- type: operation.event,
60
- keyCode: operation.key,
61
- })
62
- return 'key'
63
- case 'text':
64
- validateText(operation)
65
- for (const character of operation.text) {
66
- await layout.webContents.sendInputEvent({
67
- type: 'char',
68
- keyCode: character,
69
- })
70
- }
71
- return 'text'
72
- default:
73
- throw new Error(`Unsupported input operation: ${String(operation.operation)}`)
74
- }
75
- }
76
- }
77
-
78
- function validateOperationObject(operation) {
79
- if (
80
- !operation ||
81
- typeof operation !== 'object' ||
82
- Array.isArray(operation)
83
- ) {
84
- throw new Error('Input must be one operation object')
85
- }
86
- }
87
-
88
- function readLayout(getStore, resolveWebContents) {
89
- const layout = getStore('layout.webview')
90
- if (!layout || !layout.ref) {
91
- throw new Error('Poi game WebView is not ready')
92
- }
93
- if (
94
- !Number.isFinite(layout.width) ||
95
- layout.width <= 0 ||
96
- !Number.isFinite(layout.height) ||
97
- layout.height <= 0
98
- ) {
99
- throw new Error('Poi game WebView dimensions must be positive finite numbers')
100
- }
101
- let webContents
102
- if (typeof layout.ref.getWebContents === 'function') {
103
- webContents = layout.ref.getWebContents()
104
- } else if (typeof layout.ref.getWebContentsId === 'function') {
105
- const webContentsId = layout.ref.getWebContentsId()
106
- if (!Number.isInteger(webContentsId) || webContentsId <= 0) {
107
- throw new Error('Poi game WebContents id is invalid')
108
- }
109
- webContents = resolveWebContents(webContentsId)
110
- } else {
111
- throw new Error('Poi game WebView is not ready')
112
- }
113
- if (!webContents || typeof webContents.sendInputEvent !== 'function') {
114
- throw new Error('Poi game WebContents is not ready')
115
- }
116
- return { ...layout, webContents }
117
- }
118
-
119
- function validateClick(operation) {
120
- assertExactFields(operation, ['operation', 'x', 'y', 'button'])
121
- if (!Number.isFinite(operation.x) || !Number.isFinite(operation.y)) {
122
- throw new Error('Click coordinates must be finite numbers')
123
- }
124
- if (
125
- operation.x < 0 ||
126
- operation.x >= CANONICAL_WIDTH ||
127
- operation.y < 0 ||
128
- operation.y >= CANONICAL_HEIGHT
129
- ) {
130
- throw new Error('Click coordinates must be within canonical bounds')
131
- }
132
- if (!SUPPORTED_BUTTONS.has(operation.button)) {
133
- throw new Error(`Unsupported mouse button: ${String(operation.button)}`)
134
- }
135
- }
136
-
137
- function validateDrag(operation) {
138
- assertExactFields(operation, [
139
- 'operation',
140
- 'fromX',
141
- 'fromY',
142
- 'toX',
143
- 'toY',
144
- 'durationMs',
145
- 'button',
146
- ])
147
- const coordinates = [
148
- operation.fromX,
149
- operation.fromY,
150
- operation.toX,
151
- operation.toY,
152
- ]
153
- if (coordinates.some((coordinate) => !Number.isFinite(coordinate))) {
154
- throw new Error('Drag coordinates must be finite numbers')
155
- }
156
- if (
157
- operation.fromX < 0 ||
158
- operation.fromX >= CANONICAL_WIDTH ||
159
- operation.toX < 0 ||
160
- operation.toX >= CANONICAL_WIDTH ||
161
- operation.fromY < 0 ||
162
- operation.fromY >= CANONICAL_HEIGHT ||
163
- operation.toY < 0 ||
164
- operation.toY >= CANONICAL_HEIGHT
165
- ) {
166
- throw new Error('Drag coordinates must be within canonical bounds')
167
- }
168
- if (!Number.isInteger(operation.durationMs)) {
169
- throw new Error('Drag durationMs must be an integer')
170
- }
171
- if (operation.durationMs < 50 || operation.durationMs > 2000) {
172
- throw new Error('Drag durationMs must be from 50 to 2000')
173
- }
174
- if (!SUPPORTED_BUTTONS.has(operation.button)) {
175
- throw new Error(`Unsupported mouse button: ${String(operation.button)}`)
176
- }
177
- }
178
-
179
- function validateKey(operation) {
180
- assertExactFields(operation, ['operation', 'event', 'key'])
181
- if (!SUPPORTED_KEY_EVENTS.has(operation.event)) {
182
- throw new Error(`Unsupported key event: ${String(operation.event)}`)
183
- }
184
- if (!SUPPORTED_KEYS.has(operation.key)) {
185
- throw new Error(`Unsupported key: ${String(operation.key)}`)
186
- }
187
- }
188
-
189
- function validateText(operation) {
190
- assertExactFields(operation, ['operation', 'text'])
191
- if (
192
- typeof operation.text !== 'string' ||
193
- operation.text.length === 0 ||
194
- operation.text.length > MAX_TEXT_LENGTH
195
- ) {
196
- throw new Error('Literal text must contain 1 to 256 characters')
197
- }
198
- if (/[\u0000-\u001f\u007f-\u009f]/u.test(operation.text)) {
199
- throw new Error('Literal text must contain printable characters only')
200
- }
201
- }
202
-
203
- function assertExactFields(operation, allowedFields) {
204
- const allowed = new Set(allowedFields)
205
- const unexpected = Object.keys(operation).find((field) => !allowed.has(field))
206
- if (unexpected) {
207
- throw new Error(`Unexpected field for ${operation.operation}: ${unexpected}`)
208
- }
209
- const missing = allowedFields.find((field) => !Object.hasOwn(operation, field))
210
- if (missing) {
211
- throw new Error(`Missing field for ${operation.operation}: ${missing}`)
212
- }
213
- }
214
-
215
- async function sendClick(layout, operation, delay, clickDelayMs) {
216
- const event = {
217
- x: Math.floor((operation.x * layout.width) / CANONICAL_WIDTH),
218
- y: Math.floor((operation.y * layout.height) / CANONICAL_HEIGHT),
219
- button: operation.button,
220
- clickCount: 1,
221
- }
222
-
223
- await layout.webContents.sendInputEvent({ type: 'mouseDown', ...event })
224
- try {
225
- await delay(clickDelayMs)
226
- } finally {
227
- await layout.webContents.sendInputEvent({ type: 'mouseUp', ...event })
228
- }
229
- }
230
-
231
- async function sendDrag(layout, operation, delay) {
232
- const start = {
233
- x: Math.floor((operation.fromX * layout.width) / CANONICAL_WIDTH),
234
- y: Math.floor((operation.fromY * layout.height) / CANONICAL_HEIGHT),
235
- }
236
- const destination = {
237
- x: Math.floor((operation.toX * layout.width) / CANONICAL_WIDTH),
238
- y: Math.floor((operation.toY * layout.height) / CANONICAL_HEIGHT),
239
- }
240
- const mouseButton = { button: operation.button }
241
-
242
- await layout.webContents.sendInputEvent({
243
- type: 'mouseMove',
244
- ...start,
245
- ...mouseButton,
246
- })
247
- try {
248
- await layout.webContents.sendInputEvent({
249
- type: 'mouseDown',
250
- ...start,
251
- ...mouseButton,
252
- clickCount: 1,
253
- })
254
- for (let step = 1; step <= DRAG_MOVE_STEPS; step += 1) {
255
- const elapsed = Math.round((operation.durationMs * step) / DRAG_MOVE_STEPS)
256
- const previousElapsed = Math.round(
257
- (operation.durationMs * (step - 1)) / DRAG_MOVE_STEPS,
258
- )
259
- await delay(elapsed - previousElapsed)
260
- await layout.webContents.sendInputEvent({
261
- type: 'mouseMove',
262
- x: Math.round(
263
- start.x + ((destination.x - start.x) * step) / DRAG_MOVE_STEPS,
264
- ),
265
- y: Math.round(
266
- start.y + ((destination.y - start.y) * step) / DRAG_MOVE_STEPS,
267
- ),
268
- ...mouseButton,
269
- })
270
- }
271
- } finally {
272
- await layout.webContents.sendInputEvent({
273
- type: 'mouseUp',
274
- ...destination,
275
- ...mouseButton,
276
- clickCount: 1,
277
- })
278
- }
279
- }
280
-
281
- function defaultDelay(milliseconds) {
282
- return new Promise((resolve) => setTimeout(resolve, milliseconds))
283
- }
284
-
285
- function defaultGetStore(path) {
286
- if (typeof window !== 'undefined' && typeof window.getStore === 'function') {
287
- return window.getStore(path)
288
- }
289
- return null
290
- }
291
-
292
- function defaultResolveWebContents(webContentsId) {
293
- const { webContents } = require('@electron/remote')
294
- return webContents.fromId(webContentsId)
295
- }
296
-
297
- module.exports = {
298
- CANONICAL_HEIGHT,
299
- CANONICAL_WIDTH,
300
- MAX_TEXT_LENGTH,
301
- createPoiInputProvider,
302
- }
1
+ const CANONICAL_WIDTH = 1200
2
+ const CANONICAL_HEIGHT = 720
3
+ const DRAG_MOVE_STEPS = 6
4
+ const DRAG_RELEASE_DELAY_MS = 120
5
+ const MAX_TEXT_LENGTH = 256
6
+
7
+ const SUPPORTED_BUTTONS = new Set(['left', 'middle', 'right'])
8
+ const SUPPORTED_KEY_EVENTS = new Set(['keyDown', 'keyUp'])
9
+ const SUPPORTED_KEYS = new Set([
10
+ 'Backspace',
11
+ 'Delete',
12
+ 'End',
13
+ 'Enter',
14
+ 'Escape',
15
+ 'Home',
16
+ 'PageDown',
17
+ 'PageUp',
18
+ 'Space',
19
+ 'Tab',
20
+ 'ArrowDown',
21
+ 'ArrowLeft',
22
+ 'ArrowRight',
23
+ 'ArrowUp',
24
+ ])
25
+
26
+ function createPoiInputProvider(options = {}) {
27
+ const getStore = options.getStore || defaultGetStore
28
+ const resolveWebContents =
29
+ options.resolveWebContents || defaultResolveWebContents
30
+ const delay = options.delay || defaultDelay
31
+
32
+ return async function performPoiInput(operation) {
33
+ validateOperationObject(operation)
34
+ const layout = readLayout(getStore, resolveWebContents)
35
+ // The game's PIXI interaction manager drops synthetic pointer events while
36
+ // its document lacks focus (verified live 2026-08-17: clicks reached the
37
+ // canvas yet produced no game/API effect until a real OS-level click gave
38
+ // the WebView focus). Focus the hosting window first: webContents.focus()
39
+ // alone cannot give the document OS focus while the window is backgrounded.
40
+ focusGameWindow(layout.webContents)
41
+
42
+ switch (operation.operation) {
43
+ case 'move':
44
+ validateMove(operation)
45
+ sendMove(layout, operation)
46
+ return 'move'
47
+ case 'click':
48
+ validateClick(operation)
49
+ sendClick(layout, operation)
50
+ return 'click'
51
+ case 'drag':
52
+ validateDrag(operation)
53
+ await sendDrag(layout, operation, delay)
54
+ return 'drag'
55
+ case 'key':
56
+ validateKey(operation)
57
+ await layout.webContents.sendInputEvent({
58
+ type: operation.event,
59
+ keyCode: operation.key,
60
+ })
61
+ return 'key'
62
+ case 'text':
63
+ validateText(operation)
64
+ for (const character of operation.text) {
65
+ await layout.webContents.sendInputEvent({
66
+ type: 'char',
67
+ keyCode: character,
68
+ })
69
+ }
70
+ return 'text'
71
+ default:
72
+ throw new Error(`Unsupported input operation: ${String(operation.operation)}`)
73
+ }
74
+ }
75
+ }
76
+
77
+ function validateOperationObject(operation) {
78
+ if (
79
+ !operation ||
80
+ typeof operation !== 'object' ||
81
+ Array.isArray(operation)
82
+ ) {
83
+ throw new Error('Input must be one operation object')
84
+ }
85
+ }
86
+
87
+ function focusGameWindow(webContents) {
88
+ try {
89
+ const { BrowserWindow } = require('electron')
90
+ const win = BrowserWindow.fromWebContents(
91
+ webContents.hostWebContents || webContents,
92
+ )
93
+ if (win && !win.isFocused()) {
94
+ // show-no-activate keeps the user's foreground window; focus() then
95
+ // grants the document the OS focus the game's input layer requires.
96
+ if (win.isMinimized()) win.restore()
97
+ win.focus()
98
+ }
99
+ } catch (_windowError) {
100
+ // Fall through to webContents-level focus below.
101
+ }
102
+ try {
103
+ if (typeof webContents.focus === 'function') webContents.focus()
104
+ } catch (_focusError) {
105
+ // Input below is still delivered and may work without focus.
106
+ }
107
+ }
108
+
109
+ function readLayout(getStore, resolveWebContents) {
110
+ const layout = getStore('layout.webview')
111
+ if (!layout || !layout.ref) {
112
+ throw new Error('Poi game WebView is not ready')
113
+ }
114
+ if (
115
+ !Number.isFinite(layout.width) ||
116
+ layout.width <= 0 ||
117
+ !Number.isFinite(layout.height) ||
118
+ layout.height <= 0
119
+ ) {
120
+ throw new Error('Poi game WebView dimensions must be positive finite numbers')
121
+ }
122
+ let webContents
123
+ if (typeof layout.ref.getWebContents === 'function') {
124
+ webContents = layout.ref.getWebContents()
125
+ } else if (typeof layout.ref.getWebContentsId === 'function') {
126
+ const webContentsId = layout.ref.getWebContentsId()
127
+ if (!Number.isInteger(webContentsId) || webContentsId <= 0) {
128
+ throw new Error('Poi game WebContents id is invalid')
129
+ }
130
+ webContents = resolveWebContents(webContentsId)
131
+ } else {
132
+ throw new Error('Poi game WebView is not ready')
133
+ }
134
+ if (!webContents || typeof webContents.sendInputEvent !== 'function') {
135
+ throw new Error('Poi game WebContents is not ready')
136
+ }
137
+ return { ...layout, webContents }
138
+ }
139
+
140
+ function validateClick(operation) {
141
+ assertExactFields(operation, ['operation', 'x', 'y', 'button'])
142
+ if (!Number.isFinite(operation.x) || !Number.isFinite(operation.y)) {
143
+ throw new Error('Click coordinates must be finite numbers')
144
+ }
145
+ if (
146
+ operation.x < 0 ||
147
+ operation.x >= CANONICAL_WIDTH ||
148
+ operation.y < 0 ||
149
+ operation.y >= CANONICAL_HEIGHT
150
+ ) {
151
+ throw new Error('Click coordinates must be within canonical bounds')
152
+ }
153
+ if (!SUPPORTED_BUTTONS.has(operation.button)) {
154
+ throw new Error(`Unsupported mouse button: ${String(operation.button)}`)
155
+ }
156
+ }
157
+
158
+ function validateMove(operation) {
159
+ assertExactFields(operation, ['operation', 'x', 'y'])
160
+ if (!Number.isFinite(operation.x) || !Number.isFinite(operation.y)) {
161
+ throw new Error('Move coordinates must be finite numbers')
162
+ }
163
+ if (
164
+ operation.x < 0 ||
165
+ operation.x >= CANONICAL_WIDTH ||
166
+ operation.y < 0 ||
167
+ operation.y >= CANONICAL_HEIGHT
168
+ ) {
169
+ throw new Error('Move coordinates must be within canonical bounds')
170
+ }
171
+ }
172
+
173
+ function validateDrag(operation) {
174
+ assertExactFields(operation, [
175
+ 'operation',
176
+ 'fromX',
177
+ 'fromY',
178
+ 'toX',
179
+ 'toY',
180
+ 'durationMs',
181
+ 'button',
182
+ ])
183
+ const coordinates = [
184
+ operation.fromX,
185
+ operation.fromY,
186
+ operation.toX,
187
+ operation.toY,
188
+ ]
189
+ if (coordinates.some((coordinate) => !Number.isFinite(coordinate))) {
190
+ throw new Error('Drag coordinates must be finite numbers')
191
+ }
192
+ if (
193
+ operation.fromX < 0 ||
194
+ operation.fromX >= CANONICAL_WIDTH ||
195
+ operation.toX < 0 ||
196
+ operation.toX >= CANONICAL_WIDTH ||
197
+ operation.fromY < 0 ||
198
+ operation.fromY >= CANONICAL_HEIGHT ||
199
+ operation.toY < 0 ||
200
+ operation.toY >= CANONICAL_HEIGHT
201
+ ) {
202
+ throw new Error('Drag coordinates must be within canonical bounds')
203
+ }
204
+ if (!Number.isInteger(operation.durationMs)) {
205
+ throw new Error('Drag durationMs must be an integer')
206
+ }
207
+ if (operation.durationMs < 50 || operation.durationMs > 2000) {
208
+ throw new Error('Drag durationMs must be from 50 to 2000')
209
+ }
210
+ if (!SUPPORTED_BUTTONS.has(operation.button)) {
211
+ throw new Error(`Unsupported mouse button: ${String(operation.button)}`)
212
+ }
213
+ }
214
+
215
+ function validateKey(operation) {
216
+ assertExactFields(operation, ['operation', 'event', 'key'])
217
+ if (!SUPPORTED_KEY_EVENTS.has(operation.event)) {
218
+ throw new Error(`Unsupported key event: ${String(operation.event)}`)
219
+ }
220
+ if (!SUPPORTED_KEYS.has(operation.key)) {
221
+ throw new Error(`Unsupported key: ${String(operation.key)}`)
222
+ }
223
+ }
224
+
225
+ function validateText(operation) {
226
+ assertExactFields(operation, ['operation', 'text'])
227
+ if (
228
+ typeof operation.text !== 'string' ||
229
+ operation.text.length === 0 ||
230
+ operation.text.length > MAX_TEXT_LENGTH
231
+ ) {
232
+ throw new Error('Literal text must contain 1 to 256 characters')
233
+ }
234
+ if (/[\u0000-\u001f\u007f-\u009f]/u.test(operation.text)) {
235
+ throw new Error('Literal text must contain printable characters only')
236
+ }
237
+ }
238
+
239
+ function assertExactFields(operation, allowedFields) {
240
+ const allowed = new Set(allowedFields)
241
+ const unexpected = Object.keys(operation).find((field) => !allowed.has(field))
242
+ if (unexpected) {
243
+ throw new Error(`Unexpected field for ${operation.operation}: ${unexpected}`)
244
+ }
245
+ const missing = allowedFields.find((field) => !Object.hasOwn(operation, field))
246
+ if (missing) {
247
+ throw new Error(`Missing field for ${operation.operation}: ${missing}`)
248
+ }
249
+ }
250
+
251
+ function sendMove(layout, operation) {
252
+ sendPointerEnterAndMove(layout.webContents, {
253
+ x: Math.floor((operation.x * layout.width) / CANONICAL_WIDTH),
254
+ y: Math.floor((operation.y * layout.height) / CANONICAL_HEIGHT),
255
+ })
256
+ }
257
+
258
+ function sendClick(layout, operation) {
259
+ const point = {
260
+ x: Math.floor((operation.x * layout.width) / CANONICAL_WIDTH),
261
+ y: Math.floor((operation.y * layout.height) / CANONICAL_HEIGHT),
262
+ button: operation.button,
263
+ }
264
+
265
+ // Match poi-plugin-kancolle-assistant's long-running browser click sequence.
266
+ layout.webContents.sendInputEvent({ type: 'mouseMove', ...point })
267
+ layout.webContents.sendInputEvent({
268
+ type: 'mouseDown',
269
+ ...point,
270
+ clickCount: 3,
271
+ })
272
+ layout.webContents.sendInputEvent({ type: 'mouseUp', ...point, clickCount: 3 })
273
+ layout.webContents.sendInputEvent({
274
+ type: 'mouseMove',
275
+ x: 0,
276
+ y: 0,
277
+ button: operation.button,
278
+ })
279
+ }
280
+
281
+ async function sendDrag(layout, operation, delay) {
282
+ const start = {
283
+ x: Math.floor((operation.fromX * layout.width) / CANONICAL_WIDTH),
284
+ y: Math.floor((operation.fromY * layout.height) / CANONICAL_HEIGHT),
285
+ }
286
+ const destination = {
287
+ x: Math.floor((operation.toX * layout.width) / CANONICAL_WIDTH),
288
+ y: Math.floor((operation.toY * layout.height) / CANONICAL_HEIGHT),
289
+ }
290
+ const mouseButton = { button: operation.button }
291
+
292
+ sendPointerEnterAndMove(layout.webContents, start)
293
+ try {
294
+ await layout.webContents.sendInputEvent({
295
+ type: 'mouseDown',
296
+ ...start,
297
+ ...mouseButton,
298
+ clickCount: 1,
299
+ })
300
+ let previousX = start.x
301
+ let previousY = start.y
302
+ for (let step = 1; step <= DRAG_MOVE_STEPS; step += 1) {
303
+ const elapsed = Math.round((operation.durationMs * step) / DRAG_MOVE_STEPS)
304
+ const previousElapsed = Math.round(
305
+ (operation.durationMs * (step - 1)) / DRAG_MOVE_STEPS,
306
+ )
307
+ await delay(elapsed - previousElapsed)
308
+ const x = Math.round(
309
+ start.x + ((destination.x - start.x) * step) / DRAG_MOVE_STEPS,
310
+ )
311
+ const y = Math.round(
312
+ start.y + ((destination.y - start.y) * step) / DRAG_MOVE_STEPS,
313
+ )
314
+ await layout.webContents.sendInputEvent({
315
+ type: 'mouseMove',
316
+ x,
317
+ y,
318
+ movementX: x - previousX,
319
+ movementY: y - previousY,
320
+ ...mouseButton,
321
+ })
322
+ previousX = x
323
+ previousY = y
324
+ }
325
+ // The game's drag targets (e.g. the organization fleet tab combine drag)
326
+ // require a short hold at the destination before the release; releasing
327
+ // in the same tick as the last move drops the gesture (verified against
328
+ // poi-plugin-kancolle-assistant's mouseMove drag implementation).
329
+ await delay(DRAG_RELEASE_DELAY_MS)
330
+ } finally {
331
+ await layout.webContents.sendInputEvent({
332
+ type: 'mouseUp',
333
+ ...destination,
334
+ ...mouseButton,
335
+ clickCount: 1,
336
+ })
337
+ }
338
+ }
339
+
340
+ function sendPointerEnterAndMove(webContents, point) {
341
+ webContents.sendInputEvent({ type: 'mouseEnter', x: point.x, y: point.y })
342
+ webContents.sendInputEvent({ type: 'mouseMove', x: point.x, y: point.y })
343
+ }
344
+
345
+ function defaultDelay(milliseconds) {
346
+ return new Promise((resolve) => setTimeout(resolve, milliseconds))
347
+ }
348
+
349
+ function defaultGetStore(path) {
350
+ if (typeof window !== 'undefined' && typeof window.getStore === 'function') {
351
+ return window.getStore(path)
352
+ }
353
+ return null
354
+ }
355
+
356
+ function defaultResolveWebContents(webContentsId) {
357
+ const { webContents } = require('@electron/remote')
358
+ return webContents.fromId(webContentsId)
359
+ }
360
+
361
+ module.exports = {
362
+ CANONICAL_HEIGHT,
363
+ CANONICAL_WIDTH,
364
+ MAX_TEXT_LENGTH,
365
+ createPoiInputProvider,
366
+ }