@lvce-editor/title-bar-worker 1.0.0 → 1.1.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.
@@ -1,368 +1,161 @@
1
- const Two = '2.0';
2
- const create$4 = (method, params) => {
3
- return {
4
- jsonrpc: Two,
5
- method,
6
- params
7
- };
8
- };
9
- const state$1 = {
10
- callbacks: Object.create(null)
11
- };
12
- const set = (id, fn) => {
13
- state$1.callbacks[id] = fn;
14
- };
15
- const get = id => {
16
- return state$1.callbacks[id];
17
- };
18
- const remove = id => {
19
- delete state$1.callbacks[id];
20
- };
21
- let id = 0;
22
- const create$3 = () => {
23
- return ++id;
1
+ const normalizeLine = line => {
2
+ if (line.startsWith('Error: ')) {
3
+ return line.slice('Error: '.length);
4
+ }
5
+ if (line.startsWith('VError: ')) {
6
+ return line.slice('VError: '.length);
7
+ }
8
+ return line;
24
9
  };
25
- const warn = (...args) => {
26
- console.warn(...args);
10
+ const getCombinedMessage = (error, message) => {
11
+ const stringifiedError = normalizeLine(`${error}`);
12
+ if (message) {
13
+ return `${message}: ${stringifiedError}`;
14
+ }
15
+ return stringifiedError;
27
16
  };
28
- const registerPromise = () => {
29
- const id = create$3();
30
- const {
31
- resolve,
32
- promise
33
- } = Promise.withResolvers();
34
- set(id, resolve);
35
- return {
36
- id,
37
- promise
38
- };
17
+ const NewLine$2 = '\n';
18
+ const getNewLineIndex$1 = (string, startIndex = undefined) => {
19
+ return string.indexOf(NewLine$2, startIndex);
39
20
  };
40
- const resolve = (id, response) => {
41
- const fn = get(id);
42
- if (!fn) {
43
- console.log(response);
44
- warn(`callback ${id} may already be disposed`);
45
- return;
21
+ const mergeStacks = (parent, child) => {
22
+ if (!child) {
23
+ return parent;
46
24
  }
47
- fn(response);
48
- remove(id);
49
- };
50
- const create$2 = (method, params) => {
51
- const {
52
- id,
53
- promise
54
- } = registerPromise();
55
- const message = {
56
- jsonrpc: Two,
57
- method,
58
- params,
59
- id
60
- };
61
- return {
62
- message,
63
- promise
64
- };
25
+ const parentNewLineIndex = getNewLineIndex$1(parent);
26
+ const childNewLineIndex = getNewLineIndex$1(child);
27
+ if (childNewLineIndex === -1) {
28
+ return parent;
29
+ }
30
+ const parentFirstLine = parent.slice(0, parentNewLineIndex);
31
+ const childRest = child.slice(childNewLineIndex);
32
+ const childFirstLine = normalizeLine(child.slice(0, childNewLineIndex));
33
+ if (parentFirstLine.includes(childFirstLine)) {
34
+ return parentFirstLine + childRest;
35
+ }
36
+ return child;
65
37
  };
66
- class JsonRpcError extends Error {
38
+ class VError extends Error {
39
+ constructor(error, message) {
40
+ const combinedMessage = getCombinedMessage(error, message);
41
+ super(combinedMessage);
42
+ this.name = 'VError';
43
+ if (error instanceof Error) {
44
+ this.stack = mergeStacks(this.stack, error.stack);
45
+ }
46
+ if (error.codeFrame) {
47
+ // @ts-ignore
48
+ this.codeFrame = error.codeFrame;
49
+ }
50
+ if (error.code) {
51
+ // @ts-ignore
52
+ this.code = error.code;
53
+ }
54
+ }
55
+ }
56
+
57
+ class AssertionError extends Error {
67
58
  constructor(message) {
68
59
  super(message);
69
- this.name = 'JsonRpcError';
60
+ this.name = 'AssertionError';
70
61
  }
71
62
  }
72
- const NewLine$2 = '\n';
73
- const DomException = 'DOMException';
74
- const ReferenceError$1 = 'ReferenceError';
75
- const SyntaxError$1 = 'SyntaxError';
76
- const TypeError$1 = 'TypeError';
77
- const getErrorConstructor = (message, type) => {
78
- if (type) {
79
- switch (type) {
80
- case DomException:
81
- return DOMException;
82
- case TypeError$1:
83
- return TypeError;
84
- case SyntaxError$1:
85
- return SyntaxError;
86
- case ReferenceError$1:
87
- return ReferenceError;
88
- default:
89
- return Error;
90
- }
91
- }
92
- if (message.startsWith('TypeError: ')) {
93
- return TypeError;
63
+ const getType = value => {
64
+ switch (typeof value) {
65
+ case 'number':
66
+ return 'number';
67
+ case 'function':
68
+ return 'function';
69
+ case 'string':
70
+ return 'string';
71
+ case 'object':
72
+ if (value === null) {
73
+ return 'null';
74
+ }
75
+ if (Array.isArray(value)) {
76
+ return 'array';
77
+ }
78
+ return 'object';
79
+ case 'boolean':
80
+ return 'boolean';
81
+ default:
82
+ return 'unknown';
94
83
  }
95
- if (message.startsWith('SyntaxError: ')) {
96
- return SyntaxError;
84
+ };
85
+ const object = value => {
86
+ const type = getType(value);
87
+ if (type !== 'object') {
88
+ throw new AssertionError('expected value to be of type object');
97
89
  }
98
- if (message.startsWith('ReferenceError: ')) {
99
- return ReferenceError;
90
+ };
91
+ const number = value => {
92
+ const type = getType(value);
93
+ if (type !== 'number') {
94
+ throw new AssertionError('expected value to be of type number');
100
95
  }
101
- return Error;
102
96
  };
103
- const constructError = (message, type, name) => {
104
- const ErrorConstructor = getErrorConstructor(message, type);
105
- if (ErrorConstructor === DOMException && name) {
106
- return new ErrorConstructor(message, name);
97
+ const string = value => {
98
+ const type = getType(value);
99
+ if (type !== 'string') {
100
+ throw new AssertionError('expected value to be of type string');
107
101
  }
108
- if (ErrorConstructor === Error) {
109
- const error = new Error(message);
110
- if (name && name !== 'VError') {
111
- error.name = name;
112
- }
113
- return error;
102
+ };
103
+ const boolean = value => {
104
+ const type = getType(value);
105
+ if (type !== 'boolean') {
106
+ throw new AssertionError('expected value to be of type boolean');
114
107
  }
115
- return new ErrorConstructor(message);
116
108
  };
117
- const getNewLineIndex$1 = (string, startIndex = undefined) => {
118
- return string.indexOf(NewLine$2, startIndex);
109
+
110
+ const isMessagePort = value => {
111
+ return value && value instanceof MessagePort;
119
112
  };
120
- const getParentStack = error => {
121
- let parentStack = error.stack || error.data || error.message || '';
122
- if (parentStack.startsWith(' at')) {
123
- parentStack = error.message + NewLine$2 + parentStack;
124
- }
125
- return parentStack;
113
+ const isMessagePortMain = value => {
114
+ return value && value.constructor && value.constructor.name === 'MessagePortMain';
126
115
  };
127
- const joinLines$1 = lines => {
128
- return lines.join(NewLine$2);
116
+ const isOffscreenCanvas = value => {
117
+ return typeof OffscreenCanvas !== 'undefined' && value instanceof OffscreenCanvas;
129
118
  };
130
- const MethodNotFound = -32601;
131
- const Custom = -32001;
132
- const splitLines$1 = lines => {
133
- return lines.split(NewLine$2);
119
+ const isInstanceOf = (value, constructorName) => {
120
+ return value?.constructor?.name === constructorName;
134
121
  };
135
- const restoreJsonRpcError = error => {
136
- if (error && error instanceof Error) {
137
- return error;
138
- }
139
- const currentStack = joinLines$1(splitLines$1(new Error().stack || '').slice(1));
140
- if (error && error.code && error.code === MethodNotFound) {
141
- const restoredError = new JsonRpcError(error.message);
142
- const parentStack = getParentStack(error);
143
- restoredError.stack = parentStack + NewLine$2 + currentStack;
144
- return restoredError;
145
- }
146
- if (error && error.message) {
147
- const restoredError = constructError(error.message, error.type, error.name);
148
- if (error.data) {
149
- if (error.data.stack && error.data.type && error.message) {
150
- restoredError.stack = error.data.type + ': ' + error.message + NewLine$2 + error.data.stack + NewLine$2 + currentStack;
151
- } else if (error.data.stack) {
152
- restoredError.stack = error.data.stack;
153
- }
154
- if (error.data.codeFrame) {
155
- // @ts-ignore
156
- restoredError.codeFrame = error.data.codeFrame;
157
- }
158
- if (error.data.code) {
159
- // @ts-ignore
160
- restoredError.code = error.data.code;
161
- }
162
- if (error.data.type) {
163
- // @ts-ignore
164
- restoredError.name = error.data.type;
165
- }
166
- } else {
167
- if (error.stack) {
168
- const lowerStack = restoredError.stack || '';
169
- // @ts-ignore
170
- const indexNewLine = getNewLineIndex$1(lowerStack);
171
- const parentStack = getParentStack(error);
172
- // @ts-ignore
173
- restoredError.stack = parentStack + lowerStack.slice(indexNewLine);
174
- }
175
- if (error.codeFrame) {
176
- // @ts-ignore
177
- restoredError.codeFrame = error.codeFrame;
178
- }
122
+ const isSocket = value => {
123
+ return isInstanceOf(value, 'Socket');
124
+ };
125
+ const transferrables = [isMessagePort, isMessagePortMain, isOffscreenCanvas, isSocket];
126
+ const isTransferrable = value => {
127
+ for (const fn of transferrables) {
128
+ if (fn(value)) {
129
+ return true;
179
130
  }
180
- return restoredError;
181
- }
182
- if (typeof error === 'string') {
183
- return new Error(`JsonRpc Error: ${error}`);
184
131
  }
185
- return new Error(`JsonRpc Error: ${error}`);
132
+ return false;
186
133
  };
187
- const unwrapJsonRpcResult = responseMessage => {
188
- if ('error' in responseMessage) {
189
- const restoredError = restoreJsonRpcError(responseMessage.error);
190
- throw restoredError;
191
- }
192
- if ('result' in responseMessage) {
193
- return responseMessage.result;
194
- }
195
- throw new JsonRpcError('unexpected response message');
196
- };
197
- const E_COMMAND_NOT_FOUND = 'E_COMMAND_NOT_FOUND';
198
- const getErrorType = prettyError => {
199
- if (prettyError && prettyError.type) {
200
- return prettyError.type;
201
- }
202
- if (prettyError && prettyError.constructor && prettyError.constructor.name) {
203
- return prettyError.constructor.name;
204
- }
205
- return undefined;
206
- };
207
- const getErrorProperty = (error, prettyError) => {
208
- if (error && error.code === E_COMMAND_NOT_FOUND) {
209
- return {
210
- code: MethodNotFound,
211
- message: error.message,
212
- data: error.stack
213
- };
214
- }
215
- return {
216
- code: Custom,
217
- message: prettyError.message,
218
- data: {
219
- stack: prettyError.stack,
220
- codeFrame: prettyError.codeFrame,
221
- type: getErrorType(prettyError),
222
- code: prettyError.code,
223
- name: prettyError.name
224
- }
225
- };
226
- };
227
- const create$1$1 = (message, error) => {
228
- return {
229
- jsonrpc: Two,
230
- id: message.id,
231
- error
232
- };
233
- };
234
- const getErrorResponse = (message, error, preparePrettyError, logError) => {
235
- const prettyError = preparePrettyError(error);
236
- logError(error, prettyError);
237
- const errorProperty = getErrorProperty(error, prettyError);
238
- return create$1$1(message, errorProperty);
239
- };
240
- const create$5 = (message, result) => {
241
- return {
242
- jsonrpc: Two,
243
- id: message.id,
244
- result: result ?? null
245
- };
246
- };
247
- const getSuccessResponse = (message, result) => {
248
- const resultProperty = result ?? null;
249
- return create$5(message, resultProperty);
250
- };
251
- const getResponse = async (message, ipc, execute, preparePrettyError, logError, requiresSocket) => {
252
- try {
253
- const result = requiresSocket(message.method) ? await execute(message.method, ipc, ...message.params) : await execute(message.method, ...message.params);
254
- return getSuccessResponse(message, result);
255
- } catch (error) {
256
- return getErrorResponse(message, error, preparePrettyError, logError);
134
+ const walkValue = (value, transferrables, isTransferrable) => {
135
+ if (!value) {
136
+ return;
257
137
  }
258
- };
259
- const defaultPreparePrettyError = error => {
260
- return error;
261
- };
262
- const defaultLogError = () => {
263
- // ignore
264
- };
265
- const defaultRequiresSocket = () => {
266
- return false;
267
- };
268
- const defaultResolve = resolve;
269
-
270
- // TODO maybe remove this in v6 or v7, only accept options object to simplify the code
271
- const normalizeParams = args => {
272
- if (args.length === 1) {
273
- const options = args[0];
274
- return {
275
- ipc: options.ipc,
276
- message: options.message,
277
- execute: options.execute,
278
- resolve: options.resolve || defaultResolve,
279
- preparePrettyError: options.preparePrettyError || defaultPreparePrettyError,
280
- logError: options.logError || defaultLogError,
281
- requiresSocket: options.requiresSocket || defaultRequiresSocket
282
- };
138
+ if (isTransferrable(value)) {
139
+ transferrables.push(value);
140
+ return;
283
141
  }
284
- return {
285
- ipc: args[0],
286
- message: args[1],
287
- execute: args[2],
288
- resolve: args[3],
289
- preparePrettyError: args[4],
290
- logError: args[5],
291
- requiresSocket: args[6]
292
- };
293
- };
294
- const handleJsonRpcMessage = async (...args) => {
295
- const options = normalizeParams(args);
296
- const {
297
- message,
298
- ipc,
299
- execute,
300
- resolve,
301
- preparePrettyError,
302
- logError,
303
- requiresSocket
304
- } = options;
305
- if ('id' in message) {
306
- if ('method' in message) {
307
- const response = await getResponse(message, ipc, execute, preparePrettyError, logError, requiresSocket);
308
- try {
309
- ipc.send(response);
310
- } catch (error) {
311
- const errorResponse = getErrorResponse(message, error, preparePrettyError, logError);
312
- ipc.send(errorResponse);
313
- }
314
- return;
142
+ if (Array.isArray(value)) {
143
+ for (const item of value) {
144
+ walkValue(item, transferrables, isTransferrable);
315
145
  }
316
- resolve(message.id, message);
317
146
  return;
318
147
  }
319
- if ('method' in message) {
320
- await getResponse(message, ipc, execute, preparePrettyError, logError, requiresSocket);
148
+ if (typeof value === 'object') {
149
+ for (const property of Object.values(value)) {
150
+ walkValue(property, transferrables, isTransferrable);
151
+ }
321
152
  return;
322
153
  }
323
- throw new JsonRpcError('unexpected message');
324
- };
325
- const invokeHelper = async (ipc, method, params, useSendAndTransfer) => {
326
- const {
327
- message,
328
- promise
329
- } = create$2(method, params);
330
- if (useSendAndTransfer && ipc.sendAndTransfer) {
331
- ipc.sendAndTransfer(message);
332
- } else {
333
- ipc.send(message);
334
- }
335
- const responseMessage = await promise;
336
- return unwrapJsonRpcResult(responseMessage);
337
- };
338
- const send = (transport, method, ...params) => {
339
- const message = create$4(method, params);
340
- transport.send(message);
341
- };
342
- const invoke = (ipc, method, ...params) => {
343
- return invokeHelper(ipc, method, params, false);
344
- };
345
- const invokeAndTransfer = (ipc, method, ...params) => {
346
- return invokeHelper(ipc, method, params, true);
347
- };
348
-
349
- const commands = Object.create(null);
350
- const register = commandMap => {
351
- Object.assign(commands, commandMap);
352
- };
353
- const getCommand = key => {
354
- return commands[key];
355
- };
356
- const execute = (command, ...args) => {
357
- const fn = getCommand(command);
358
- if (!fn) {
359
- throw new Error(`command not found ${command}`);
360
- }
361
- return fn(...args);
362
154
  };
363
-
364
- const getData$1 = event => {
365
- return event.data;
155
+ const getTransferrables = value => {
156
+ const transferrables = [];
157
+ walkValue(value, transferrables, isTransferrable);
158
+ return transferrables;
366
159
  };
367
160
  const attachEvents = that => {
368
161
  const handleMessage = (...args) => {
@@ -384,108 +177,61 @@ class Ipc extends EventTarget {
384
177
  attachEvents(this);
385
178
  }
386
179
  }
387
- const readyMessage = 'ready';
388
- const walkValue = (value, transferrables, isTransferrable) => {
389
- if (!value) {
390
- return;
391
- }
392
- if (isTransferrable(value)) {
393
- transferrables.push(value);
394
- return;
395
- }
396
- if (Array.isArray(value)) {
397
- for (const item of value) {
398
- walkValue(item, transferrables, isTransferrable);
399
- }
400
- return;
180
+ const E_INCOMPATIBLE_NATIVE_MODULE = 'E_INCOMPATIBLE_NATIVE_MODULE';
181
+ const E_MODULES_NOT_SUPPORTED_IN_ELECTRON = 'E_MODULES_NOT_SUPPORTED_IN_ELECTRON';
182
+ const ERR_MODULE_NOT_FOUND = 'ERR_MODULE_NOT_FOUND';
183
+ const NewLine$1 = '\n';
184
+ const joinLines$1 = lines => {
185
+ return lines.join(NewLine$1);
186
+ };
187
+ const RE_AT = /^\s+at/;
188
+ const RE_AT_PROMISE_INDEX = /^\s*at async Promise.all \(index \d+\)$/;
189
+ const isNormalStackLine = line => {
190
+ return RE_AT.test(line) && !RE_AT_PROMISE_INDEX.test(line);
191
+ };
192
+ const getDetails = lines => {
193
+ const index = lines.findIndex(isNormalStackLine);
194
+ if (index === -1) {
195
+ return {
196
+ actualMessage: joinLines$1(lines),
197
+ rest: []
198
+ };
401
199
  }
402
- if (typeof value === 'object') {
403
- for (const property of Object.values(value)) {
404
- walkValue(property, transferrables, isTransferrable);
200
+ let lastIndex = index - 1;
201
+ while (++lastIndex < lines.length) {
202
+ if (!isNormalStackLine(lines[lastIndex])) {
203
+ break;
405
204
  }
406
- return;
407
205
  }
206
+ return {
207
+ actualMessage: lines[index - 1],
208
+ rest: lines.slice(index, lastIndex)
209
+ };
408
210
  };
409
- const isMessagePort = value => {
410
- return value && value instanceof MessagePort;
211
+ const splitLines$1 = lines => {
212
+ return lines.split(NewLine$1);
411
213
  };
412
- const isMessagePortMain = value => {
413
- return value && value.constructor && value.constructor.name === 'MessagePortMain';
214
+ const RE_MESSAGE_CODE_BLOCK_START = /^Error: The module '.*'$/;
215
+ const RE_MESSAGE_CODE_BLOCK_END = /^\s* at/;
216
+ const isMessageCodeBlockStartIndex = line => {
217
+ return RE_MESSAGE_CODE_BLOCK_START.test(line);
414
218
  };
415
- const isOffscreenCanvas = value => {
416
- return typeof OffscreenCanvas !== 'undefined' && value instanceof OffscreenCanvas;
219
+ const isMessageCodeBlockEndIndex = line => {
220
+ return RE_MESSAGE_CODE_BLOCK_END.test(line);
417
221
  };
418
- const isInstanceOf = (value, constructorName) => {
419
- return value?.constructor?.name === constructorName;
222
+ const getMessageCodeBlock = stderr => {
223
+ const lines = splitLines$1(stderr);
224
+ const startIndex = lines.findIndex(isMessageCodeBlockStartIndex);
225
+ const endIndex = startIndex + lines.slice(startIndex).findIndex(isMessageCodeBlockEndIndex, startIndex);
226
+ const relevantLines = lines.slice(startIndex, endIndex);
227
+ const relevantMessage = relevantLines.join(' ').slice('Error: '.length);
228
+ return relevantMessage;
420
229
  };
421
- const isSocket = value => {
422
- return isInstanceOf(value, 'Socket');
423
- };
424
- const transferrables = [isMessagePort, isMessagePortMain, isOffscreenCanvas, isSocket];
425
- const isTransferrable = value => {
426
- for (const fn of transferrables) {
427
- if (fn(value)) {
428
- return true;
429
- }
430
- }
431
- return false;
432
- };
433
- const getTransferrables = value => {
434
- const transferrables = [];
435
- walkValue(value, transferrables, isTransferrable);
436
- return transferrables;
437
- };
438
- const listen$2 = () => {
439
- // @ts-ignore
440
- if (typeof WorkerGlobalScope === 'undefined') {
441
- throw new TypeError('module is not in web worker scope');
442
- }
443
- return globalThis;
444
- };
445
- const signal$2 = global => {
446
- global.postMessage(readyMessage);
447
- };
448
- class IpcChildWithModuleWorker extends Ipc {
449
- getData(event) {
450
- return getData$1(event);
451
- }
452
- send(message) {
453
- // @ts-ignore
454
- this._rawIpc.postMessage(message);
455
- }
456
- sendAndTransfer(message) {
457
- const transfer = getTransferrables(message);
458
- // @ts-ignore
459
- this._rawIpc.postMessage(message, transfer);
460
- }
461
- dispose() {
462
- // ignore
463
- }
464
- onClose(callback) {
465
- // ignore
466
- }
467
- onMessage(callback) {
468
- this._rawIpc.addEventListener('message', callback);
469
- }
470
- }
471
- const wrap$5 = global => {
472
- return new IpcChildWithModuleWorker(global);
473
- };
474
- const E_INCOMPATIBLE_NATIVE_MODULE = 'E_INCOMPATIBLE_NATIVE_MODULE';
475
- const E_MODULES_NOT_SUPPORTED_IN_ELECTRON = 'E_MODULES_NOT_SUPPORTED_IN_ELECTRON';
476
- const ERR_MODULE_NOT_FOUND = 'ERR_MODULE_NOT_FOUND';
477
- const NewLine$1 = '\n';
478
- const joinLines = lines => {
479
- return lines.join(NewLine$1);
480
- };
481
- const splitLines = lines => {
482
- return lines.split(NewLine$1);
483
- };
484
- const isModuleNotFoundMessage = line => {
485
- return line.includes('[ERR_MODULE_NOT_FOUND]');
230
+ const isModuleNotFoundMessage = line => {
231
+ return line.includes('[ERR_MODULE_NOT_FOUND]');
486
232
  };
487
233
  const getModuleNotFoundError = stderr => {
488
- const lines = splitLines(stderr);
234
+ const lines = splitLines$1(stderr);
489
235
  const messageIndex = lines.findIndex(isModuleNotFoundMessage);
490
236
  const message = lines[messageIndex];
491
237
  return {
@@ -493,29 +239,23 @@ const getModuleNotFoundError = stderr => {
493
239
  code: ERR_MODULE_NOT_FOUND
494
240
  };
495
241
  };
242
+ const isModuleNotFoundError = stderr => {
243
+ if (!stderr) {
244
+ return false;
245
+ }
246
+ return stderr.includes('ERR_MODULE_NOT_FOUND');
247
+ };
248
+ const isModulesSyntaxError = stderr => {
249
+ if (!stderr) {
250
+ return false;
251
+ }
252
+ return stderr.includes('SyntaxError: Cannot use import statement outside a module');
253
+ };
496
254
  const RE_NATIVE_MODULE_ERROR = /^innerError Error: Cannot find module '.*.node'/;
497
255
  const RE_NATIVE_MODULE_ERROR_2 = /was compiled against a different Node.js version/;
498
- const RE_MESSAGE_CODE_BLOCK_START = /^Error: The module '.*'$/;
499
- const RE_MESSAGE_CODE_BLOCK_END = /^\s* at/;
500
- const RE_AT = /^\s+at/;
501
- const RE_AT_PROMISE_INDEX = /^\s*at async Promise.all \(index \d+\)$/;
502
256
  const isUnhelpfulNativeModuleError = stderr => {
503
257
  return RE_NATIVE_MODULE_ERROR.test(stderr) && RE_NATIVE_MODULE_ERROR_2.test(stderr);
504
258
  };
505
- const isMessageCodeBlockStartIndex = line => {
506
- return RE_MESSAGE_CODE_BLOCK_START.test(line);
507
- };
508
- const isMessageCodeBlockEndIndex = line => {
509
- return RE_MESSAGE_CODE_BLOCK_END.test(line);
510
- };
511
- const getMessageCodeBlock = stderr => {
512
- const lines = splitLines(stderr);
513
- const startIndex = lines.findIndex(isMessageCodeBlockStartIndex);
514
- const endIndex = startIndex + lines.slice(startIndex).findIndex(isMessageCodeBlockEndIndex, startIndex);
515
- const relevantLines = lines.slice(startIndex, endIndex);
516
- const relevantMessage = relevantLines.join(' ').slice('Error: '.length);
517
- return relevantMessage;
518
- };
519
259
  const getNativeModuleErrorMessage = stderr => {
520
260
  const message = getMessageCodeBlock(stderr);
521
261
  return {
@@ -523,46 +263,12 @@ const getNativeModuleErrorMessage = stderr => {
523
263
  code: E_INCOMPATIBLE_NATIVE_MODULE
524
264
  };
525
265
  };
526
- const isModulesSyntaxError = stderr => {
527
- if (!stderr) {
528
- return false;
529
- }
530
- return stderr.includes('SyntaxError: Cannot use import statement outside a module');
531
- };
532
266
  const getModuleSyntaxError = () => {
533
267
  return {
534
268
  message: `ES Modules are not supported in electron`,
535
269
  code: E_MODULES_NOT_SUPPORTED_IN_ELECTRON
536
270
  };
537
271
  };
538
- const isModuleNotFoundError = stderr => {
539
- if (!stderr) {
540
- return false;
541
- }
542
- return stderr.includes('ERR_MODULE_NOT_FOUND');
543
- };
544
- const isNormalStackLine = line => {
545
- return RE_AT.test(line) && !RE_AT_PROMISE_INDEX.test(line);
546
- };
547
- const getDetails = lines => {
548
- const index = lines.findIndex(isNormalStackLine);
549
- if (index === -1) {
550
- return {
551
- actualMessage: joinLines(lines),
552
- rest: []
553
- };
554
- }
555
- let lastIndex = index - 1;
556
- while (++lastIndex < lines.length) {
557
- if (!isNormalStackLine(lines[lastIndex])) {
558
- break;
559
- }
560
- }
561
- return {
562
- actualMessage: lines[index - 1],
563
- rest: lines.slice(index, lastIndex)
564
- };
565
- };
566
272
  const getHelpfulChildProcessError = (stdout, stderr) => {
567
273
  if (isUnhelpfulNativeModuleError(stderr)) {
568
274
  return getNativeModuleErrorMessage(stderr);
@@ -573,72 +279,17 @@ const getHelpfulChildProcessError = (stdout, stderr) => {
573
279
  if (isModuleNotFoundError(stderr)) {
574
280
  return getModuleNotFoundError(stderr);
575
281
  }
576
- const lines = splitLines(stderr);
282
+ const lines = splitLines$1(stderr);
577
283
  const {
578
284
  actualMessage,
579
285
  rest
580
286
  } = getDetails(lines);
581
287
  return {
582
- message: `${actualMessage}`,
288
+ message: actualMessage,
583
289
  code: '',
584
290
  stack: rest
585
291
  };
586
292
  };
587
- const normalizeLine = line => {
588
- if (line.startsWith('Error: ')) {
589
- return line.slice('Error: '.length);
590
- }
591
- if (line.startsWith('VError: ')) {
592
- return line.slice('VError: '.length);
593
- }
594
- return line;
595
- };
596
- const getCombinedMessage = (error, message) => {
597
- const stringifiedError = normalizeLine(`${error}`);
598
- if (message) {
599
- return `${message}: ${stringifiedError}`;
600
- }
601
- return stringifiedError;
602
- };
603
- const NewLine = '\n';
604
- const getNewLineIndex = (string, startIndex = undefined) => {
605
- return string.indexOf(NewLine, startIndex);
606
- };
607
- const mergeStacks = (parent, child) => {
608
- if (!child) {
609
- return parent;
610
- }
611
- const parentNewLineIndex = getNewLineIndex(parent);
612
- const childNewLineIndex = getNewLineIndex(child);
613
- if (childNewLineIndex === -1) {
614
- return parent;
615
- }
616
- const parentFirstLine = parent.slice(0, parentNewLineIndex);
617
- const childRest = child.slice(childNewLineIndex);
618
- const childFirstLine = normalizeLine(child.slice(0, childNewLineIndex));
619
- if (parentFirstLine.includes(childFirstLine)) {
620
- return parentFirstLine + childRest;
621
- }
622
- return child;
623
- };
624
- class VError extends Error {
625
- constructor(error, message) {
626
- const combinedMessage = getCombinedMessage(error, message);
627
- super(combinedMessage);
628
- this.name = 'VError';
629
- if (error instanceof Error) {
630
- this.stack = mergeStacks(this.stack, error.stack);
631
- }
632
- if (error.codeFrame) {
633
- // @ts-ignore
634
- this.codeFrame = error.codeFrame;
635
- }
636
- if (error.code) {
637
- // @ts-ignore
638
- this.code = error.code;
639
- }
640
- }
641
- }
642
293
  class IpcError extends VError {
643
294
  // @ts-ignore
644
295
  constructor(betterMessage, stdout = '', stderr = '') {
@@ -665,12 +316,53 @@ class IpcError extends VError {
665
316
  this.stderr = stderr;
666
317
  }
667
318
  }
319
+ const readyMessage = 'ready';
320
+ const getData$2 = event => {
321
+ return event.data;
322
+ };
323
+ const listen$7 = () => {
324
+ // @ts-ignore
325
+ if (typeof WorkerGlobalScope === 'undefined') {
326
+ throw new TypeError('module is not in web worker scope');
327
+ }
328
+ return globalThis;
329
+ };
330
+ const signal$8 = global => {
331
+ global.postMessage(readyMessage);
332
+ };
333
+ class IpcChildWithModuleWorker extends Ipc {
334
+ getData(event) {
335
+ return getData$2(event);
336
+ }
337
+ send(message) {
338
+ // @ts-ignore
339
+ this._rawIpc.postMessage(message);
340
+ }
341
+ sendAndTransfer(message) {
342
+ const transfer = getTransferrables(message);
343
+ // @ts-ignore
344
+ this._rawIpc.postMessage(message, transfer);
345
+ }
346
+ dispose() {
347
+ // ignore
348
+ }
349
+ onClose(callback) {
350
+ // ignore
351
+ }
352
+ onMessage(callback) {
353
+ this._rawIpc.addEventListener('message', callback);
354
+ }
355
+ }
356
+ const wrap$f = global => {
357
+ return new IpcChildWithModuleWorker(global);
358
+ };
668
359
  const withResolvers = () => {
669
360
  let _resolve;
670
361
  const promise = new Promise(resolve => {
671
362
  _resolve = resolve;
672
363
  });
673
364
  return {
365
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
674
366
  resolve: _resolve,
675
367
  promise
676
368
  };
@@ -687,10 +379,10 @@ const waitForFirstMessage = async port => {
687
379
  // @ts-ignore
688
380
  return event.data;
689
381
  };
690
- const listen$1$1 = async () => {
691
- const parentIpcRaw = listen$2();
692
- signal$2(parentIpcRaw);
693
- const parentIpc = wrap$5(parentIpcRaw);
382
+ const listen$6 = async () => {
383
+ const parentIpcRaw = listen$7();
384
+ signal$8(parentIpcRaw);
385
+ const parentIpc = wrap$f(parentIpcRaw);
694
386
  const firstMessage = await waitForFirstMessage(parentIpc);
695
387
  if (firstMessage.method !== 'initialize') {
696
388
  throw new IpcError('unexpected first message');
@@ -709,11 +401,8 @@ const listen$1$1 = async () => {
709
401
  return globalThis;
710
402
  };
711
403
  class IpcChildWithModuleWorkerAndMessagePort extends Ipc {
712
- constructor(port) {
713
- super(port);
714
- }
715
404
  getData(event) {
716
- return getData$1(event);
405
+ return getData$2(event);
717
406
  }
718
407
  send(message) {
719
408
  this._rawIpc.postMessage(message);
@@ -735,416 +424,1991 @@ class IpcChildWithModuleWorkerAndMessagePort extends Ipc {
735
424
  this._rawIpc.start();
736
425
  }
737
426
  }
738
- const wrap$4 = port => {
427
+ const wrap$e = port => {
739
428
  return new IpcChildWithModuleWorkerAndMessagePort(port);
740
429
  };
741
430
  const IpcChildWithModuleWorkerAndMessagePort$1 = {
742
431
  __proto__: null,
743
- listen: listen$1$1,
744
- wrap: wrap$4
432
+ listen: listen$6,
433
+ wrap: wrap$e
434
+ };
435
+
436
+ const Two = '2.0';
437
+ const create$4 = (method, params) => {
438
+ return {
439
+ jsonrpc: Two,
440
+ method,
441
+ params
442
+ };
443
+ };
444
+ const callbacks = Object.create(null);
445
+ const set = (id, fn) => {
446
+ callbacks[id] = fn;
447
+ };
448
+ const get = id => {
449
+ return callbacks[id];
450
+ };
451
+ const remove = id => {
452
+ delete callbacks[id];
453
+ };
454
+ let id$a = 0;
455
+ const create$3 = () => {
456
+ return ++id$a;
457
+ };
458
+ const registerPromise = () => {
459
+ const id = create$3();
460
+ const {
461
+ resolve,
462
+ promise
463
+ } = Promise.withResolvers();
464
+ set(id, resolve);
465
+ return {
466
+ id,
467
+ promise
468
+ };
469
+ };
470
+ const create$2 = (method, params) => {
471
+ const {
472
+ id,
473
+ promise
474
+ } = registerPromise();
475
+ const message = {
476
+ jsonrpc: Two,
477
+ method,
478
+ params,
479
+ id
480
+ };
481
+ return {
482
+ message,
483
+ promise
484
+ };
485
+ };
486
+ class JsonRpcError extends Error {
487
+ constructor(message) {
488
+ super(message);
489
+ this.name = 'JsonRpcError';
490
+ }
491
+ }
492
+ const NewLine = '\n';
493
+ const DomException = 'DOMException';
494
+ const ReferenceError$1 = 'ReferenceError';
495
+ const SyntaxError$1 = 'SyntaxError';
496
+ const TypeError$1 = 'TypeError';
497
+ const getErrorConstructor = (message, type) => {
498
+ if (type) {
499
+ switch (type) {
500
+ case DomException:
501
+ return DOMException;
502
+ case TypeError$1:
503
+ return TypeError;
504
+ case SyntaxError$1:
505
+ return SyntaxError;
506
+ case ReferenceError$1:
507
+ return ReferenceError;
508
+ default:
509
+ return Error;
510
+ }
511
+ }
512
+ if (message.startsWith('TypeError: ')) {
513
+ return TypeError;
514
+ }
515
+ if (message.startsWith('SyntaxError: ')) {
516
+ return SyntaxError;
517
+ }
518
+ if (message.startsWith('ReferenceError: ')) {
519
+ return ReferenceError;
520
+ }
521
+ return Error;
522
+ };
523
+ const constructError = (message, type, name) => {
524
+ const ErrorConstructor = getErrorConstructor(message, type);
525
+ if (ErrorConstructor === DOMException && name) {
526
+ return new ErrorConstructor(message, name);
527
+ }
528
+ if (ErrorConstructor === Error) {
529
+ const error = new Error(message);
530
+ if (name && name !== 'VError') {
531
+ error.name = name;
532
+ }
533
+ return error;
534
+ }
535
+ return new ErrorConstructor(message);
536
+ };
537
+ const getNewLineIndex = (string, startIndex = undefined) => {
538
+ return string.indexOf(NewLine, startIndex);
539
+ };
540
+ const getParentStack = error => {
541
+ let parentStack = error.stack || error.data || error.message || '';
542
+ if (parentStack.startsWith(' at')) {
543
+ parentStack = error.message + NewLine + parentStack;
544
+ }
545
+ return parentStack;
546
+ };
547
+ const joinLines = lines => {
548
+ return lines.join(NewLine);
549
+ };
550
+ const MethodNotFound = -32601;
551
+ const Custom = -32001;
552
+ const splitLines = lines => {
553
+ return lines.split(NewLine);
554
+ };
555
+ const restoreJsonRpcError = error => {
556
+ if (error && error instanceof Error) {
557
+ return error;
558
+ }
559
+ const currentStack = joinLines(splitLines(new Error().stack || '').slice(1));
560
+ if (error && error.code && error.code === MethodNotFound) {
561
+ const restoredError = new JsonRpcError(error.message);
562
+ const parentStack = getParentStack(error);
563
+ restoredError.stack = parentStack + NewLine + currentStack;
564
+ return restoredError;
565
+ }
566
+ if (error && error.message) {
567
+ const restoredError = constructError(error.message, error.type, error.name);
568
+ if (error.data) {
569
+ if (error.data.stack && error.data.type && error.message) {
570
+ restoredError.stack = error.data.type + ': ' + error.message + NewLine + error.data.stack + NewLine + currentStack;
571
+ } else if (error.data.stack) {
572
+ restoredError.stack = error.data.stack;
573
+ }
574
+ if (error.data.codeFrame) {
575
+ // @ts-ignore
576
+ restoredError.codeFrame = error.data.codeFrame;
577
+ }
578
+ if (error.data.code) {
579
+ // @ts-ignore
580
+ restoredError.code = error.data.code;
581
+ }
582
+ if (error.data.type) {
583
+ // @ts-ignore
584
+ restoredError.name = error.data.type;
585
+ }
586
+ } else {
587
+ if (error.stack) {
588
+ const lowerStack = restoredError.stack || '';
589
+ // @ts-ignore
590
+ const indexNewLine = getNewLineIndex(lowerStack);
591
+ const parentStack = getParentStack(error);
592
+ // @ts-ignore
593
+ restoredError.stack = parentStack + lowerStack.slice(indexNewLine);
594
+ }
595
+ if (error.codeFrame) {
596
+ // @ts-ignore
597
+ restoredError.codeFrame = error.codeFrame;
598
+ }
599
+ }
600
+ return restoredError;
601
+ }
602
+ if (typeof error === 'string') {
603
+ return new Error(`JsonRpc Error: ${error}`);
604
+ }
605
+ return new Error(`JsonRpc Error: ${error}`);
606
+ };
607
+ const unwrapJsonRpcResult = responseMessage => {
608
+ if ('error' in responseMessage) {
609
+ const restoredError = restoreJsonRpcError(responseMessage.error);
610
+ throw restoredError;
611
+ }
612
+ if ('result' in responseMessage) {
613
+ return responseMessage.result;
614
+ }
615
+ throw new JsonRpcError('unexpected response message');
616
+ };
617
+ const warn = (...args) => {
618
+ console.warn(...args);
619
+ };
620
+ const resolve = (id, response) => {
621
+ const fn = get(id);
622
+ if (!fn) {
623
+ console.log(response);
624
+ warn(`callback ${id} may already be disposed`);
625
+ return;
626
+ }
627
+ fn(response);
628
+ remove(id);
629
+ };
630
+ const E_COMMAND_NOT_FOUND = 'E_COMMAND_NOT_FOUND';
631
+ const getErrorType = prettyError => {
632
+ if (prettyError && prettyError.type) {
633
+ return prettyError.type;
634
+ }
635
+ if (prettyError && prettyError.constructor && prettyError.constructor.name) {
636
+ return prettyError.constructor.name;
637
+ }
638
+ return undefined;
639
+ };
640
+ const getErrorProperty = (error, prettyError) => {
641
+ if (error && error.code === E_COMMAND_NOT_FOUND) {
642
+ return {
643
+ code: MethodNotFound,
644
+ message: error.message,
645
+ data: error.stack
646
+ };
647
+ }
648
+ return {
649
+ code: Custom,
650
+ message: prettyError.message,
651
+ data: {
652
+ stack: prettyError.stack,
653
+ codeFrame: prettyError.codeFrame,
654
+ type: getErrorType(prettyError),
655
+ code: prettyError.code,
656
+ name: prettyError.name
657
+ }
658
+ };
659
+ };
660
+ const create$1$1 = (message, error) => {
661
+ return {
662
+ jsonrpc: Two,
663
+ id: message.id,
664
+ error
665
+ };
666
+ };
667
+ const getErrorResponse = (message, error, preparePrettyError, logError) => {
668
+ const prettyError = preparePrettyError(error);
669
+ logError(error, prettyError);
670
+ const errorProperty = getErrorProperty(error, prettyError);
671
+ return create$1$1(message, errorProperty);
672
+ };
673
+ const create$5 = (message, result) => {
674
+ return {
675
+ jsonrpc: Two,
676
+ id: message.id,
677
+ result: result ?? null
678
+ };
679
+ };
680
+ const getSuccessResponse = (message, result) => {
681
+ const resultProperty = result ?? null;
682
+ return create$5(message, resultProperty);
683
+ };
684
+ const getResponse = async (message, ipc, execute, preparePrettyError, logError, requiresSocket) => {
685
+ try {
686
+ const result = requiresSocket(message.method) ? await execute(message.method, ipc, ...message.params) : await execute(message.method, ...message.params);
687
+ return getSuccessResponse(message, result);
688
+ } catch (error) {
689
+ return getErrorResponse(message, error, preparePrettyError, logError);
690
+ }
691
+ };
692
+ const defaultPreparePrettyError = error => {
693
+ return error;
694
+ };
695
+ const defaultLogError = () => {
696
+ // ignore
697
+ };
698
+ const defaultRequiresSocket = () => {
699
+ return false;
700
+ };
701
+ const defaultResolve = resolve;
702
+
703
+ // TODO maybe remove this in v6 or v7, only accept options object to simplify the code
704
+ const normalizeParams = args => {
705
+ if (args.length === 1) {
706
+ const options = args[0];
707
+ return {
708
+ ipc: options.ipc,
709
+ message: options.message,
710
+ execute: options.execute,
711
+ resolve: options.resolve || defaultResolve,
712
+ preparePrettyError: options.preparePrettyError || defaultPreparePrettyError,
713
+ logError: options.logError || defaultLogError,
714
+ requiresSocket: options.requiresSocket || defaultRequiresSocket
715
+ };
716
+ }
717
+ return {
718
+ ipc: args[0],
719
+ message: args[1],
720
+ execute: args[2],
721
+ resolve: args[3],
722
+ preparePrettyError: args[4],
723
+ logError: args[5],
724
+ requiresSocket: args[6]
725
+ };
726
+ };
727
+ const handleJsonRpcMessage = async (...args) => {
728
+ const options = normalizeParams(args);
729
+ const {
730
+ message,
731
+ ipc,
732
+ execute,
733
+ resolve,
734
+ preparePrettyError,
735
+ logError,
736
+ requiresSocket
737
+ } = options;
738
+ if ('id' in message) {
739
+ if ('method' in message) {
740
+ const response = await getResponse(message, ipc, execute, preparePrettyError, logError, requiresSocket);
741
+ try {
742
+ ipc.send(response);
743
+ } catch (error) {
744
+ const errorResponse = getErrorResponse(message, error, preparePrettyError, logError);
745
+ ipc.send(errorResponse);
746
+ }
747
+ return;
748
+ }
749
+ resolve(message.id, message);
750
+ return;
751
+ }
752
+ if ('method' in message) {
753
+ await getResponse(message, ipc, execute, preparePrettyError, logError, requiresSocket);
754
+ return;
755
+ }
756
+ throw new JsonRpcError('unexpected message');
757
+ };
758
+ const invokeHelper = async (ipc, method, params, useSendAndTransfer) => {
759
+ const {
760
+ message,
761
+ promise
762
+ } = create$2(method, params);
763
+ if (useSendAndTransfer && ipc.sendAndTransfer) {
764
+ ipc.sendAndTransfer(message);
765
+ } else {
766
+ ipc.send(message);
767
+ }
768
+ const responseMessage = await promise;
769
+ return unwrapJsonRpcResult(responseMessage);
770
+ };
771
+ const send = (transport, method, ...params) => {
772
+ const message = create$4(method, params);
773
+ transport.send(message);
774
+ };
775
+ const invoke$1 = (ipc, method, ...params) => {
776
+ return invokeHelper(ipc, method, params, false);
777
+ };
778
+ const invokeAndTransfer = (ipc, method, ...params) => {
779
+ return invokeHelper(ipc, method, params, true);
780
+ };
781
+
782
+ const commands = Object.create(null);
783
+ const register = commandMap => {
784
+ Object.assign(commands, commandMap);
785
+ };
786
+ const getCommand = key => {
787
+ return commands[key];
788
+ };
789
+ const execute = (command, ...args) => {
790
+ const fn = getCommand(command);
791
+ if (!fn) {
792
+ throw new Error(`command not found ${command}`);
793
+ }
794
+ return fn(...args);
795
+ };
796
+
797
+ const createRpc = ipc => {
798
+ const rpc = {
799
+ // @ts-ignore
800
+ ipc,
801
+ /**
802
+ * @deprecated
803
+ */
804
+ send(method, ...params) {
805
+ send(ipc, method, ...params);
806
+ },
807
+ invoke(method, ...params) {
808
+ return invoke$1(ipc, method, ...params);
809
+ },
810
+ invokeAndTransfer(method, ...params) {
811
+ return invokeAndTransfer(ipc, method, ...params);
812
+ }
813
+ };
814
+ return rpc;
815
+ };
816
+ const requiresSocket = () => {
817
+ return false;
818
+ };
819
+ const preparePrettyError = error => {
820
+ return error;
821
+ };
822
+ const logError = () => {
823
+ // handled by renderer worker
824
+ };
825
+ const handleMessage = event => {
826
+ const actualRequiresSocket = event?.target?.requiresSocket || requiresSocket;
827
+ const actualExecute = event?.target?.execute || execute;
828
+ return handleJsonRpcMessage(event.target, event.data, actualExecute, resolve, preparePrettyError, logError, actualRequiresSocket);
829
+ };
830
+ const handleIpc = ipc => {
831
+ if ('addEventListener' in ipc) {
832
+ ipc.addEventListener('message', handleMessage);
833
+ } else if ('on' in ipc) {
834
+ // deprecated
835
+ ipc.on('message', handleMessage);
836
+ }
837
+ };
838
+ const listen$1 = async (module, options) => {
839
+ const rawIpc = await module.listen(options);
840
+ if (module.signal) {
841
+ module.signal(rawIpc);
842
+ }
843
+ const ipc = module.wrap(rawIpc);
844
+ return ipc;
845
+ };
846
+ const create$1 = async ({
847
+ commandMap
848
+ }) => {
849
+ // TODO create a commandMap per rpc instance
850
+ register(commandMap);
851
+ const ipc = await listen$1(IpcChildWithModuleWorkerAndMessagePort$1);
852
+ handleIpc(ipc);
853
+ const rpc = createRpc(ipc);
854
+ return rpc;
855
+ };
856
+ const WebWorkerRpcClient = {
857
+ __proto__: null,
858
+ create: create$1
859
+ };
860
+
861
+ const Enter = 3;
862
+ const Escape = 8;
863
+ const Space = 9;
864
+ const End = 255;
865
+ const Home = 12;
866
+ const LeftArrow = 13;
867
+ const UpArrow = 14;
868
+ const RightArrow = 15;
869
+ const DownArrow = 16;
870
+ const Delete = 18;
871
+ const KeyC = 31;
872
+ const KeyV = 50;
873
+ const F2 = 58;
874
+ const Star = 131;
875
+
876
+ const CtrlCmd = 1 << 11 >>> 0;
877
+ const Alt = 1 << 9 >>> 0;
878
+
879
+ const FocusExplorer = 13;
880
+ const FocusExplorerEditBox = 14;
881
+ const FocusTitleBarMenuBar = 26;
882
+
883
+ const getKeyBindings = () => {
884
+ return [{
885
+ key: RightArrow,
886
+ command: 'Explorer.handleArrowRight',
887
+ when: FocusExplorer
888
+ }, {
889
+ key: LeftArrow,
890
+ command: 'Explorer.handleArrowLeft',
891
+ when: FocusExplorer
892
+ }, {
893
+ key: Home,
894
+ command: 'Explorer.focusFirst',
895
+ when: FocusExplorer
896
+ }, {
897
+ key: End,
898
+ command: 'Explorer.focusLast',
899
+ when: FocusExplorer
900
+ }, {
901
+ key: UpArrow,
902
+ command: 'Explorer.focusPrevious',
903
+ when: FocusExplorer
904
+ }, {
905
+ key: DownArrow,
906
+ command: 'Explorer.focusNext',
907
+ when: FocusExplorer
908
+ }, {
909
+ key: CtrlCmd | Star,
910
+ command: 'Explorer.expandAll',
911
+ when: FocusExplorer
912
+ }, {
913
+ key: Alt | RightArrow,
914
+ command: 'Explorer.expandRecursively',
915
+ when: FocusExplorer
916
+ }, {
917
+ key: CtrlCmd | LeftArrow,
918
+ command: 'Explorer.collapseAll',
919
+ when: FocusExplorer
920
+ }, {
921
+ key: CtrlCmd | KeyV,
922
+ command: 'Explorer.handlePaste',
923
+ when: FocusExplorer
924
+ }, {
925
+ key: CtrlCmd | KeyC,
926
+ command: 'Explorer.handleCopy',
927
+ when: FocusExplorer
928
+ }, {
929
+ key: F2,
930
+ command: 'Explorer.rename',
931
+ when: FocusExplorer
932
+ }, {
933
+ key: Escape,
934
+ command: 'Explorer.cancelEdit',
935
+ when: FocusExplorerEditBox
936
+ }, {
937
+ key: Enter,
938
+ command: 'Explorer.acceptEdit',
939
+ when: FocusExplorerEditBox
940
+ }, {
941
+ key: Delete,
942
+ command: 'Explorer.removeDirent',
943
+ when: FocusExplorer
944
+ }, {
945
+ key: Escape,
946
+ command: 'Explorer.focusNone',
947
+ when: FocusExplorer
948
+ }, {
949
+ key: Space,
950
+ command: 'Explorer.handleClickCurrentButKeepFocus',
951
+ when: FocusExplorer
952
+ }, {
953
+ key: Enter,
954
+ command: 'Explorer.handleClickCurrent',
955
+ when: FocusExplorer
956
+ }];
957
+ };
958
+
959
+ const MenuBar = 'menubar';
960
+ const MenuItem = 'menuitem';
961
+
962
+ const HandleClick = 'handleClick';
963
+ const HandleFocusIn = 'handleFocusIn';
964
+ const HandleFocusOut = 'handleFocusOut';
965
+ const HandlePointerOut = 'handlePointerOut';
966
+ const HandlePointerOver = 'handlePointerOver';
967
+
968
+ const TitleBarEntryActive = 'TitleBarEntryActive';
969
+ const TitleBarTopLevelEntry = 'TitleBarTopLevelEntry';
970
+ const TitleBarTopLevelEntryLabel = 'TitleBarTopLevelEntryLabel';
971
+
972
+ const Div = 4;
973
+ const Text = 12;
974
+
975
+ const text = data => {
976
+ return {
977
+ type: Text,
978
+ text: data,
979
+ childCount: 0
980
+ };
981
+ };
982
+
983
+ const getItemVirtualDom = item => {
984
+ // @ts-ignore
985
+ const {
986
+ keyboardShortCut,
987
+ label,
988
+ isOpen,
989
+ isFocused
990
+ } = item;
991
+ const dom = [];
992
+ dom.push({
993
+ type: Div,
994
+ className: TitleBarTopLevelEntry,
995
+ ariaHasPopup: true,
996
+ ariaExpanded: isOpen,
997
+ role: MenuItem,
998
+ childCount: 1,
999
+ ariaKeyShortcuts: keyboardShortCut
1000
+ });
1001
+ if (isOpen) {
1002
+ // @ts-ignore
1003
+ dom[0].ariaOwns = 'Menu-0';
1004
+ }
1005
+ if (isFocused) {
1006
+ dom[0].className += ' ' + TitleBarEntryActive;
1007
+ // @ts-ignore
1008
+ dom[0].id = 'TitleBarEntryActive';
1009
+ dom.push({
1010
+ type: Div,
1011
+ className: TitleBarTopLevelEntryLabel,
1012
+ childCount: 1
1013
+ });
1014
+ }
1015
+ dom.push(text(label));
1016
+ return dom;
1017
+ };
1018
+ const getTitleBarMenuBarItemsVirtualDom = visibleItems => {
1019
+ const dom = visibleItems.flatMap(getItemVirtualDom);
1020
+ return dom;
1021
+ };
1022
+
1023
+ const getTitleBarMenuBarVirtualDom = visibleItems => {
1024
+ return [{
1025
+ type: Div,
1026
+ className: 'Viewlet TitleBarMenuBar',
1027
+ role: MenuBar,
1028
+ tabIndex: 0,
1029
+ childCount: visibleItems.length,
1030
+ onMouseDown: HandleClick,
1031
+ onFocusOut: HandleFocusOut,
1032
+ onFocusIn: HandleFocusIn,
1033
+ onPointerOver: HandlePointerOver,
1034
+ onPointerOut: HandlePointerOut
1035
+ }, ...getTitleBarMenuBarItemsVirtualDom(visibleItems)];
1036
+ };
1037
+
1038
+ const emptyObject = {};
1039
+ const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
1040
+ const i18nString = (key, placeholders = emptyObject) => {
1041
+ if (placeholders === emptyObject) {
1042
+ return key;
1043
+ }
1044
+ const replacer = (match, rest) => {
1045
+ return placeholders[rest];
1046
+ };
1047
+ return key.replaceAll(RE_PLACEHOLDER, replacer);
1048
+ };
1049
+
1050
+ /**
1051
+ * @enum {string}
1052
+ */
1053
+ const UiStrings$5 = {
1054
+ Copy: 'Copy',
1055
+ CopyLineDown: 'Copy Line Down',
1056
+ CopyLineUp: 'Copy Line Up',
1057
+ Cut: 'Cut',
1058
+ MoveLineDown: 'Move Line Down',
1059
+ MoveLineUp: 'Move Line Up',
1060
+ Paste: 'Paste',
1061
+ Redo: 'Redo',
1062
+ SelectAll: 'Select All',
1063
+ ToggleBlockComment: 'Toggle Block Comment',
1064
+ ToggleLineComment: 'Toggle Line Comment',
1065
+ Undo: 'Undo'};
1066
+ const cut = () => {
1067
+ return i18nString(UiStrings$5.Cut);
1068
+ };
1069
+ const copy = () => {
1070
+ return i18nString(UiStrings$5.Copy);
1071
+ };
1072
+ const paste = () => {
1073
+ return i18nString(UiStrings$5.Paste);
1074
+ };
1075
+ const undo = () => {
1076
+ return i18nString(UiStrings$5.Undo);
1077
+ };
1078
+ const redo = () => {
1079
+ return i18nString(UiStrings$5.Redo);
1080
+ };
1081
+ const toggleLineComment = () => {
1082
+ return i18nString(UiStrings$5.ToggleLineComment);
1083
+ };
1084
+ const toggleBlockComment = () => {
1085
+ return i18nString(UiStrings$5.ToggleBlockComment);
1086
+ };
1087
+ const selectAll = () => {
1088
+ return i18nString(UiStrings$5.SelectAll);
1089
+ };
1090
+ const copyLineUp = () => {
1091
+ return i18nString(UiStrings$5.CopyLineUp);
1092
+ };
1093
+ const copyLineDown = () => {
1094
+ return i18nString(UiStrings$5.CopyLineDown);
1095
+ };
1096
+ const moveLineUp = () => {
1097
+ return i18nString(UiStrings$5.MoveLineUp);
1098
+ };
1099
+ const moveLineDown = () => {
1100
+ return i18nString(UiStrings$5.MoveLineDown);
1101
+ };
1102
+
1103
+ const Separator = 1;
1104
+ const None = 0;
1105
+ const SubMenu = 4;
1106
+ const Disabled = 5;
1107
+ const RestoreFocus = 6;
1108
+ const Ignore = 7;
1109
+
1110
+ const menuEntrySeparator = {
1111
+ id: 'separator',
1112
+ label: '',
1113
+ flags: Separator,
1114
+ command: ''
1115
+ };
1116
+
1117
+ const Edit = 2;
1118
+ const File = 5;
1119
+ const Go = 6;
1120
+ const Help = 7;
1121
+ const OpenRecent = 9;
1122
+ const Run = 10;
1123
+ const Selection = 11;
1124
+ const Terminal = 14;
1125
+ const TitleBar = 15;
1126
+ const View = 16;
1127
+
1128
+ const id$9 = Edit;
1129
+ const getMenuEntries$c = () => {
1130
+ return [{
1131
+ id: 'undo',
1132
+ label: undo(),
1133
+ flags: Disabled,
1134
+ command: /* TODO */-1
1135
+ }, {
1136
+ id: 'redo',
1137
+ label: redo(),
1138
+ flags: Disabled,
1139
+ command: /* TODO */-1
1140
+ }, menuEntrySeparator, {
1141
+ id: 'cut',
1142
+ label: cut(),
1143
+ flags: None,
1144
+ command: /* Editor.cut */'Editor.cut'
1145
+ }, {
1146
+ id: 'copy',
1147
+ label: copy(),
1148
+ flags: None,
1149
+ command: /* Editor.copy */'Editor.copy'
1150
+ }, {
1151
+ id: 'paste',
1152
+ label: paste(),
1153
+ flags: None,
1154
+ command: /* Editor.paste */'Editor.paste'
1155
+ }, menuEntrySeparator, {
1156
+ id: 'toggle-line-comment',
1157
+ label: toggleLineComment(),
1158
+ flags: None,
1159
+ command: /* Editor.toggleLineComment */'Editor.toggleLineComment'
1160
+ }, {
1161
+ id: 'toggle-block-comment',
1162
+ label: toggleBlockComment(),
1163
+ flags: None,
1164
+ command: /* Editor.toggleBlockComment */'Editor.toggleBlockComment'
1165
+ }];
1166
+ };
1167
+
1168
+ const MenuEntriesEdit = {
1169
+ __proto__: null,
1170
+ getMenuEntries: getMenuEntries$c,
1171
+ id: id$9
1172
+ };
1173
+
1174
+ /**
1175
+ * @enum {string}
1176
+ */
1177
+ const UiStrings$4 = {
1178
+ NewFile: 'New File',
1179
+ NewWindow: 'New Window',
1180
+ OpenFile: 'Open File',
1181
+ OpenFolder: 'Open Folder',
1182
+ OpenRecent: 'Open Recent',
1183
+ Save: 'Save',
1184
+ SaveAll: 'Save All'
1185
+ };
1186
+ const newFile = () => {
1187
+ return i18nString(UiStrings$4.NewFile);
1188
+ };
1189
+ const newWindow = () => {
1190
+ return i18nString(UiStrings$4.NewWindow);
1191
+ };
1192
+ const openFile = () => {
1193
+ return i18nString(UiStrings$4.OpenFile);
1194
+ };
1195
+ const openFolder = () => {
1196
+ return i18nString(UiStrings$4.OpenFolder);
1197
+ };
1198
+ const openRecent = () => {
1199
+ return i18nString(UiStrings$4.OpenRecent);
1200
+ };
1201
+ const save = () => {
1202
+ return i18nString(UiStrings$4.Save);
1203
+ };
1204
+ const saveAll = () => {
1205
+ return i18nString(UiStrings$4.SaveAll);
1206
+ };
1207
+
1208
+ const platform = 1;
1209
+
1210
+ const Web = 1;
1211
+
1212
+ const id$8 = File;
1213
+ const getMenuEntries$b = () => {
1214
+ const entries = [{
1215
+ id: 'newFile',
1216
+ label: newFile(),
1217
+ flags: None,
1218
+ command: -1
1219
+ }, {
1220
+ id: 'newWindow',
1221
+ label: newWindow(),
1222
+ flags: None,
1223
+ command: /* Window.openNew */'Window.openNew'
1224
+ }, menuEntrySeparator, {
1225
+ id: 'openFile',
1226
+ label: openFile(),
1227
+ flags: None,
1228
+ command: 'Dialog.openFile'
1229
+ }, {
1230
+ id: 'openFolder',
1231
+ label: openFolder(),
1232
+ flags: RestoreFocus,
1233
+ command: 'Dialog.openFolder'
1234
+ }, {
1235
+ id: OpenRecent,
1236
+ label: openRecent(),
1237
+ flags: SubMenu,
1238
+ command: ''
1239
+ }, menuEntrySeparator, {
1240
+ id: 'save',
1241
+ label: save(),
1242
+ flags: None,
1243
+ command: 'Main.save'
1244
+ }, {
1245
+ id: 'saveAll',
1246
+ label: saveAll(),
1247
+ flags: None,
1248
+ command: 'Main.saveAll'
1249
+ }];
1250
+ return entries;
1251
+ };
1252
+
1253
+ const MenuEntriesFile = {
1254
+ __proto__: null,
1255
+ getMenuEntries: getMenuEntries$b,
1256
+ id: id$8
1257
+ };
1258
+
1259
+ const id$7 = Go;
1260
+ const getMenuEntries$a = () => {
1261
+ return [];
1262
+ };
1263
+
1264
+ const MenuEntriesGo = {
1265
+ __proto__: null,
1266
+ getMenuEntries: getMenuEntries$a,
1267
+ id: id$7
1268
+ };
1269
+
1270
+ /**
1271
+ * @enum {string}
1272
+ */
1273
+ const UiStrings$3 = {
1274
+ About: 'About',
1275
+ CheckForUpdates: 'Check For Updates'};
1276
+ const checkForUpdates = () => {
1277
+ return i18nString(UiStrings$3.CheckForUpdates);
1278
+ };
1279
+ const about = () => {
1280
+ return i18nString(UiStrings$3.About);
1281
+ };
1282
+
1283
+ const isAutoUpdateSupported = () => {
1284
+ {
1285
+ return false;
1286
+ }
1287
+ };
1288
+
1289
+ const id$6 = Help;
1290
+ const getMenuEntries$9 = async () => {
1291
+ const autoUpdateSupported = isAutoUpdateSupported();
1292
+ const entries = [];
1293
+ if (autoUpdateSupported) {
1294
+ entries.push(menuEntrySeparator, {
1295
+ id: 'checkForUpdates',
1296
+ label: checkForUpdates(),
1297
+ flags: RestoreFocus,
1298
+ command: 'AutoUpdater.checkForUpdates'
1299
+ });
1300
+ }
1301
+ if (entries.length > 0) {
1302
+ entries.push(menuEntrySeparator);
1303
+ }
1304
+ entries.push({
1305
+ id: 'about',
1306
+ label: about(),
1307
+ flags: RestoreFocus,
1308
+ command: 'About.showAbout'
1309
+ });
1310
+ return entries;
1311
+ };
1312
+
1313
+ const MenuEntriesHelp = {
1314
+ __proto__: null,
1315
+ getMenuEntries: getMenuEntries$9,
1316
+ id: id$6
1317
+ };
1318
+
1319
+ const state$1 = {
1320
+ rpc: undefined
1321
+ };
1322
+ const invoke = (method, ...params) => {
1323
+ const rpc = state$1.rpc;
1324
+ // @ts-ignore
1325
+ return rpc.invoke(method, ...params);
1326
+ };
1327
+ const setRpc = rpc => {
1328
+ state$1.rpc = rpc;
1329
+ };
1330
+
1331
+ const getTitle = uri => {
1332
+ if (!uri) {
1333
+ return '';
1334
+ }
1335
+ return uri;
1336
+ };
1337
+
1338
+ /**
1339
+ * @enum {string}
1340
+ */
1341
+ const UiStrings$2 = {
1342
+ MoreDot: 'More ...',
1343
+ ClearRecentlyOpened: 'Clear Recently Opened'
1344
+ };
1345
+ const moreDot = () => {
1346
+ return i18nString(UiStrings$2.MoreDot);
1347
+ };
1348
+ const clearRecentlyOpened = () => {
1349
+ return i18nString(UiStrings$2.ClearRecentlyOpened);
1350
+ };
1351
+
1352
+ const MAX_MENU_RECENT_ENTRIES = 10;
1353
+ const toMenuItem = folder => {
1354
+ const label = getTitle(folder);
1355
+ return {
1356
+ label,
1357
+ flags: None,
1358
+ command: 'Workspace.setPath',
1359
+ args: [folder]
1360
+ };
1361
+ };
1362
+ const getRecentlyOpened = () => {
1363
+ return invoke(/* RecentlyOpened.getRecentlyOpened */'RecentlyOpened.getRecentlyOpened');
1364
+ };
1365
+ const id$5 = OpenRecent;
1366
+ const getMenuEntries$8 = async () => {
1367
+ const allItems = await getRecentlyOpened();
1368
+ const itemsToShow = allItems.slice(0, MAX_MENU_RECENT_ENTRIES);
1369
+ const items = [];
1370
+ if (itemsToShow.length > 0) {
1371
+ items.push(...itemsToShow.map(toMenuItem), menuEntrySeparator);
1372
+ }
1373
+ items.push({
1374
+ id: 'more',
1375
+ label: moreDot(),
1376
+ flags: None,
1377
+ command: 'QuickPick.showRecent'
1378
+ }, menuEntrySeparator, {
1379
+ id: 'clearRecentlyOpened',
1380
+ label: clearRecentlyOpened(),
1381
+ flags: None,
1382
+ command: 'RecentlyOpened.clearRecentlyOpened'
1383
+ });
1384
+ return items;
1385
+ };
1386
+
1387
+ const MenuEntriesOpenRecent = {
1388
+ __proto__: null,
1389
+ getMenuEntries: getMenuEntries$8,
1390
+ id: id$5
1391
+ };
1392
+
1393
+ const id$4 = Run;
1394
+ const getMenuEntries$7 = () => {
1395
+ return [];
1396
+ };
1397
+
1398
+ const MenuEntriesRun = {
1399
+ __proto__: null,
1400
+ getMenuEntries: getMenuEntries$7,
1401
+ id: id$4
1402
+ };
1403
+
1404
+ const id$3 = Selection;
1405
+ const getMenuEntries$6 = () => {
1406
+ return [{
1407
+ id: 'selectAll',
1408
+ label: selectAll(),
1409
+ flags: None,
1410
+ command: 'Editor.selectAll'
1411
+ }, {
1412
+ id: 'copyLineUp',
1413
+ label: copyLineUp(),
1414
+ flags: None,
1415
+ command: 'Editor.copyLineUp'
1416
+ }, {
1417
+ id: 'copyLineDown',
1418
+ label: copyLineDown(),
1419
+ flags: None,
1420
+ command: 'Editor.copyLineDown'
1421
+ }, {
1422
+ id: 'moveLineUp',
1423
+ label: moveLineUp(),
1424
+ flags: Disabled,
1425
+ command: 'Editor.moveLineUp'
1426
+ }, {
1427
+ id: 'moveLineDown',
1428
+ label: moveLineDown(),
1429
+ flags: Disabled,
1430
+ command: 'Editor.moveLineDown'
1431
+ }];
1432
+ };
1433
+
1434
+ const MenuEntriesSelection = {
1435
+ __proto__: null,
1436
+ getMenuEntries: getMenuEntries$6,
1437
+ id: id$3
1438
+ };
1439
+
1440
+ /**
1441
+ * @enum {string}
1442
+ */
1443
+ const UiStrings$1 = {
1444
+ NewTerminal: 'New Terminal'
1445
+ };
1446
+ const newTerminal = () => {
1447
+ return i18nString(UiStrings$1.NewTerminal);
1448
+ };
1449
+
1450
+ const id$2 = Terminal;
1451
+ const getMenuEntries$5 = () => {
1452
+ return [{
1453
+ id: 'newTerminal',
1454
+ label: newTerminal(),
1455
+ flags: None,
1456
+ command: 'Layout.togglePanel',
1457
+ args: ['Terminal']
1458
+ }];
1459
+ };
1460
+
1461
+ const MenuEntriesTerminal = {
1462
+ __proto__: null,
1463
+ getMenuEntries: getMenuEntries$5,
1464
+ id: id$2
1465
+ };
1466
+
1467
+ /**
1468
+ * @enum {string}
1469
+ */
1470
+ const UiStrings = {
1471
+ File: 'File',
1472
+ Edit: 'Edit',
1473
+ Selection: 'Selection',
1474
+ View: 'View',
1475
+ Go: 'Go',
1476
+ Run: 'Run',
1477
+ Terminal: 'Terminal',
1478
+ Help: 'Help'
1479
+ };
1480
+ const file = () => {
1481
+ return i18nString(UiStrings.File);
1482
+ };
1483
+ const edit = () => {
1484
+ return i18nString(UiStrings.Edit);
1485
+ };
1486
+ const selection = () => {
1487
+ return i18nString(UiStrings.Selection);
1488
+ };
1489
+ const view = () => {
1490
+ return i18nString(UiStrings.View);
1491
+ };
1492
+ const go = () => {
1493
+ return i18nString(UiStrings.Go);
1494
+ };
1495
+ const run = () => {
1496
+ return i18nString(UiStrings.Run);
1497
+ };
1498
+ const terminal = () => {
1499
+ return i18nString(UiStrings.Terminal);
1500
+ };
1501
+ const help = () => {
1502
+ return i18nString(UiStrings.Help);
1503
+ };
1504
+
1505
+ const getMenuEntries$4 = () => {
1506
+ return [{
1507
+ id: File,
1508
+ label: file(),
1509
+ flags: SubMenu
1510
+ }, {
1511
+ id: Edit,
1512
+ label: edit(),
1513
+ flags: SubMenu
1514
+ }, {
1515
+ id: Selection,
1516
+ label: selection(),
1517
+ flags: SubMenu
1518
+ }, {
1519
+ id: View,
1520
+ label: view(),
1521
+ flags: SubMenu
1522
+ }, {
1523
+ id: Go,
1524
+ label: go(),
1525
+ flags: SubMenu
1526
+ }, {
1527
+ id: Run,
1528
+ label: run(),
1529
+ keyboardShortCut: 'Alt+r',
1530
+ flags: SubMenu
1531
+ }, {
1532
+ id: Terminal,
1533
+ label: terminal(),
1534
+ keyboardShortCut: 'Alt+t',
1535
+ flags: SubMenu
1536
+ }, {
1537
+ id: Help,
1538
+ label: help(),
1539
+ keyboardShortCut: 'Alt+h',
1540
+ flags: SubMenu
1541
+ }];
1542
+ };
1543
+
1544
+ const getMenuEntries$3 = () => {
1545
+ return [{
1546
+ id: File,
1547
+ label: file(),
1548
+ flags: None
1549
+ }, {
1550
+ id: Edit,
1551
+ label: edit(),
1552
+ flags: None
1553
+ }, {
1554
+ id: Selection,
1555
+ label: selection(),
1556
+ flags: None
1557
+ }, {
1558
+ id: View,
1559
+ label: view(),
1560
+ flags: None
1561
+ }, {
1562
+ id: Go,
1563
+ label: go(),
1564
+ flags: None
1565
+ }, {
1566
+ id: Help,
1567
+ label: help(),
1568
+ flags: None
1569
+ }];
1570
+ };
1571
+
1572
+ const getFn = () => {
1573
+ switch (platform) {
1574
+ case Web:
1575
+ return getMenuEntries$3;
1576
+ default:
1577
+ return getMenuEntries$4;
1578
+ }
1579
+ };
1580
+ const id$1 = TitleBar;
1581
+ const getMenuEntries$2 = async () => {
1582
+ const fn = getFn();
1583
+ return fn();
1584
+ };
1585
+
1586
+ const MenuEntriesTitleBar = {
1587
+ __proto__: null,
1588
+ getMenuEntries: getMenuEntries$2,
1589
+ id: id$1
1590
+ };
1591
+
1592
+ const id = View;
1593
+ const getMenuEntries$1 = () => {
1594
+ return [];
1595
+ };
1596
+
1597
+ const MenuEntriesView = {
1598
+ __proto__: null,
1599
+ getMenuEntries: getMenuEntries$1,
1600
+ id
1601
+ };
1602
+
1603
+ const menus = [MenuEntriesEdit, MenuEntriesFile, MenuEntriesGo, MenuEntriesHelp, MenuEntriesRun, MenuEntriesSelection, MenuEntriesTerminal, MenuEntriesTitleBar, MenuEntriesView, MenuEntriesOpenRecent];
1604
+ const getMenus = () => {
1605
+ return menus;
1606
+ };
1607
+ const getMenuEntries = async (id, ...args) => {
1608
+ try {
1609
+ const module = menus[0];
1610
+ // @ts-ignore
1611
+ const inject = module.inject || [];
1612
+ // @ts-ignore
1613
+ return module.getMenuEntries(...args);
1614
+ } catch (error) {
1615
+ throw new VError(error, `Failed to load menu entries for id ${id}`);
1616
+ }
1617
+ };
1618
+
1619
+ const getFontString = (fontWeight, fontSize, fontFamily) => {
1620
+ return `${fontWeight} ${fontSize}px ${fontFamily}`;
1621
+ };
1622
+
1623
+ const state = {
1624
+ ctx: undefined
1625
+ };
1626
+ const getOrCreate = createCtx => {
1627
+ if (state.ctx) {
1628
+ return state.ctx;
1629
+ }
1630
+ state.ctx = createCtx();
1631
+ return state.ctx;
1632
+ };
1633
+
1634
+ const createCtx = () => {
1635
+ const canvas = new OffscreenCanvas(0, 0);
1636
+ const ctx = /** @type {OffscreenCanvasRenderingContext2D} */canvas.getContext('2d');
1637
+ if (!ctx) {
1638
+ throw new Error('Failed to get canvas context 2d');
1639
+ }
1640
+ return ctx;
1641
+ };
1642
+ const getContext = () => {
1643
+ const ctx = getOrCreate(createCtx);
1644
+ return ctx;
1645
+ };
1646
+
1647
+ const px = value => {
1648
+ return `${value}px`;
1649
+ };
1650
+
1651
+ const getLetterSpacingString = letterSpacing => {
1652
+ return px(letterSpacing);
1653
+ };
1654
+ const measureTextWidth = (text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth) => {
1655
+ string(text);
1656
+ number(fontWeight);
1657
+ number(fontSize);
1658
+ string(fontFamily);
1659
+ boolean(isMonoSpaceFont);
1660
+ number(charWidth);
1661
+ if (typeof letterSpacing !== 'number') {
1662
+ throw new TypeError('letterSpacing must be of type number');
1663
+ }
1664
+ const letterSpacingString = getLetterSpacingString(letterSpacing);
1665
+ const fontString = getFontString(fontWeight, fontSize, fontFamily);
1666
+ const ctx = getContext();
1667
+ // @ts-ignore
1668
+ ctx.letterSpacing = letterSpacingString;
1669
+ ctx.font = fontString;
1670
+ const metrics = ctx.measureText(text);
1671
+ const width = metrics.width;
1672
+ return width;
1673
+ };
1674
+
1675
+ const measureTitleBarEntryWidth = (label, fontWeight, fontSize, fontFamily, letterSpacing) => {
1676
+ const isMonospaceFont = false;
1677
+ const charWidth = 0;
1678
+ return measureTextWidth(label, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
1679
+ };
1680
+
1681
+ const create = (id, uri, x, y, width, height) => {
1682
+ return {
1683
+ uid: id,
1684
+ titleBarEntries: [],
1685
+ focusedIndex: -1,
1686
+ isMenuOpen: false,
1687
+ menus: [],
1688
+ labelFontWeight: 400,
1689
+ labelFontSize: 13,
1690
+ labelFontFamily: 'system-ui, Ubuntu, Droid Sans, sans-serif',
1691
+ labelPadding: 8,
1692
+ labelLetterSpacing: 0,
1693
+ titleBarHeight: height,
1694
+ x,
1695
+ y,
1696
+ width,
1697
+ height
1698
+ };
1699
+ };
1700
+ const addWidths = (entries, labelPadding, fontWeight, fontSize, fontFamily, letterSpacing) => {
1701
+ const withWidths = [];
1702
+ for (const entry of entries) {
1703
+ const textWidth = measureTitleBarEntryWidth(entry.label, fontWeight, fontSize, fontFamily, letterSpacing);
1704
+ const width = textWidth + labelPadding * 2;
1705
+ withWidths.push({
1706
+ ...entry,
1707
+ width
1708
+ });
1709
+ }
1710
+ return withWidths;
1711
+ };
1712
+ const loadContent = async state => {
1713
+ const {
1714
+ labelFontFamily,
1715
+ labelFontSize,
1716
+ labelFontWeight,
1717
+ labelLetterSpacing,
1718
+ labelPadding
1719
+ } = state;
1720
+ const titleBarEntries = [];
1721
+ const withWidths = addWidths(titleBarEntries, labelPadding, labelFontWeight, labelFontSize, labelFontFamily, labelLetterSpacing);
1722
+ // const visible = GetVisibleTitleBarEntries.getVisibleTitleBarEntries(withWidths, width)
1723
+ // console.log({ visible })
1724
+ return {
1725
+ ...state,
1726
+ titleBarEntries: withWidths
1727
+ };
745
1728
  };
746
1729
 
747
- const createRpc = ipc => {
748
- const rpc = {
749
- /**
750
- * @deprecated
751
- */
752
- send(method, ...params) {
753
- send(ipc, method, ...params);
754
- },
755
- invoke(method, ...params) {
756
- return invoke(ipc, method, ...params);
757
- },
758
- invokeAndTransfer(method, ...params) {
759
- return invokeAndTransfer(ipc, method, ...params);
760
- }
1730
+ const closeMenu = (state, keepFocus) => {
1731
+ const {
1732
+ focusedIndex
1733
+ } = state;
1734
+ // TODO send to renderer process
1735
+ // 1. close menu
1736
+ // 2. focus top level entry
1737
+ const newFocusedIndex = keepFocus ? focusedIndex : -1;
1738
+ return {
1739
+ ...state,
1740
+ menus: [],
1741
+ isMenuOpen: false,
1742
+ focusedIndex: newFocusedIndex
761
1743
  };
762
- return rpc;
763
1744
  };
764
- const requiresSocket = () => {
765
- return false;
1745
+
1746
+ const first = () => {
1747
+ return 0;
766
1748
  };
767
- const preparePrettyError = error => {
768
- return error;
1749
+ const last = items => {
1750
+ return items.length - 1;
769
1751
  };
770
- const logError = () => {
771
- // handled by renderer worker
1752
+ const next = (items, index) => {
1753
+ return (index + 1) % items.length;
772
1754
  };
773
- const handleMessage = event => {
774
- return handleJsonRpcMessage(event.target, event.data, execute, resolve, preparePrettyError, logError, requiresSocket);
1755
+ const previous = (items, index) => {
1756
+ return index === 0 ? items.length - 1 : index - 1;
775
1757
  };
776
- const handleIpc = ipc => {
777
- ipc.addEventListener('message', handleMessage);
1758
+
1759
+ // TODO lazyload menuEntries and use Command.execute (maybe)
1760
+ const MENU_WIDTH = 150;
1761
+ const CONTEXT_MENU_ITEM_HEIGHT = 26;
1762
+ const CONTEXT_MENU_SEPARATOR_HEIGHT = 11;
1763
+ const CONTEXT_MENU_PADDING = 8;
1764
+ const CONTEXT_MENU_WIDTH = 250;
1765
+ const getMenuWidth = () => {
1766
+ return CONTEXT_MENU_WIDTH;
1767
+ };
1768
+ const getMenuHeight = items => {
1769
+ let height = CONTEXT_MENU_PADDING;
1770
+ for (const item of items) {
1771
+ switch (item.flags) {
1772
+ case Separator:
1773
+ height += CONTEXT_MENU_SEPARATOR_HEIGHT;
1774
+ break;
1775
+ default:
1776
+ height += CONTEXT_MENU_ITEM_HEIGHT;
1777
+ break;
1778
+ }
1779
+ }
1780
+ return height;
778
1781
  };
779
1782
 
780
- // @ts-ignore
781
- const listen$1 = async () => {
782
- const module = IpcChildWithModuleWorkerAndMessagePort$1;
783
- const rawIpc = await module.listen();
784
- const ipc = module.wrap(rawIpc);
785
- return ipc;
1783
+ // TODO difference between focusing with mouse or keyboard
1784
+ // with mouse -> open submenu
1785
+ // with keyboard -> don't open submenu, only focus
1786
+
1787
+ const getIndexToFocusNextStartingAt = (items, startIndex) => {
1788
+ for (let i = startIndex; i < startIndex + items.length; i++) {
1789
+ const index = i % items.length;
1790
+ const item = items[index];
1791
+ if (canBeFocused(item)) {
1792
+ return index;
1793
+ }
1794
+ }
1795
+ return -1;
786
1796
  };
787
- const create$1 = async ({
788
- commandMap
789
- }) => {
790
- // TODO create a commandMap per rpc instance
791
- register(commandMap);
792
- const ipc = await listen$1();
793
- handleIpc(ipc);
794
- const rpc = createRpc(ipc);
795
- return rpc;
1797
+ const getIndexToFocusFirst = items => {
1798
+ return getIndexToFocusNextStartingAt(items, 0);
796
1799
  };
797
- const WebWorkerRpcClient = {
798
- __proto__: null,
799
- create: create$1
1800
+ const getIndexToFocusLast = items => {
1801
+ return getIndexToFocusPreviousStartingAt(items, items.length - 1);
800
1802
  };
801
1803
 
802
- const Enter = 3;
803
- const Escape = 8;
804
- const Space = 9;
805
- const End = 255;
806
- const Home = 12;
807
- const LeftArrow = 13;
808
- const UpArrow = 14;
809
- const RightArrow = 15;
810
- const DownArrow = 16;
811
- const Delete = 18;
812
- const KeyC = 31;
813
- const KeyV = 50;
814
- const F2 = 58;
815
- const Star = 131;
816
-
817
- const CtrlCmd = 1 << 11 >>> 0;
818
- const Alt = 1 << 9 >>> 0;
819
-
820
- const FocusExplorer = 13;
821
- const FocusExplorerEditBox = 14;
822
-
823
- const getKeyBindings = () => {
824
- return [{
825
- key: RightArrow,
826
- command: 'Explorer.handleArrowRight',
827
- when: FocusExplorer
828
- }, {
829
- key: LeftArrow,
830
- command: 'Explorer.handleArrowLeft',
831
- when: FocusExplorer
832
- }, {
833
- key: Home,
834
- command: 'Explorer.focusFirst',
835
- when: FocusExplorer
836
- }, {
837
- key: End,
838
- command: 'Explorer.focusLast',
839
- when: FocusExplorer
840
- }, {
841
- key: UpArrow,
842
- command: 'Explorer.focusPrevious',
843
- when: FocusExplorer
844
- }, {
845
- key: DownArrow,
846
- command: 'Explorer.focusNext',
847
- when: FocusExplorer
848
- }, {
849
- key: CtrlCmd | Star,
850
- command: 'Explorer.expandAll',
851
- when: FocusExplorer
852
- }, {
853
- key: Alt | RightArrow,
854
- command: 'Explorer.expandRecursively',
855
- when: FocusExplorer
856
- }, {
857
- key: CtrlCmd | LeftArrow,
858
- command: 'Explorer.collapseAll',
859
- when: FocusExplorer
860
- }, {
861
- key: CtrlCmd | KeyV,
862
- command: 'Explorer.handlePaste',
863
- when: FocusExplorer
864
- }, {
865
- key: CtrlCmd | KeyC,
866
- command: 'Explorer.handleCopy',
867
- when: FocusExplorer
868
- }, {
869
- key: F2,
870
- command: 'Explorer.rename',
871
- when: FocusExplorer
872
- }, {
873
- key: Escape,
874
- command: 'Explorer.cancelEdit',
875
- when: FocusExplorerEditBox
876
- }, {
877
- key: Enter,
878
- command: 'Explorer.acceptEdit',
879
- when: FocusExplorerEditBox
880
- }, {
881
- key: Delete,
882
- command: 'Explorer.removeDirent',
883
- when: FocusExplorer
884
- }, {
885
- key: Escape,
886
- command: 'Explorer.focusNone',
887
- when: FocusExplorer
888
- }, {
889
- key: Space,
890
- command: 'Explorer.handleClickCurrentButKeepFocus',
891
- when: FocusExplorer
892
- }, {
893
- key: Enter,
894
- command: 'Explorer.handleClickCurrent',
895
- when: FocusExplorer
896
- }];
1804
+ // TODO this code seems a bit too complicated, maybe it can be simplified
1805
+ const getIndexToFocusPreviousStartingAt = (items, startIndex) => {
1806
+ for (let i = startIndex; i > startIndex - items.length; i--) {
1807
+ const index = (i + items.length) % items.length;
1808
+ const item = items[index];
1809
+ if (canBeFocused(item)) {
1810
+ return index;
1811
+ }
1812
+ }
1813
+ return -1;
1814
+ };
1815
+ const getIndexToFocusPrevious = menu => {
1816
+ const startIndex = menu.focusedIndex === -1 ? menu.items.length - 1 : menu.focusedIndex - 1;
1817
+ return getIndexToFocusPreviousStartingAt(menu.items, startIndex);
1818
+ };
1819
+ const canBeFocused = item => {
1820
+ switch (item.flags) {
1821
+ case Separator:
1822
+ case Disabled:
1823
+ return false;
1824
+ default:
1825
+ return true;
1826
+ }
1827
+ };
1828
+ const getIndexToFocusNext = menu => {
1829
+ const startIndex = menu.focusedIndex + 1;
1830
+ return getIndexToFocusNextStartingAt(menu.items, startIndex);
897
1831
  };
898
1832
 
899
- const MenuBar = 'menubar';
900
- const MenuItem = 'menuitem';
1833
+ // TODO handle printable letter and focus item that starts with that letter
901
1834
 
902
- const HandleClick = 'handleClick';
903
- const HandleFocusIn = 'handleFocusIn';
904
- const HandleFocusOut = 'handleFocusOut';
905
- const HandlePointerOut = 'handlePointerOut';
906
- const HandlePointerOver = 'handlePointerOver';
1835
+ // TODO pageup / pagedown keys
907
1836
 
908
- const TitleBarEntryActive = 'TitleBarEntryActive';
909
- const TitleBarTopLevelEntry = 'TitleBarTopLevelEntry';
910
- const TitleBarTopLevelEntryLabel = 'TitleBarTopLevelEntryLabel';
1837
+ // TODO more tests
911
1838
 
912
- const Div = 4;
913
- const Text = 12;
1839
+ const getTotalWidth = entries => {
1840
+ let total = 0;
1841
+ for (const entry of entries) {
1842
+ total += entry.width;
1843
+ }
1844
+ return total;
1845
+ };
1846
+ const openMenuAtIndex = async (state, index, shouldBeFocused) => {
1847
+ const {
1848
+ titleBarEntries,
1849
+ titleBarHeight,
1850
+ x
1851
+ } = state;
1852
+ // TODO race conditions
1853
+ // TODO send renderer process
1854
+ // 1. open menu, items to show
1855
+ // 2. focus menu
1856
+ const titleBarEntry = titleBarEntries[index];
1857
+ const {
1858
+ id
1859
+ } = titleBarEntry;
1860
+ const items = await getMenuEntries(id);
1861
+ const relevantEntries = titleBarEntries.slice(0, index);
1862
+ const totalWidths = getTotalWidth(relevantEntries);
1863
+ const offset = totalWidths;
1864
+ // TODO race condition: another menu might already be open at this point
914
1865
 
915
- const text = data => {
1866
+ const menuX = x + offset;
1867
+ const menuY = titleBarHeight;
1868
+ const width = getMenuWidth();
1869
+ const height = getMenuHeight(items);
1870
+ const menuFocusedIndex = shouldBeFocused ? getIndexToFocusNextStartingAt(items, 0) : -1;
1871
+ const menu = {
1872
+ id,
1873
+ items,
1874
+ focusedIndex: menuFocusedIndex,
1875
+ level: 0,
1876
+ x: menuX,
1877
+ y: menuY,
1878
+ width,
1879
+ height
1880
+ };
1881
+ const menus = [menu];
916
1882
  return {
917
- type: Text,
918
- text: data,
919
- childCount: 0
1883
+ ...state,
1884
+ isMenuOpen: true,
1885
+ focusedIndex: index,
1886
+ menus
920
1887
  };
921
1888
  };
922
1889
 
923
- const getItemVirtualDom = item => {
924
- // @ts-ignore
1890
+ const focusIndex = (state, index) => {
1891
+ object(state);
1892
+ number(index);
925
1893
  const {
926
- keyboardShortCut,
927
- label,
928
- icon,
929
- isOpen,
930
- isFocused
931
- } = item;
932
- const dom = [];
933
- dom.push({
934
- type: Div,
935
- className: TitleBarTopLevelEntry,
936
- ariaHasPopup: true,
937
- ariaExpanded: isOpen,
938
- role: MenuItem,
939
- childCount: 1,
940
- ariaKeyShortcuts: keyboardShortCut
941
- });
942
- if (isOpen) {
943
- // @ts-ignore
944
- dom[0].ariaOwns = 'Menu-0';
945
- }
946
- if (isFocused) {
947
- dom[0].className += ' ' + TitleBarEntryActive;
948
- // @ts-ignore
949
- dom[0].id = 'TitleBarEntryActive';
950
- dom.push({
951
- type: Div,
952
- className: TitleBarTopLevelEntryLabel,
953
- childCount: 1
954
- });
1894
+ isMenuOpen
1895
+ } = state;
1896
+ if (isMenuOpen) {
1897
+ return openMenuAtIndex(state, index, /* focus */false);
955
1898
  }
956
- dom.push(text(label));
957
- return dom;
1899
+ return {
1900
+ ...state,
1901
+ focusedIndex: index
1902
+ };
958
1903
  };
959
- const getTitleBarMenuBarItemsVirtualDom = visibleItems => {
960
- const dom = visibleItems.flatMap(getItemVirtualDom);
961
- return dom;
1904
+
1905
+ const focusFirst = state => {
1906
+ const indexToFocus = first();
1907
+ return focusIndex(state, indexToFocus);
962
1908
  };
963
1909
 
964
- const getTitleBarMenuBarVirtualDom = visibleItems => {
965
- return [{
966
- type: Div,
967
- className: 'Viewlet TitleBarMenuBar',
968
- role: MenuBar,
969
- tabIndex: 0,
970
- childCount: visibleItems.length,
971
- onMouseDown: HandleClick,
972
- onFocusOut: HandleFocusOut,
973
- onFocusIn: HandleFocusIn,
974
- onPointerOver: HandlePointerOver,
975
- onPointerOut: HandlePointerOut
976
- }, ...getTitleBarMenuBarItemsVirtualDom(visibleItems)];
1910
+ const focus = state => {
1911
+ return focusFirst(state);
1912
+ };
1913
+
1914
+ const focusLast = state => {
1915
+ const {
1916
+ titleBarEntries
1917
+ } = state;
1918
+ const indexToFocus = last(titleBarEntries);
1919
+ return focusIndex(state, indexToFocus);
1920
+ };
1921
+
1922
+ const focusNext = state => {
1923
+ const {
1924
+ titleBarEntries,
1925
+ focusedIndex
1926
+ } = state;
1927
+ const indexToFocus = next(titleBarEntries, focusedIndex);
1928
+ return focusIndex(state, indexToFocus);
1929
+ };
1930
+
1931
+ const focusPrevious = state => {
1932
+ const {
1933
+ titleBarEntries,
1934
+ focusedIndex
1935
+ } = state;
1936
+ const indexToFocus = previous(titleBarEntries, focusedIndex);
1937
+ return focusIndex(state, indexToFocus);
1938
+ };
1939
+
1940
+ const LeftClick = 0;
1941
+
1942
+ const toggleIndex = (state, index) => {
1943
+ const {
1944
+ isMenuOpen,
1945
+ focusedIndex
1946
+ } = state;
1947
+ if (isMenuOpen && focusedIndex === index) {
1948
+ return closeMenu(state, /* keepFocus */true);
1949
+ }
1950
+ return openMenuAtIndex(state, index, /* focus */false);
977
1951
  };
978
1952
 
979
- class AssertionError extends Error {
980
- constructor(message) {
981
- super(message);
982
- this.name = 'AssertionError';
1953
+ const handleClick = (state, button, index) => {
1954
+ if (button !== LeftClick) {
1955
+ return state;
983
1956
  }
984
- }
985
- const getType = value => {
986
- switch (typeof value) {
987
- case 'number':
988
- return 'number';
989
- case 'function':
990
- return 'function';
991
- case 'string':
992
- return 'string';
993
- case 'object':
994
- if (value === null) {
995
- return 'null';
996
- }
997
- if (Array.isArray(value)) {
998
- return 'array';
999
- }
1000
- return 'object';
1001
- case 'boolean':
1002
- return 'boolean';
1003
- default:
1004
- return 'unknown';
1957
+ if (index === -1) {
1958
+ return state;
1005
1959
  }
1960
+ return toggleIndex(state, index);
1006
1961
  };
1007
- const number = value => {
1008
- const type = getType(value);
1009
- if (type !== 'number') {
1010
- throw new AssertionError('expected value to be of type number');
1011
- }
1962
+
1963
+ // TODO remove this file and merge with whenExpressions
1964
+ const TitleBarMenuBar = FocusTitleBarMenuBar;
1965
+
1966
+ const handleFocus = async state => {
1967
+ await invoke('Focus.setFocus', TitleBarMenuBar);
1968
+ return state;
1012
1969
  };
1013
- const string = value => {
1014
- const type = getType(value);
1015
- if (type !== 'string') {
1016
- throw new AssertionError('expected value to be of type string');
1970
+
1971
+ /**
1972
+ * @param {boolean} focus
1973
+ */
1974
+ const openMenu = (state, focus) => {
1975
+ const {
1976
+ focusedIndex
1977
+ } = state;
1978
+ if (focusedIndex === -1) {
1979
+ return state;
1017
1980
  }
1981
+ return openMenuAtIndex(state, focusedIndex, focus);
1018
1982
  };
1019
- const boolean = value => {
1020
- const type = getType(value);
1021
- if (type !== 'boolean') {
1022
- throw new AssertionError('expected value to be of type boolean');
1023
- }
1983
+
1984
+ const handleKeyArrowDownMenuClosed = state => {
1985
+ return openMenu(state, /* focus */true);
1024
1986
  };
1025
1987
 
1026
- const getFontString = (fontWeight, fontSize, fontFamily) => {
1027
- return `${fontWeight} ${fontSize}px ${fontFamily}`;
1988
+ const handleKeyArrowDownMenuOpen = state => {
1989
+ const {
1990
+ menus
1991
+ } = state;
1992
+ const menu = menus.at(-1);
1993
+ const newFocusedIndex = getIndexToFocusNext(menu);
1994
+ const newMenus = [...menus.slice(0, -1), {
1995
+ ...menu,
1996
+ focusedIndex: newFocusedIndex
1997
+ }];
1998
+ return {
1999
+ ...state,
2000
+ menus: newMenus
2001
+ };
1028
2002
  };
1029
2003
 
1030
- const state = {
1031
- ctx: undefined
2004
+ const ifElse = (menuOpenFunction, menuClosedFunction) => {
2005
+ const ifElseFunction = (state, ...args) => {
2006
+ const {
2007
+ isMenuOpen
2008
+ } = state;
2009
+ if (isMenuOpen) {
2010
+ return menuOpenFunction(state, ...args);
2011
+ }
2012
+ return menuClosedFunction(state, ...args);
2013
+ };
2014
+ return ifElseFunction;
1032
2015
  };
1033
- const getOrCreate = createCtx => {
1034
- if (state.ctx) {
1035
- return state.ctx;
2016
+
2017
+ const handleKeyArrowDown = ifElse(handleKeyArrowDownMenuOpen, handleKeyArrowDownMenuClosed);
2018
+
2019
+ const handleKeyArrowLeftMenuClosed = state => {
2020
+ // TODO menu collapse
2021
+ return focusPrevious(state);
2022
+ };
2023
+
2024
+ const closeOneMenu = state => {
2025
+ const {
2026
+ menus
2027
+ } = state;
2028
+ const parentMenu = menus.at(-2);
2029
+ const newParentMenu = {
2030
+ ...parentMenu,
2031
+ expanded: false
2032
+ };
2033
+ const newMenus = [...menus.slice(0, -2), newParentMenu];
2034
+ return {
2035
+ ...state,
2036
+ menus: newMenus
2037
+ };
2038
+ };
2039
+
2040
+ const handleKeyArrowLeftMenuOpen = state => {
2041
+ const {
2042
+ menus
2043
+ } = state;
2044
+ if (menus.length > 1) {
2045
+ return closeOneMenu(state);
1036
2046
  }
1037
- state.ctx = createCtx();
1038
- return state.ctx;
2047
+ return focusPrevious(state);
1039
2048
  };
1040
2049
 
1041
- const createCtx = () => {
1042
- const canvas = new OffscreenCanvas(0, 0);
1043
- const ctx = /** @type {OffscreenCanvasRenderingContext2D} */canvas.getContext('2d');
1044
- if (!ctx) {
1045
- throw new Error('Failed to get canvas context 2d');
2050
+ const handleKeyArrowLeft = ifElse(handleKeyArrowLeftMenuOpen, handleKeyArrowLeftMenuClosed);
2051
+
2052
+ // TODO menu should not be needed initially, only when item is selected and menu is opened
2053
+ const handleKeyArrowRightMenuOpen = async state => {
2054
+ const {
2055
+ menus
2056
+ } = state;
2057
+ // if menu can open sub menu to the right -> do that
2058
+ const menu = menus.at(-1);
2059
+ const {
2060
+ items,
2061
+ focusedIndex,
2062
+ x,
2063
+ y
2064
+ } = menu;
2065
+ if (focusedIndex === -1) {
2066
+ return focusNext(state);
2067
+ }
2068
+ const item = items[focusedIndex];
2069
+ if (item.flags === SubMenu) {
2070
+ const subMenuEntries = await getMenuEntries(item.id);
2071
+ const subMenu = {
2072
+ level: menus.length,
2073
+ items: subMenuEntries,
2074
+ focusedIndex: 0,
2075
+ y: y + focusedIndex * 25,
2076
+ x: x + MENU_WIDTH
2077
+ };
2078
+ const newParentMenu = {
2079
+ ...menu,
2080
+ expanded: true
2081
+ };
2082
+ const newMenus = [...menus.slice(0, -1), newParentMenu, subMenu];
2083
+ return {
2084
+ ...state,
2085
+ menus: newMenus
2086
+ };
1046
2087
  }
1047
- return ctx;
2088
+ return focusNext(state);
1048
2089
  };
1049
- const getContext = () => {
1050
- const ctx = getOrCreate(createCtx);
1051
- return ctx;
2090
+
2091
+ const handleKeyArrowRight = ifElse(handleKeyArrowRightMenuOpen, focusNext);
2092
+
2093
+ const noop = state => {
2094
+ return state;
1052
2095
  };
1053
2096
 
1054
- const px = value => {
1055
- return `${value}px`;
2097
+ const handleKeyArrowUpMenuOpen = state => {
2098
+ const {
2099
+ menus
2100
+ } = state;
2101
+ const menu = menus.at(-1);
2102
+ const previousIndex = getIndexToFocusPrevious(menu);
2103
+ const newMenus = [...menus.slice(0, -1), {
2104
+ ...menu,
2105
+ focusedIndex: previousIndex
2106
+ }];
2107
+ return {
2108
+ ...state,
2109
+ menus: newMenus
2110
+ };
1056
2111
  };
1057
2112
 
1058
- const getLetterSpacingString = letterSpacing => {
1059
- return px(letterSpacing);
2113
+ const handleKeyArrowUp = ifElse(handleKeyArrowUpMenuOpen, noop);
2114
+
2115
+ const handleKeyEndMenuOpen = state => {
2116
+ const {
2117
+ menus
2118
+ } = state;
2119
+ const menu = menus[0];
2120
+ const newFocusedIndex = getIndexToFocusLast(menu.items);
2121
+ const newMenus = [{
2122
+ ...menu,
2123
+ focusedIndex: newFocusedIndex
2124
+ }];
2125
+ return {
2126
+ ...state,
2127
+ menus: newMenus
2128
+ };
1060
2129
  };
1061
- const measureTextWidth = (text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth) => {
1062
- string(text);
1063
- number(fontWeight);
1064
- number(fontSize);
1065
- string(fontFamily);
1066
- boolean(isMonoSpaceFont);
1067
- number(charWidth);
1068
- if (typeof letterSpacing !== 'number') {
1069
- throw new TypeError('letterSpacing must be of type number');
2130
+
2131
+ // TODO this is also use for pagedown -> maybe find a better name for this function
2132
+ const handleKeyEnd = ifElse(handleKeyEndMenuOpen, focusLast);
2133
+
2134
+ const handleKeyEnterMenuClosed = state => {
2135
+ return openMenu(state, /* focus */true);
2136
+ };
2137
+
2138
+ const handleKeyEnterMenuOpen = state => {
2139
+ // TODO
2140
+ // await Menu.selectCurrent()
2141
+ return state;
2142
+ };
2143
+
2144
+ const handleKeyEnter = ifElse(handleKeyEnterMenuOpen, handleKeyEnterMenuClosed);
2145
+
2146
+ const handleKeyEscapeMenuOpen = state => {
2147
+ const {
2148
+ menus
2149
+ } = state;
2150
+ if (menus.length > 1) {
2151
+ return closeOneMenu(state);
1070
2152
  }
1071
- const letterSpacingString = getLetterSpacingString(letterSpacing);
1072
- const fontString = getFontString(fontWeight, fontSize, fontFamily);
1073
- const ctx = getContext();
1074
- // @ts-ignore
1075
- ctx.letterSpacing = letterSpacingString;
1076
- ctx.font = fontString;
1077
- const metrics = ctx.measureText(text);
1078
- const width = metrics.width;
1079
- return width;
2153
+ return closeMenu(state, /* keepFocus */true);
1080
2154
  };
1081
2155
 
1082
- const measureTitleBarEntryWidth = (label, fontWeight, fontSize, fontFamily, letterSpacing) => {
1083
- const isMonospaceFont = false;
1084
- const charWidth = 0;
1085
- return measureTextWidth(label, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
2156
+ const handleKeyEscape = ifElse(handleKeyEscapeMenuOpen, noop);
2157
+
2158
+ const handleKeyHomeMenuOpen = state => {
2159
+ const {
2160
+ menus
2161
+ } = state;
2162
+ const menu = menus[0];
2163
+ const newFocusedIndex = getIndexToFocusFirst(menu.items);
2164
+ const newMenus = [{
2165
+ ...menu,
2166
+ focusedIndex: newFocusedIndex
2167
+ }];
2168
+ return {
2169
+ ...state,
2170
+ menus: newMenus
2171
+ };
1086
2172
  };
1087
2173
 
1088
- const create = (id, uri, x, y, width, height) => {
2174
+ const handleKeyHome = ifElse(handleKeyHomeMenuOpen, focusFirst);
2175
+
2176
+ const handleKeySpaceMenuClosed = state => {
2177
+ return openMenu(state, /* focus */true);
2178
+ };
2179
+
2180
+ const handleKeySpaceMenuOpen = state => {
2181
+ // TODO
2182
+ // await Menu.selectCurrent()
2183
+ return state;
2184
+ };
2185
+
2186
+ // TODO this is same as handle key enter -> merge the functions
2187
+ const handleKeySpace = ifElse(handleKeySpaceMenuOpen, handleKeySpaceMenuClosed);
2188
+
2189
+ const executeMenuItemCommand = async item => {
2190
+ // TODO
2191
+ throw new Error('not implemented');
2192
+ };
2193
+
2194
+ const selectIndexIgnore = async (state, item) => {
2195
+ await executeMenuItemCommand();
2196
+ return state;
2197
+ };
2198
+
2199
+ const selectIndexNone = async (state, item) => {
2200
+ await executeMenuItemCommand();
1089
2201
  return {
1090
- uid: id,
1091
- titleBarEntries: [],
1092
- focusedIndex: -1,
1093
- isMenuOpen: false,
2202
+ ...state,
1094
2203
  menus: [],
1095
- labelFontWeight: 400,
1096
- labelFontSize: 13,
1097
- labelFontFamily: 'system-ui, Ubuntu, Droid Sans, sans-serif',
1098
- labelPadding: 8,
1099
- labelLetterSpacing: 0,
1100
- titleBarHeight: height,
2204
+ isMenuOpen: false
2205
+ };
2206
+ };
2207
+
2208
+ const selectIndexRestoreFocus = async (state, item) => {
2209
+ await executeMenuItemCommand();
2210
+ return {
2211
+ ...state,
2212
+ menus: [],
2213
+ isMenuOpen: false
2214
+ };
2215
+ };
2216
+
2217
+ const selectIndexSubMenu = async (state, menu, index) => {
2218
+ const {
2219
+ menus
2220
+ } = state;
2221
+ const {
2222
+ items,
1101
2223
  x,
1102
2224
  y,
1103
- width,
1104
- height
2225
+ level
2226
+ } = menu;
2227
+ const item = items[index];
2228
+ const subMenuEntries = await getMenuEntries(item.id);
2229
+ const subMenu = {
2230
+ level: menus.length,
2231
+ items: subMenuEntries,
2232
+ focusedIndex: -1,
2233
+ x: x + MENU_WIDTH,
2234
+ y: y + index * 25
2235
+ };
2236
+ const newParentMenu = {
2237
+ ...menu,
2238
+ focusedIndex: index
2239
+ };
2240
+ const newMenus = [...menus.slice(0, level - 1), newParentMenu, subMenu];
2241
+ return {
2242
+ ...state,
2243
+ menus: newMenus
1105
2244
  };
1106
2245
  };
1107
- const addWidths = (entries, labelPadding, fontWeight, fontSize, fontFamily, letterSpacing) => {
1108
- const withWidths = [];
1109
- for (const entry of entries) {
1110
- const textWidth = measureTitleBarEntryWidth(entry.label, fontWeight, fontSize, fontFamily, letterSpacing);
1111
- const width = textWidth + labelPadding * 2;
1112
- withWidths.push({
1113
- ...entry,
1114
- width
1115
- });
2246
+
2247
+ const handleMenuClick = (state, level, index) => {
2248
+ const {
2249
+ menus
2250
+ } = state;
2251
+ const menu = menus[level];
2252
+ const item = menu.items[index];
2253
+ switch (item.flags) {
2254
+ case None:
2255
+ return selectIndexNone(state);
2256
+ case SubMenu:
2257
+ return selectIndexSubMenu(state, menu, index);
2258
+ case RestoreFocus:
2259
+ return selectIndexRestoreFocus(state);
2260
+ case Ignore:
2261
+ return selectIndexIgnore(state);
2262
+ default:
2263
+ return state;
1116
2264
  }
1117
- return withWidths;
1118
2265
  };
1119
- const loadContent = async state => {
2266
+
2267
+ const handleMenuMouseOver = async (state, level, index) => {
2268
+ object(state);
2269
+ number(level);
2270
+ number(index);
1120
2271
  const {
1121
- labelFontFamily,
1122
- labelFontSize,
1123
- labelFontWeight,
1124
- labelLetterSpacing,
1125
- labelPadding
2272
+ menus
1126
2273
  } = state;
1127
- const titleBarEntries = [];
1128
- const withWidths = addWidths(titleBarEntries, labelPadding, labelFontWeight, labelFontSize, labelFontFamily, labelLetterSpacing);
1129
- // const visible = GetVisibleTitleBarEntries.getVisibleTitleBarEntries(withWidths, width)
1130
- // console.log({ visible })
2274
+ const menu = menus[level];
2275
+ const {
2276
+ items,
2277
+ focusedIndex,
2278
+ y,
2279
+ x
2280
+ } = menu;
2281
+ const item = items[index];
2282
+ if (focusedIndex === index) {
2283
+ if (index === -1) {
2284
+ return state;
2285
+ }
2286
+ if (item.flags === SubMenu && level === menus.length - 2) {
2287
+ const subMenu = menus[level + 1];
2288
+ if (subMenu.focusedIndex !== -1) {
2289
+ const newSubMenu = {
2290
+ ...subMenu,
2291
+ focusedIndex: -1
2292
+ };
2293
+ const newMenus = [...menus.slice(0, -1), newSubMenu];
2294
+ return {
2295
+ ...state,
2296
+ menus: newMenus
2297
+ };
2298
+ }
2299
+ }
2300
+ return state;
2301
+ }
2302
+ if (index === -1) {
2303
+ const newMenus = [...menus.slice(0, level), {
2304
+ ...menu,
2305
+ focusedIndex: -1
2306
+ }];
2307
+ return {
2308
+ ...state,
2309
+ menus: newMenus
2310
+ };
2311
+ }
2312
+ if (item.flags === SubMenu) {
2313
+ const item = items[index];
2314
+ const subMenuEntries = await getMenuEntries(item.id);
2315
+ const subMenu = {
2316
+ level: menus.length,
2317
+ items: subMenuEntries,
2318
+ focusedIndex: -1,
2319
+ y: y + index * 25,
2320
+ x: x + MENU_WIDTH
2321
+ };
2322
+ const newParentMenu = {
2323
+ ...menu,
2324
+ focusedIndex: index
2325
+ };
2326
+ const newMenus = [...menus.slice(0, level - 1), newParentMenu, subMenu];
2327
+ return {
2328
+ ...state,
2329
+ menus: newMenus
2330
+ };
2331
+ }
2332
+ const newMenus = [...menus.slice(0, level), {
2333
+ ...menu,
2334
+ focusedIndex: index
2335
+ }];
1131
2336
  return {
1132
2337
  ...state,
1133
- titleBarEntries: withWidths
2338
+ menus: newMenus
1134
2339
  };
1135
2340
  };
1136
2341
 
2342
+ const handleMouseOutMenuClosed = state => {
2343
+ return focusIndex(state, -1);
2344
+ };
2345
+
2346
+ const handleMouseOutMenuOpen = state => {
2347
+ return state;
2348
+ };
2349
+
2350
+ const handleMouseOut = ifElse(handleMouseOutMenuOpen, handleMouseOutMenuClosed);
2351
+
2352
+ const handleMouseOverMenuClosed = (state, index) => {
2353
+ return focusIndex(state, index);
2354
+ };
2355
+
2356
+ const handleMouseOverMenuOpen = (state, index) => {
2357
+ if (index === -1) {
2358
+ return state;
2359
+ }
2360
+ return focusIndex(state, index);
2361
+ };
2362
+
2363
+ const handleMouseOver = ifElse(handleMouseOverMenuOpen, handleMouseOverMenuClosed);
2364
+
2365
+ const toggleMenu = state => {
2366
+ const {
2367
+ isMenuOpen
2368
+ } = state;
2369
+ if (isMenuOpen) {
2370
+ return closeMenu(state, /* keepFocus */true);
2371
+ }
2372
+ return openMenu(state, /* focus */false);
2373
+ };
2374
+
1137
2375
  const commandMap = {
2376
+ 'TitleBarMenuBar.closeMenu': closeMenu,
1138
2377
  'TitleBarMenuBar.create': create,
2378
+ 'TitleBarMenuBar.focus': focus,
2379
+ 'TitleBarMenuBar.focusFirst': focusFirst,
2380
+ 'TitleBarMenuBar.focusIndex': focusLast,
2381
+ 'TitleBarMenuBar.focusLast': focusIndex,
2382
+ 'TitleBarMenuBar.focusNext': focusNext,
2383
+ 'TitleBarMenuBar.focusPrevious': focusPrevious,
2384
+ 'TitleBarMenuBar.getKeyBindings': getKeyBindings,
2385
+ 'TitleBarMenuBar.getMenus': getMenus,
1139
2386
  'TitleBarMenuBar.getVirtualDom': getTitleBarMenuBarVirtualDom,
2387
+ 'TitleBarMenuBar.handleClick': handleClick,
2388
+ 'TitleBarMenuBar.handleFocus': handleFocus,
2389
+ 'TitleBarMenuBar.handleKeyArrowDown': handleKeyArrowDown,
2390
+ 'TitleBarMenuBar.handleKeyArrowLeft': handleKeyArrowLeft,
2391
+ 'TitleBarMenuBar.handleKeyArrowRight': handleKeyArrowRight,
2392
+ 'TitleBarMenuBar.handleKeyArrowUp': handleKeyArrowUp,
2393
+ 'TitleBarMenuBar.handleKeyEnd': handleKeyEnd,
2394
+ 'TitleBarMenuBar.handleKeyEnter': handleKeyEnter,
2395
+ 'TitleBarMenuBar.handleKeyEscape': handleKeyEscape,
2396
+ 'TitleBarMenuBar.handleKeyHome': handleKeyHome,
2397
+ 'TitleBarMenuBar.handleKeySpace': handleKeySpace,
2398
+ 'TitleBarMenuBar.handleMenuClick': handleMenuClick,
2399
+ 'TitleBarMenuBar.handleMenuMouseOver': handleMenuMouseOver,
2400
+ 'TitleBarMenuBar.handleMouseOut': handleMouseOut,
2401
+ 'TitleBarMenuBar.handleMouseOver': handleMouseOver,
1140
2402
  'TitleBarMenuBar.loadContent': loadContent,
1141
- 'TitleBarMenuBar.getKeyBindings': getKeyBindings
2403
+ 'TitleBarMenuBar.toggleIndex': toggleIndex,
2404
+ 'TitleBarMenuBar.toggleMenu': toggleMenu
1142
2405
  };
1143
2406
 
1144
2407
  const listen = async () => {
1145
- await WebWorkerRpcClient.create({
2408
+ const rpc = await WebWorkerRpcClient.create({
1146
2409
  commandMap: commandMap
1147
2410
  });
2411
+ setRpc(rpc);
1148
2412
  };
1149
2413
 
1150
2414
  const main = async () => {