@lvce-editor/title-bar-worker 1.0.0 → 1.2.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,3 +1,444 @@
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;
9
+ };
10
+ const getCombinedMessage = (error, message) => {
11
+ const stringifiedError = normalizeLine(`${error}`);
12
+ if (message) {
13
+ return `${message}: ${stringifiedError}`;
14
+ }
15
+ return stringifiedError;
16
+ };
17
+ const NewLine$2 = '\n';
18
+ const getNewLineIndex$1 = (string, startIndex = undefined) => {
19
+ return string.indexOf(NewLine$2, startIndex);
20
+ };
21
+ const mergeStacks = (parent, child) => {
22
+ if (!child) {
23
+ return parent;
24
+ }
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;
37
+ };
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 {
58
+ constructor(message) {
59
+ super(message);
60
+ this.name = 'AssertionError';
61
+ }
62
+ }
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';
83
+ }
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');
89
+ }
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');
95
+ }
96
+ };
97
+ const array = value => {
98
+ const type = getType(value);
99
+ if (type !== 'array') {
100
+ throw new AssertionError('expected value to be of type array');
101
+ }
102
+ };
103
+ const string = value => {
104
+ const type = getType(value);
105
+ if (type !== 'string') {
106
+ throw new AssertionError('expected value to be of type string');
107
+ }
108
+ };
109
+ const boolean = value => {
110
+ const type = getType(value);
111
+ if (type !== 'boolean') {
112
+ throw new AssertionError('expected value to be of type boolean');
113
+ }
114
+ };
115
+
116
+ const isMessagePort = value => {
117
+ return value && value instanceof MessagePort;
118
+ };
119
+ const isMessagePortMain = value => {
120
+ return value && value.constructor && value.constructor.name === 'MessagePortMain';
121
+ };
122
+ const isOffscreenCanvas = value => {
123
+ return typeof OffscreenCanvas !== 'undefined' && value instanceof OffscreenCanvas;
124
+ };
125
+ const isInstanceOf = (value, constructorName) => {
126
+ return value?.constructor?.name === constructorName;
127
+ };
128
+ const isSocket = value => {
129
+ return isInstanceOf(value, 'Socket');
130
+ };
131
+ const transferrables = [isMessagePort, isMessagePortMain, isOffscreenCanvas, isSocket];
132
+ const isTransferrable = value => {
133
+ for (const fn of transferrables) {
134
+ if (fn(value)) {
135
+ return true;
136
+ }
137
+ }
138
+ return false;
139
+ };
140
+ const walkValue = (value, transferrables, isTransferrable) => {
141
+ if (!value) {
142
+ return;
143
+ }
144
+ if (isTransferrable(value)) {
145
+ transferrables.push(value);
146
+ return;
147
+ }
148
+ if (Array.isArray(value)) {
149
+ for (const item of value) {
150
+ walkValue(item, transferrables, isTransferrable);
151
+ }
152
+ return;
153
+ }
154
+ if (typeof value === 'object') {
155
+ for (const property of Object.values(value)) {
156
+ walkValue(property, transferrables, isTransferrable);
157
+ }
158
+ return;
159
+ }
160
+ };
161
+ const getTransferrables = value => {
162
+ const transferrables = [];
163
+ walkValue(value, transferrables, isTransferrable);
164
+ return transferrables;
165
+ };
166
+ const attachEvents = that => {
167
+ const handleMessage = (...args) => {
168
+ const data = that.getData(...args);
169
+ that.dispatchEvent(new MessageEvent('message', {
170
+ data
171
+ }));
172
+ };
173
+ that.onMessage(handleMessage);
174
+ const handleClose = event => {
175
+ that.dispatchEvent(new Event('close'));
176
+ };
177
+ that.onClose(handleClose);
178
+ };
179
+ class Ipc extends EventTarget {
180
+ constructor(rawIpc) {
181
+ super();
182
+ this._rawIpc = rawIpc;
183
+ attachEvents(this);
184
+ }
185
+ }
186
+ const E_INCOMPATIBLE_NATIVE_MODULE = 'E_INCOMPATIBLE_NATIVE_MODULE';
187
+ const E_MODULES_NOT_SUPPORTED_IN_ELECTRON = 'E_MODULES_NOT_SUPPORTED_IN_ELECTRON';
188
+ const ERR_MODULE_NOT_FOUND = 'ERR_MODULE_NOT_FOUND';
189
+ const NewLine$1 = '\n';
190
+ const joinLines$1 = lines => {
191
+ return lines.join(NewLine$1);
192
+ };
193
+ const RE_AT = /^\s+at/;
194
+ const RE_AT_PROMISE_INDEX = /^\s*at async Promise.all \(index \d+\)$/;
195
+ const isNormalStackLine = line => {
196
+ return RE_AT.test(line) && !RE_AT_PROMISE_INDEX.test(line);
197
+ };
198
+ const getDetails = lines => {
199
+ const index = lines.findIndex(isNormalStackLine);
200
+ if (index === -1) {
201
+ return {
202
+ actualMessage: joinLines$1(lines),
203
+ rest: []
204
+ };
205
+ }
206
+ let lastIndex = index - 1;
207
+ while (++lastIndex < lines.length) {
208
+ if (!isNormalStackLine(lines[lastIndex])) {
209
+ break;
210
+ }
211
+ }
212
+ return {
213
+ actualMessage: lines[index - 1],
214
+ rest: lines.slice(index, lastIndex)
215
+ };
216
+ };
217
+ const splitLines$1 = lines => {
218
+ return lines.split(NewLine$1);
219
+ };
220
+ const RE_MESSAGE_CODE_BLOCK_START = /^Error: The module '.*'$/;
221
+ const RE_MESSAGE_CODE_BLOCK_END = /^\s* at/;
222
+ const isMessageCodeBlockStartIndex = line => {
223
+ return RE_MESSAGE_CODE_BLOCK_START.test(line);
224
+ };
225
+ const isMessageCodeBlockEndIndex = line => {
226
+ return RE_MESSAGE_CODE_BLOCK_END.test(line);
227
+ };
228
+ const getMessageCodeBlock = stderr => {
229
+ const lines = splitLines$1(stderr);
230
+ const startIndex = lines.findIndex(isMessageCodeBlockStartIndex);
231
+ const endIndex = startIndex + lines.slice(startIndex).findIndex(isMessageCodeBlockEndIndex, startIndex);
232
+ const relevantLines = lines.slice(startIndex, endIndex);
233
+ const relevantMessage = relevantLines.join(' ').slice('Error: '.length);
234
+ return relevantMessage;
235
+ };
236
+ const isModuleNotFoundMessage = line => {
237
+ return line.includes('[ERR_MODULE_NOT_FOUND]');
238
+ };
239
+ const getModuleNotFoundError = stderr => {
240
+ const lines = splitLines$1(stderr);
241
+ const messageIndex = lines.findIndex(isModuleNotFoundMessage);
242
+ const message = lines[messageIndex];
243
+ return {
244
+ message,
245
+ code: ERR_MODULE_NOT_FOUND
246
+ };
247
+ };
248
+ const isModuleNotFoundError = stderr => {
249
+ if (!stderr) {
250
+ return false;
251
+ }
252
+ return stderr.includes('ERR_MODULE_NOT_FOUND');
253
+ };
254
+ const isModulesSyntaxError = stderr => {
255
+ if (!stderr) {
256
+ return false;
257
+ }
258
+ return stderr.includes('SyntaxError: Cannot use import statement outside a module');
259
+ };
260
+ const RE_NATIVE_MODULE_ERROR = /^innerError Error: Cannot find module '.*.node'/;
261
+ const RE_NATIVE_MODULE_ERROR_2 = /was compiled against a different Node.js version/;
262
+ const isUnhelpfulNativeModuleError = stderr => {
263
+ return RE_NATIVE_MODULE_ERROR.test(stderr) && RE_NATIVE_MODULE_ERROR_2.test(stderr);
264
+ };
265
+ const getNativeModuleErrorMessage = stderr => {
266
+ const message = getMessageCodeBlock(stderr);
267
+ return {
268
+ message: `Incompatible native node module: ${message}`,
269
+ code: E_INCOMPATIBLE_NATIVE_MODULE
270
+ };
271
+ };
272
+ const getModuleSyntaxError = () => {
273
+ return {
274
+ message: `ES Modules are not supported in electron`,
275
+ code: E_MODULES_NOT_SUPPORTED_IN_ELECTRON
276
+ };
277
+ };
278
+ const getHelpfulChildProcessError = (stdout, stderr) => {
279
+ if (isUnhelpfulNativeModuleError(stderr)) {
280
+ return getNativeModuleErrorMessage(stderr);
281
+ }
282
+ if (isModulesSyntaxError(stderr)) {
283
+ return getModuleSyntaxError();
284
+ }
285
+ if (isModuleNotFoundError(stderr)) {
286
+ return getModuleNotFoundError(stderr);
287
+ }
288
+ const lines = splitLines$1(stderr);
289
+ const {
290
+ actualMessage,
291
+ rest
292
+ } = getDetails(lines);
293
+ return {
294
+ message: actualMessage,
295
+ code: '',
296
+ stack: rest
297
+ };
298
+ };
299
+ class IpcError extends VError {
300
+ // @ts-ignore
301
+ constructor(betterMessage, stdout = '', stderr = '') {
302
+ if (stdout || stderr) {
303
+ // @ts-ignore
304
+ const {
305
+ message,
306
+ code,
307
+ stack
308
+ } = getHelpfulChildProcessError(stdout, stderr);
309
+ const cause = new Error(message);
310
+ // @ts-ignore
311
+ cause.code = code;
312
+ cause.stack = stack;
313
+ super(cause, betterMessage);
314
+ } else {
315
+ super(betterMessage);
316
+ }
317
+ // @ts-ignore
318
+ this.name = 'IpcError';
319
+ // @ts-ignore
320
+ this.stdout = stdout;
321
+ // @ts-ignore
322
+ this.stderr = stderr;
323
+ }
324
+ }
325
+ const readyMessage = 'ready';
326
+ const getData$2 = event => {
327
+ return event.data;
328
+ };
329
+ const listen$7 = () => {
330
+ // @ts-ignore
331
+ if (typeof WorkerGlobalScope === 'undefined') {
332
+ throw new TypeError('module is not in web worker scope');
333
+ }
334
+ return globalThis;
335
+ };
336
+ const signal$8 = global => {
337
+ global.postMessage(readyMessage);
338
+ };
339
+ class IpcChildWithModuleWorker extends Ipc {
340
+ getData(event) {
341
+ return getData$2(event);
342
+ }
343
+ send(message) {
344
+ // @ts-ignore
345
+ this._rawIpc.postMessage(message);
346
+ }
347
+ sendAndTransfer(message) {
348
+ const transfer = getTransferrables(message);
349
+ // @ts-ignore
350
+ this._rawIpc.postMessage(message, transfer);
351
+ }
352
+ dispose() {
353
+ // ignore
354
+ }
355
+ onClose(callback) {
356
+ // ignore
357
+ }
358
+ onMessage(callback) {
359
+ this._rawIpc.addEventListener('message', callback);
360
+ }
361
+ }
362
+ const wrap$f = global => {
363
+ return new IpcChildWithModuleWorker(global);
364
+ };
365
+ const withResolvers = () => {
366
+ let _resolve;
367
+ const promise = new Promise(resolve => {
368
+ _resolve = resolve;
369
+ });
370
+ return {
371
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
372
+ resolve: _resolve,
373
+ promise
374
+ };
375
+ };
376
+ const waitForFirstMessage = async port => {
377
+ const {
378
+ resolve,
379
+ promise
380
+ } = withResolvers();
381
+ port.addEventListener('message', resolve, {
382
+ once: true
383
+ });
384
+ const event = await promise;
385
+ // @ts-ignore
386
+ return event.data;
387
+ };
388
+ const listen$6 = async () => {
389
+ const parentIpcRaw = listen$7();
390
+ signal$8(parentIpcRaw);
391
+ const parentIpc = wrap$f(parentIpcRaw);
392
+ const firstMessage = await waitForFirstMessage(parentIpc);
393
+ if (firstMessage.method !== 'initialize') {
394
+ throw new IpcError('unexpected first message');
395
+ }
396
+ const type = firstMessage.params[0];
397
+ if (type === 'message-port') {
398
+ parentIpc.send({
399
+ jsonrpc: '2.0',
400
+ id: firstMessage.id,
401
+ result: null
402
+ });
403
+ parentIpc.dispose();
404
+ const port = firstMessage.params[1];
405
+ return port;
406
+ }
407
+ return globalThis;
408
+ };
409
+ class IpcChildWithModuleWorkerAndMessagePort extends Ipc {
410
+ getData(event) {
411
+ return getData$2(event);
412
+ }
413
+ send(message) {
414
+ this._rawIpc.postMessage(message);
415
+ }
416
+ sendAndTransfer(message) {
417
+ const transfer = getTransferrables(message);
418
+ this._rawIpc.postMessage(message, transfer);
419
+ }
420
+ dispose() {
421
+ if (this._rawIpc.close) {
422
+ this._rawIpc.close();
423
+ }
424
+ }
425
+ onClose(callback) {
426
+ // ignore
427
+ }
428
+ onMessage(callback) {
429
+ this._rawIpc.addEventListener('message', callback);
430
+ this._rawIpc.start();
431
+ }
432
+ }
433
+ const wrap$e = port => {
434
+ return new IpcChildWithModuleWorkerAndMessagePort(port);
435
+ };
436
+ const IpcChildWithModuleWorkerAndMessagePort$1 = {
437
+ __proto__: null,
438
+ listen: listen$6,
439
+ wrap: wrap$e
440
+ };
441
+
1
442
  const Two = '2.0';
2
443
  const create$4 = (method, params) => {
3
444
  return {
@@ -6,24 +447,19 @@ const create$4 = (method, params) => {
6
447
  params
7
448
  };
8
449
  };
9
- const state$1 = {
10
- callbacks: Object.create(null)
11
- };
12
- const set = (id, fn) => {
13
- state$1.callbacks[id] = fn;
450
+ const callbacks = Object.create(null);
451
+ const set$1 = (id, fn) => {
452
+ callbacks[id] = fn;
14
453
  };
15
- const get = id => {
16
- return state$1.callbacks[id];
454
+ const get$1 = id => {
455
+ return callbacks[id];
17
456
  };
18
457
  const remove = id => {
19
- delete state$1.callbacks[id];
458
+ delete callbacks[id];
20
459
  };
21
- let id = 0;
460
+ let id$a = 0;
22
461
  const create$3 = () => {
23
- return ++id;
24
- };
25
- const warn = (...args) => {
26
- console.warn(...args);
462
+ return ++id$a;
27
463
  };
28
464
  const registerPromise = () => {
29
465
  const id = create$3();
@@ -31,23 +467,13 @@ const registerPromise = () => {
31
467
  resolve,
32
468
  promise
33
469
  } = Promise.withResolvers();
34
- set(id, resolve);
470
+ set$1(id, resolve);
35
471
  return {
36
472
  id,
37
473
  promise
38
474
  };
39
475
  };
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;
46
- }
47
- fn(response);
48
- remove(id);
49
- };
50
- const create$2 = (method, params) => {
476
+ const create$2$1 = (method, params) => {
51
477
  const {
52
478
  id,
53
479
  promise
@@ -69,7 +495,7 @@ class JsonRpcError extends Error {
69
495
  this.name = 'JsonRpcError';
70
496
  }
71
497
  }
72
- const NewLine$2 = '\n';
498
+ const NewLine = '\n';
73
499
  const DomException = 'DOMException';
74
500
  const ReferenceError$1 = 'ReferenceError';
75
501
  const SyntaxError$1 = 'SyntaxError';
@@ -114,40 +540,40 @@ const constructError = (message, type, name) => {
114
540
  }
115
541
  return new ErrorConstructor(message);
116
542
  };
117
- const getNewLineIndex$1 = (string, startIndex = undefined) => {
118
- return string.indexOf(NewLine$2, startIndex);
543
+ const getNewLineIndex = (string, startIndex = undefined) => {
544
+ return string.indexOf(NewLine, startIndex);
119
545
  };
120
546
  const getParentStack = error => {
121
547
  let parentStack = error.stack || error.data || error.message || '';
122
548
  if (parentStack.startsWith(' at')) {
123
- parentStack = error.message + NewLine$2 + parentStack;
549
+ parentStack = error.message + NewLine + parentStack;
124
550
  }
125
551
  return parentStack;
126
552
  };
127
- const joinLines$1 = lines => {
128
- return lines.join(NewLine$2);
553
+ const joinLines = lines => {
554
+ return lines.join(NewLine);
129
555
  };
130
556
  const MethodNotFound = -32601;
131
557
  const Custom = -32001;
132
- const splitLines$1 = lines => {
133
- return lines.split(NewLine$2);
558
+ const splitLines = lines => {
559
+ return lines.split(NewLine);
134
560
  };
135
561
  const restoreJsonRpcError = error => {
136
562
  if (error && error instanceof Error) {
137
563
  return error;
138
564
  }
139
- const currentStack = joinLines$1(splitLines$1(new Error().stack || '').slice(1));
565
+ const currentStack = joinLines(splitLines(new Error().stack || '').slice(1));
140
566
  if (error && error.code && error.code === MethodNotFound) {
141
567
  const restoredError = new JsonRpcError(error.message);
142
568
  const parentStack = getParentStack(error);
143
- restoredError.stack = parentStack + NewLine$2 + currentStack;
569
+ restoredError.stack = parentStack + NewLine + currentStack;
144
570
  return restoredError;
145
571
  }
146
572
  if (error && error.message) {
147
573
  const restoredError = constructError(error.message, error.type, error.name);
148
574
  if (error.data) {
149
575
  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;
576
+ restoredError.stack = error.data.type + ': ' + error.message + NewLine + error.data.stack + NewLine + currentStack;
151
577
  } else if (error.data.stack) {
152
578
  restoredError.stack = error.data.stack;
153
579
  }
@@ -167,7 +593,7 @@ const restoreJsonRpcError = error => {
167
593
  if (error.stack) {
168
594
  const lowerStack = restoredError.stack || '';
169
595
  // @ts-ignore
170
- const indexNewLine = getNewLineIndex$1(lowerStack);
596
+ const indexNewLine = getNewLineIndex(lowerStack);
171
597
  const parentStack = getParentStack(error);
172
598
  // @ts-ignore
173
599
  restoredError.stack = parentStack + lowerStack.slice(indexNewLine);
@@ -194,6 +620,19 @@ const unwrapJsonRpcResult = responseMessage => {
194
620
  }
195
621
  throw new JsonRpcError('unexpected response message');
196
622
  };
623
+ const warn = (...args) => {
624
+ console.warn(...args);
625
+ };
626
+ const resolve = (id, response) => {
627
+ const fn = get$1(id);
628
+ if (!fn) {
629
+ console.log(response);
630
+ warn(`callback ${id} may already be disposed`);
631
+ return;
632
+ }
633
+ fn(response);
634
+ remove(id);
635
+ };
197
636
  const E_COMMAND_NOT_FOUND = 'E_COMMAND_NOT_FOUND';
198
637
  const getErrorType = prettyError => {
199
638
  if (prettyError && prettyError.type) {
@@ -320,831 +759,2235 @@ const handleJsonRpcMessage = async (...args) => {
320
759
  await getResponse(message, ipc, execute, preparePrettyError, logError, requiresSocket);
321
760
  return;
322
761
  }
323
- throw new JsonRpcError('unexpected message');
762
+ throw new JsonRpcError('unexpected message');
763
+ };
764
+ const invokeHelper = async (ipc, method, params, useSendAndTransfer) => {
765
+ const {
766
+ message,
767
+ promise
768
+ } = create$2$1(method, params);
769
+ if (useSendAndTransfer && ipc.sendAndTransfer) {
770
+ ipc.sendAndTransfer(message);
771
+ } else {
772
+ ipc.send(message);
773
+ }
774
+ const responseMessage = await promise;
775
+ return unwrapJsonRpcResult(responseMessage);
776
+ };
777
+ const send = (transport, method, ...params) => {
778
+ const message = create$4(method, params);
779
+ transport.send(message);
780
+ };
781
+ const invoke$1 = (ipc, method, ...params) => {
782
+ return invokeHelper(ipc, method, params, false);
783
+ };
784
+ const invokeAndTransfer = (ipc, method, ...params) => {
785
+ return invokeHelper(ipc, method, params, true);
786
+ };
787
+
788
+ const commands = Object.create(null);
789
+ const register = commandMap => {
790
+ Object.assign(commands, commandMap);
791
+ };
792
+ const getCommand = key => {
793
+ return commands[key];
794
+ };
795
+ const execute = (command, ...args) => {
796
+ const fn = getCommand(command);
797
+ if (!fn) {
798
+ throw new Error(`command not found ${command}`);
799
+ }
800
+ return fn(...args);
801
+ };
802
+
803
+ const createRpc = ipc => {
804
+ const rpc = {
805
+ // @ts-ignore
806
+ ipc,
807
+ /**
808
+ * @deprecated
809
+ */
810
+ send(method, ...params) {
811
+ send(ipc, method, ...params);
812
+ },
813
+ invoke(method, ...params) {
814
+ return invoke$1(ipc, method, ...params);
815
+ },
816
+ invokeAndTransfer(method, ...params) {
817
+ return invokeAndTransfer(ipc, method, ...params);
818
+ }
819
+ };
820
+ return rpc;
821
+ };
822
+ const requiresSocket = () => {
823
+ return false;
824
+ };
825
+ const preparePrettyError = error => {
826
+ return error;
827
+ };
828
+ const logError = () => {
829
+ // handled by renderer worker
830
+ };
831
+ const handleMessage = event => {
832
+ const actualRequiresSocket = event?.target?.requiresSocket || requiresSocket;
833
+ const actualExecute = event?.target?.execute || execute;
834
+ return handleJsonRpcMessage(event.target, event.data, actualExecute, resolve, preparePrettyError, logError, actualRequiresSocket);
835
+ };
836
+ const handleIpc = ipc => {
837
+ if ('addEventListener' in ipc) {
838
+ ipc.addEventListener('message', handleMessage);
839
+ } else if ('on' in ipc) {
840
+ // deprecated
841
+ ipc.on('message', handleMessage);
842
+ }
843
+ };
844
+ const listen$1 = async (module, options) => {
845
+ const rawIpc = await module.listen(options);
846
+ if (module.signal) {
847
+ module.signal(rawIpc);
848
+ }
849
+ const ipc = module.wrap(rawIpc);
850
+ return ipc;
851
+ };
852
+ const create$2 = async ({
853
+ commandMap
854
+ }) => {
855
+ // TODO create a commandMap per rpc instance
856
+ register(commandMap);
857
+ const ipc = await listen$1(IpcChildWithModuleWorkerAndMessagePort$1);
858
+ handleIpc(ipc);
859
+ const rpc = createRpc(ipc);
860
+ return rpc;
861
+ };
862
+ const WebWorkerRpcClient = {
863
+ __proto__: null,
864
+ create: create$2
865
+ };
866
+
867
+ const Menu$1 = 'menu';
868
+ const MenuBar = 'menubar';
869
+ const MenuItem$1 = 'menuitem';
870
+ const MenuItemCheckBox = 'menuitemcheckbox';
871
+ const Separator$1 = 'separator';
872
+
873
+ const Menu = 'Menu';
874
+ const MenuItem = 'MenuItem';
875
+ const MenuItemFocused = 'MenuItemFocused';
876
+ const MenuItemSeparator = 'MenuItemSeparator';
877
+ const MenuItemSeparatorLine = 'MenuItemSeparatorLine';
878
+ const MenuItemSubMenu = 'MenuItemSubMenu';
879
+ const MenuItemSubMenuArrowRight = 'MenuItemSubMenuArrowRight';
880
+ const TitleBarEntryActive = 'TitleBarEntryActive';
881
+ const TitleBarTopLevelEntry = 'TitleBarTopLevelEntry';
882
+ const TitleBarTopLevelEntryLabel = 'TitleBarTopLevelEntryLabel';
883
+
884
+ const getKeyBindingString = (key, altKey, ctrlKey, shiftKey, metaKey) => {
885
+ let string = '';
886
+ if (ctrlKey) {
887
+ string += 'Ctrl+';
888
+ }
889
+ if (shiftKey) {
890
+ string += 'Shift+';
891
+ }
892
+ string += key.toUpperCase();
893
+ return string;
894
+ };
895
+
896
+ const Separator = 1;
897
+ const None = 0;
898
+ const SubMenu = 4;
899
+ const Checked = 2;
900
+ const Unchecked = 3;
901
+ const Disabled = 5;
902
+ const RestoreFocus = 6;
903
+ const Ignore = 7;
904
+
905
+ const Backspace$1 = 1;
906
+ const Tab$1 = 2;
907
+ const Enter$1 = 3;
908
+ const Escape$1 = 8;
909
+ const Space$1 = 9;
910
+ const PageUp$1 = 10;
911
+ const PageDown$1 = 11;
912
+ const End$1 = 255;
913
+ const Home$1 = 12;
914
+ const LeftArrow$1 = 13;
915
+ const UpArrow$1 = 14;
916
+ const RightArrow$1 = 15;
917
+ const DownArrow$1 = 16;
918
+ const Insert$1 = 17;
919
+ const Delete$1 = 18;
920
+ const Digit0$1 = 19;
921
+ const Digit1$1 = 20;
922
+ const Digit2$1 = 21;
923
+ const Digit3$1 = 22;
924
+ const Digit4$1 = 23;
925
+ const Digit5$1 = 24;
926
+ const Digit6$1 = 25;
927
+ const Digit7$1 = 26;
928
+ const Digit8$1 = 27;
929
+ const Digit9$1 = 28;
930
+ const KeyA$1 = 29;
931
+ const KeyB$1 = 30;
932
+ const KeyC$1 = 31;
933
+ const KeyD$1 = 32;
934
+ const KeyE$1 = 33;
935
+ const KeyF$1 = 34;
936
+ const KeyG$1 = 35;
937
+ const KeyH$1 = 36;
938
+ const KeyI$1 = 37;
939
+ const KeyJ$1 = 38;
940
+ const KeyK$1 = 39;
941
+ const KeyL$1 = 40;
942
+ const KeyM$1 = 41;
943
+ const KeyN$1 = 42;
944
+ const KeyO$1 = 43;
945
+ const KeyP$1 = 44;
946
+ const KeyQ$1 = 45;
947
+ const KeyR$1 = 46;
948
+ const KeyS$1 = 47;
949
+ const KeyT$1 = 48;
950
+ const KeyU$1 = 49;
951
+ const KeyV$1 = 50;
952
+ const KeyW$1 = 51;
953
+ const KeyX$1 = 52;
954
+ const KeyY$1 = 53;
955
+ const KeyZ$1 = 54;
956
+ const F1$1 = 57;
957
+ const F2$1 = 58;
958
+ const F3$1 = 59;
959
+ const F4$1 = 60;
960
+ const F5$1 = 61;
961
+ const F6$1 = 62;
962
+ const Equal$1 = 84;
963
+ const Comma$1 = 85;
964
+ const Minus$1 = 86;
965
+ const Backquote$1 = 89;
966
+ const Backslash$1 = 91;
967
+ const Star$1 = 131;
968
+ const Plus$1 = 132;
969
+
970
+ const Unknown = 'Unknown';
971
+ const Backspace = 'Backspace';
972
+ const Tab = 'Tab';
973
+ const Enter = 'Enter';
974
+ const Escape = 'Escape';
975
+ const Space = 'Space';
976
+ const PageUp = 'PageUp';
977
+ const PageDown = 'PageDown';
978
+ const End = 'End';
979
+ const Home = 'Home';
980
+ const LeftArrow = 'LeftArrow';
981
+ const UpArrow = 'UpArrow';
982
+ const RightArrow = 'RightArrow';
983
+ const DownArrow = 'DownArrow';
984
+ const Insert = 'Insert';
985
+ const Delete = 'Delete';
986
+ const Digit0 = '0';
987
+ const Digit1 = '1';
988
+ const Digit2 = '2';
989
+ const Digit3 = '3';
990
+ const Digit4 = '4';
991
+ const Digit5 = '5';
992
+ const Digit6 = '6';
993
+ const Digit7 = '7';
994
+ const Digit8 = '8';
995
+ const Digit9 = '9';
996
+ const KeyA = 'a';
997
+ const KeyB = 'b';
998
+ const KeyC = 'c';
999
+ const KeyD = 'd';
1000
+ const KeyE = 'e';
1001
+ const KeyF = 'f';
1002
+ const KeyG = 'g';
1003
+ const KeyH = 'h';
1004
+ const KeyI = 'i';
1005
+ const KeyJ = 'j';
1006
+ const KeyK = 'k';
1007
+ const KeyL = 'l';
1008
+ const KeyM = 'm';
1009
+ const KeyN = 'n';
1010
+ const KeyO = 'o';
1011
+ const KeyP = 'p';
1012
+ const KeyQ = 'q';
1013
+ const KeyR = 'r';
1014
+ const KeyS = 's';
1015
+ const KeyT = 't';
1016
+ const KeyU = 'u';
1017
+ const KeyV = 'v';
1018
+ const KeyW = 'w';
1019
+ const KeyX = 'x';
1020
+ const KeyY = 'y';
1021
+ const KeyZ = 'z';
1022
+ const F1 = 'F1';
1023
+ const F2 = 'F2';
1024
+ const F3 = 'F3';
1025
+ const F4 = 'F4';
1026
+ const F5 = 'F5';
1027
+ const F6 = 'F6';
1028
+ const Equal = '=';
1029
+ const Comma = ',';
1030
+ const Minus = 'Minus';
1031
+ const Backquote = 'Backquote';
1032
+ const Backslash = 'Backslash';
1033
+ const Star = '*';
1034
+ const Plus = '+';
1035
+
1036
+ const getKeyCodeString = keyCode => {
1037
+ switch (keyCode) {
1038
+ case Backspace$1:
1039
+ return Backspace;
1040
+ case Tab$1:
1041
+ return Tab;
1042
+ case Escape$1:
1043
+ return Escape;
1044
+ case Enter$1:
1045
+ return Enter;
1046
+ case Space$1:
1047
+ return Space;
1048
+ case PageUp$1:
1049
+ return PageUp;
1050
+ case PageDown$1:
1051
+ return PageDown;
1052
+ case End$1:
1053
+ return End;
1054
+ case Home$1:
1055
+ return Home;
1056
+ case LeftArrow$1:
1057
+ return LeftArrow;
1058
+ case UpArrow$1:
1059
+ return UpArrow;
1060
+ case RightArrow$1:
1061
+ return RightArrow;
1062
+ case DownArrow$1:
1063
+ return DownArrow;
1064
+ case Insert$1:
1065
+ return Insert;
1066
+ case Delete$1:
1067
+ return Delete;
1068
+ case Digit0$1:
1069
+ return Digit0;
1070
+ case Digit1$1:
1071
+ return Digit1;
1072
+ case Digit2$1:
1073
+ return Digit2;
1074
+ case Digit3$1:
1075
+ return Digit3;
1076
+ case Digit4$1:
1077
+ return Digit4;
1078
+ case Digit5$1:
1079
+ return Digit5;
1080
+ case Digit6$1:
1081
+ return Digit6;
1082
+ case Digit7$1:
1083
+ return Digit7;
1084
+ case Digit8$1:
1085
+ return Digit8;
1086
+ case Digit9$1:
1087
+ return Digit9;
1088
+ case KeyA$1:
1089
+ return KeyA;
1090
+ case KeyB$1:
1091
+ return KeyB;
1092
+ case KeyC$1:
1093
+ return KeyC;
1094
+ case KeyD$1:
1095
+ return KeyD;
1096
+ case KeyE$1:
1097
+ return KeyE;
1098
+ case KeyF$1:
1099
+ return KeyF;
1100
+ case KeyG$1:
1101
+ return KeyG;
1102
+ case KeyH$1:
1103
+ return KeyH;
1104
+ case KeyI$1:
1105
+ return KeyI;
1106
+ case KeyJ$1:
1107
+ return KeyJ;
1108
+ case KeyK$1:
1109
+ return KeyK;
1110
+ case KeyL$1:
1111
+ return KeyL;
1112
+ case KeyM$1:
1113
+ return KeyM;
1114
+ case KeyN$1:
1115
+ return KeyN;
1116
+ case KeyO$1:
1117
+ return KeyO;
1118
+ case KeyP$1:
1119
+ return KeyP;
1120
+ case KeyQ$1:
1121
+ return KeyQ;
1122
+ case KeyR$1:
1123
+ return KeyR;
1124
+ case KeyS$1:
1125
+ return KeyS;
1126
+ case KeyT$1:
1127
+ return KeyT;
1128
+ case KeyU$1:
1129
+ return KeyU;
1130
+ case KeyV$1:
1131
+ return KeyV;
1132
+ case KeyW$1:
1133
+ return KeyW;
1134
+ case KeyX$1:
1135
+ return KeyX;
1136
+ case KeyY$1:
1137
+ return KeyY;
1138
+ case KeyZ$1:
1139
+ return KeyZ;
1140
+ case F1$1:
1141
+ return F1;
1142
+ case F2$1:
1143
+ return F2;
1144
+ case F3$1:
1145
+ return F3;
1146
+ case F4$1:
1147
+ return F4;
1148
+ case F5$1:
1149
+ return F5;
1150
+ case F6$1:
1151
+ return F6;
1152
+ case Backslash$1:
1153
+ return Backslash;
1154
+ case Equal$1:
1155
+ return Equal;
1156
+ case Comma$1:
1157
+ return Comma;
1158
+ case Backquote$1:
1159
+ return Backquote;
1160
+ case Plus$1:
1161
+ return Plus;
1162
+ case Star$1:
1163
+ return Star;
1164
+ case Minus$1:
1165
+ return Minus;
1166
+ default:
1167
+ return Unknown;
1168
+ }
1169
+ };
1170
+
1171
+ const CtrlCmd = 1 << 11 >>> 0;
1172
+ const Shift = 1 << 10 >>> 0;
1173
+
1174
+ const parseKey = rawKey => {
1175
+ number(rawKey);
1176
+ const isCtrl = Boolean(rawKey & CtrlCmd);
1177
+ const isShift = Boolean(rawKey & Shift);
1178
+ const keyCode = rawKey & 0x00_00_00_ff;
1179
+ const key = getKeyCodeString(keyCode);
1180
+ return {
1181
+ key,
1182
+ isCtrl,
1183
+ isShift
1184
+ };
1185
+ };
1186
+
1187
+ const Div = 4;
1188
+ const Span = 8;
1189
+ const Text = 12;
1190
+
1191
+ const text = data => {
1192
+ return {
1193
+ type: Text,
1194
+ text: data,
1195
+ childCount: 0
1196
+ };
1197
+ };
1198
+
1199
+ const separator = {
1200
+ type: Div,
1201
+ className: MenuItemSeparator,
1202
+ role: Separator$1,
1203
+ childCount: 1
1204
+ };
1205
+ const separatorLine = {
1206
+ type: Div,
1207
+ className: MenuItemSeparatorLine,
1208
+ childCount: 0
1209
+ };
1210
+ const checkboxUnchecked = {
1211
+ type: Div,
1212
+ className: MenuItem,
1213
+ role: MenuItemCheckBox,
1214
+ ariaChecked: false,
1215
+ tabIndex: -1,
1216
+ childCount: 1
1217
+ };
1218
+ const checkboxChecked = {
1219
+ type: Div,
1220
+ className: `${MenuItem} MenuItemCheckMark`,
1221
+ role: MenuItemCheckBox,
1222
+ ariaChecked: true,
1223
+ tabIndex: -1,
1224
+ childCount: 2
1225
+ };
1226
+ const disabled = {
1227
+ type: Div,
1228
+ className: MenuItem,
1229
+ role: MenuItem$1,
1230
+ tabIndex: -1,
1231
+ disabled: true,
1232
+ childCount: 1
1233
+ };
1234
+ const arrowRight = {
1235
+ type: Div,
1236
+ className: MenuItemSubMenuArrowRight,
1237
+ childCount: 0
1238
+ };
1239
+ const getMenuItemSeparatorDom = menuItem => {
1240
+ return [separator, separatorLine];
1241
+ };
1242
+ const getMenuItemCheckedDom = menuItem => {
1243
+ const {
1244
+ label
1245
+ } = menuItem;
1246
+ return [checkboxChecked, {
1247
+ type: Div,
1248
+ className: 'MenuItemCheckmarkIcon MaskIconCheck'
1249
+ }, text(label)];
1250
+ };
1251
+ const getMenuItemUncheckedDom = menuItem => {
1252
+ const {
1253
+ label
1254
+ } = menuItem;
1255
+ return [checkboxUnchecked, text(label)];
1256
+ };
1257
+ const getMenuItemDisabledDom = menuItem => {
1258
+ const {
1259
+ label
1260
+ } = menuItem;
1261
+ return [disabled, text(label)];
1262
+ };
1263
+ const getMenuItemDefaultDom = menuItem => {
1264
+ const {
1265
+ label,
1266
+ isFocused,
1267
+ key
1268
+ } = menuItem;
1269
+ let className = MenuItem;
1270
+ if (isFocused) {
1271
+ className += ' ' + MenuItemFocused;
1272
+ }
1273
+ const dom = [];
1274
+ dom.push({
1275
+ type: Div,
1276
+ className,
1277
+ role: MenuItem$1,
1278
+ tabIndex: -1,
1279
+ childCount: 1
1280
+ }, text(label));
1281
+ if (key) {
1282
+ dom[0].childCount++;
1283
+ const parsedKey = parseKey(key);
1284
+ const keyBindingsString = getKeyBindingString(parsedKey.key, false, parsedKey.isCtrl, parsedKey.isShift);
1285
+ dom.push({
1286
+ type: Span,
1287
+ className: 'MenuItemKeyBinding',
1288
+ childCount: 1
1289
+ }, text(keyBindingsString));
1290
+ }
1291
+ return dom;
1292
+ };
1293
+ const getMenuItemSubMenuDom = menuItem => {
1294
+ const {
1295
+ label,
1296
+ isFocused,
1297
+ isExpanded,
1298
+ level
1299
+ } = menuItem;
1300
+ let className = MenuItem;
1301
+ className += ' ' + MenuItemSubMenu;
1302
+ if (isFocused) {
1303
+ className += ' ' + MenuItemFocused;
1304
+ }
1305
+ return [{
1306
+ type: Div,
1307
+ className,
1308
+ role: MenuItem$1,
1309
+ tabIndex: -1,
1310
+ ariaHasPopup: true,
1311
+ ariaExpanded: isExpanded,
1312
+ ariaOwns: isExpanded ? `Menu-${level + 1}` : undefined,
1313
+ childCount: 2
1314
+ }, text(label), arrowRight];
1315
+ };
1316
+ const getMenuItemVirtualDom = menuItem => {
1317
+ const {
1318
+ flags
1319
+ } = menuItem;
1320
+ switch (flags) {
1321
+ case None:
1322
+ case RestoreFocus:
1323
+ case Ignore:
1324
+ return getMenuItemDefaultDom(menuItem);
1325
+ case Separator:
1326
+ return getMenuItemSeparatorDom();
1327
+ case Checked:
1328
+ return getMenuItemCheckedDom(menuItem);
1329
+ case Unchecked:
1330
+ return getMenuItemUncheckedDom(menuItem);
1331
+ case Disabled:
1332
+ return getMenuItemDisabledDom(menuItem);
1333
+ case SubMenu:
1334
+ return getMenuItemSubMenuDom(menuItem);
1335
+ default:
1336
+ return [];
1337
+ }
1338
+ };
1339
+ const getMenuVirtualDom = menuItems => {
1340
+ const dom = [];
1341
+ dom.push({
1342
+ type: Div,
1343
+ className: Menu,
1344
+ role: Menu$1,
1345
+ tabIndex: -1,
1346
+ childCount: menuItems.length
1347
+ });
1348
+ dom.push(...menuItems.flatMap(getMenuItemVirtualDom));
1349
+ return dom;
1350
+ };
1351
+
1352
+ const HandleClick = 'handleClick';
1353
+ const HandleFocusIn = 'handleFocusIn';
1354
+ const HandleFocusOut = 'handleFocusOut';
1355
+ const HandlePointerOut = 'handlePointerOut';
1356
+ const HandlePointerOver = 'handlePointerOver';
1357
+
1358
+ const getItemVirtualDom = item => {
1359
+ // @ts-ignore
1360
+ const {
1361
+ keyboardShortCut,
1362
+ label,
1363
+ isOpen,
1364
+ isFocused
1365
+ } = item;
1366
+ const dom = [];
1367
+ dom.push({
1368
+ type: Div,
1369
+ className: TitleBarTopLevelEntry,
1370
+ ariaHasPopup: true,
1371
+ ariaExpanded: isOpen,
1372
+ role: MenuItem$1,
1373
+ childCount: 1,
1374
+ ariaKeyShortcuts: keyboardShortCut
1375
+ });
1376
+ if (isOpen) {
1377
+ // @ts-ignore
1378
+ dom[0].ariaOwns = 'Menu-0';
1379
+ }
1380
+ if (isFocused) {
1381
+ dom[0].className += ' ' + TitleBarEntryActive;
1382
+ // @ts-ignore
1383
+ dom[0].id = 'TitleBarEntryActive';
1384
+ dom.push({
1385
+ type: Div,
1386
+ className: TitleBarTopLevelEntryLabel,
1387
+ childCount: 1
1388
+ });
1389
+ }
1390
+ dom.push(text(label));
1391
+ return dom;
324
1392
  };
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);
1393
+ const getTitleBarMenuBarItemsVirtualDom = visibleItems => {
1394
+ const dom = visibleItems.flatMap(getItemVirtualDom);
1395
+ return dom;
337
1396
  };
338
- const send = (transport, method, ...params) => {
339
- const message = create$4(method, params);
340
- transport.send(message);
1397
+
1398
+ const getTitleBarMenuBarVirtualDom = visibleItems => {
1399
+ return [{
1400
+ type: Div,
1401
+ className: 'Viewlet TitleBarMenuBar',
1402
+ role: MenuBar,
1403
+ tabIndex: 0,
1404
+ childCount: visibleItems.length,
1405
+ onMouseDown: HandleClick,
1406
+ onFocusOut: HandleFocusOut,
1407
+ onFocusIn: HandleFocusIn,
1408
+ onPointerOver: HandlePointerOver,
1409
+ onPointerOut: HandlePointerOut
1410
+ }, ...getTitleBarMenuBarItemsVirtualDom(visibleItems)];
341
1411
  };
342
- const invoke = (ipc, method, ...params) => {
343
- return invokeHelper(ipc, method, params, false);
1412
+
1413
+ const getVisible = (items, focusedIndex, expanded, level) => {
1414
+ const visibleItems = [];
1415
+ const {
1416
+ length
1417
+ } = items;
1418
+ for (let i = 0; i < length; i++) {
1419
+ const item = items[i];
1420
+ const {
1421
+ flags,
1422
+ label
1423
+ } = item;
1424
+ visibleItems.push({
1425
+ label,
1426
+ flags,
1427
+ isFocused: i === focusedIndex,
1428
+ isExpanded: i === focusedIndex && expanded,
1429
+ level,
1430
+ key: item.key
1431
+ });
1432
+ }
1433
+ return visibleItems;
344
1434
  };
345
- const invokeAndTransfer = (ipc, method, ...params) => {
346
- return invokeHelper(ipc, method, params, true);
1435
+
1436
+ const Ellipsis = 'Ellipsis';
1437
+
1438
+ const emptyObject = {};
1439
+ const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
1440
+ const i18nString = (key, placeholders = emptyObject) => {
1441
+ if (placeholders === emptyObject) {
1442
+ return key;
1443
+ }
1444
+ const replacer = (match, rest) => {
1445
+ return placeholders[rest];
1446
+ };
1447
+ return key.replaceAll(RE_PLACEHOLDER, replacer);
347
1448
  };
348
1449
 
349
- const commands = Object.create(null);
350
- const register = commandMap => {
351
- Object.assign(commands, commandMap);
1450
+ const About = 'About';
1451
+ const CheckForUpdates = 'Check For Updates';
1452
+ const ClearRecentlyOpened = 'Clear Recently Opened';
1453
+ const Edit$1 = 'Edit';
1454
+ const File$1 = 'File';
1455
+ const Go$1 = 'Go';
1456
+ const Help$1 = 'Help';
1457
+ const MoreDot = 'More ...';
1458
+ const Run$1 = 'Run';
1459
+ const Selection$1 = 'Selection';
1460
+ const Terminal$1 = 'Terminal';
1461
+ const View$1 = 'View';
1462
+
1463
+ const moreDot = () => {
1464
+ return i18nString(MoreDot);
352
1465
  };
353
- const getCommand = key => {
354
- return commands[key];
1466
+ const clearRecentlyOpened = () => {
1467
+ return i18nString(ClearRecentlyOpened);
355
1468
  };
356
- const execute = (command, ...args) => {
357
- const fn = getCommand(command);
358
- if (!fn) {
359
- throw new Error(`command not found ${command}`);
1469
+
1470
+ const getVisibleTitleBarEntries = (entries, width, focusedIndex, isMenuOpen) => {
1471
+ array(entries);
1472
+ number(width);
1473
+ let total = 0;
1474
+ const visible = [];
1475
+ for (let i = 0; i < entries.length; i++) {
1476
+ const entry = entries[i];
1477
+ total += entry.width;
1478
+ if (total >= width) {
1479
+ break;
1480
+ }
1481
+ const isOpen = i === focusedIndex && isMenuOpen;
1482
+ const isFocused = i === focusedIndex;
1483
+ visible.push({
1484
+ ...entry,
1485
+ isOpen,
1486
+ isFocused
1487
+ });
360
1488
  }
361
- return fn(...args);
1489
+ const hasOverflow = visible.length < entries.length;
1490
+ if (hasOverflow) {
1491
+ const padding = 8;
1492
+ const moreIconWidth = 22;
1493
+ const totalPadding = padding * 2;
1494
+ const hasStillOverflow = total + moreIconWidth + totalPadding > width;
1495
+ if (hasStillOverflow) {
1496
+ visible.pop();
1497
+ }
1498
+ visible.push({
1499
+ ariaLabel: moreDot(),
1500
+ icon: Ellipsis,
1501
+ label: '',
1502
+ width: moreIconWidth + totalPadding
1503
+ });
1504
+ }
1505
+ return visible;
362
1506
  };
363
1507
 
364
- const getData$1 = event => {
365
- return event.data;
1508
+ const SetFocusedIndex = 'setFocusedIndex';
1509
+ const SetMenus = 'setMenus';
1510
+
1511
+ const renderTitleBarEntries = {
1512
+ isEqual(oldState, newState) {
1513
+ return oldState.titleBarEntries === newState.titleBarEntries && oldState.width === newState.width && oldState.focusedIndex === newState.focusedIndex && oldState.isMenuOpen === newState.isMenuOpen;
1514
+ },
1515
+ apply(oldState, newState) {
1516
+ const visibleEntries = getVisibleTitleBarEntries(newState.titleBarEntries, newState.width, newState.focusedIndex, newState.isMenuOpen);
1517
+ const dom = getTitleBarMenuBarVirtualDom(visibleEntries);
1518
+ return ['Viewlet.setDom2', dom];
1519
+ }
1520
+ };
1521
+ const renderFocusedIndex = {
1522
+ isEqual(oldState, newState) {
1523
+ return oldState.focusedIndex === newState.focusedIndex && oldState.isMenuOpen === newState.isMenuOpen;
1524
+ },
1525
+ apply(oldState, newState) {
1526
+ return [/* method */SetFocusedIndex, /* oldFocusedIndex */oldState.focusedIndex, /* newfocusedIndex */newState.focusedIndex, /* oldIsMenuOpen */oldState.isMenuOpen, /* newIsMenuOpen */newState.isMenuOpen];
1527
+ }
1528
+ };
1529
+ const renderMenus = {
1530
+ isEqual(oldState, newState) {
1531
+ return oldState.menus === newState.menus;
1532
+ },
1533
+ apply(oldState, newState) {
1534
+ const oldMenus = oldState.menus;
1535
+ const newMenus = newState.menus;
1536
+ const oldLength = oldMenus.length;
1537
+ const newLength = newMenus.length;
1538
+ const commonLength = Math.min(oldLength, newLength);
1539
+ const changes = [];
1540
+ for (let i = 0; i < commonLength; i++) {
1541
+ const oldMenu = oldMenus[i];
1542
+ const newMenu = newMenus[i];
1543
+ if (oldMenu !== newMenu) {
1544
+ const visible = getVisible(newMenu.items, newMenu.focusedIndex, newMenu.expanded, newMenu.level);
1545
+ const dom = getMenuVirtualDom(visible).slice(1);
1546
+ changes.push([/* method */'updateMenu', newMenu, /* newLength */newLength, dom]);
1547
+ }
1548
+ }
1549
+ const difference = newLength - oldLength;
1550
+ if (difference > 0) {
1551
+ const newMenu = newMenus.at(-1);
1552
+ const visible = getVisible(newMenu.items, newMenu.focusedIndex, newMenu.expanded, newMenu.level);
1553
+ const dom = getMenuVirtualDom(visible).slice(1);
1554
+ changes.push(['addMenu', newMenu, dom]);
1555
+ } else if (difference < 0) {
1556
+ changes.push(['closeMenus', newLength]);
1557
+ }
1558
+ return [/* method */SetMenus, /* changes */changes, newState.uid];
1559
+ }
366
1560
  };
367
- const attachEvents = that => {
368
- const handleMessage = (...args) => {
369
- const data = that.getData(...args);
370
- that.dispatchEvent(new MessageEvent('message', {
371
- data
372
- }));
373
- };
374
- that.onMessage(handleMessage);
375
- const handleClose = event => {
376
- that.dispatchEvent(new Event('close'));
1561
+ const render = [renderTitleBarEntries, renderFocusedIndex, renderMenus];
1562
+
1563
+ const create$1 = () => {
1564
+ const states = Object.create(null);
1565
+ return {
1566
+ get(uid) {
1567
+ return states[uid];
1568
+ },
1569
+ set(uid, oldState, newState) {
1570
+ states[uid] = {
1571
+ oldState,
1572
+ newState
1573
+ };
1574
+ }
377
1575
  };
378
- that.onClose(handleClose);
379
1576
  };
380
- class Ipc extends EventTarget {
381
- constructor(rawIpc) {
382
- super();
383
- this._rawIpc = rawIpc;
384
- attachEvents(this);
385
- }
386
- }
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;
401
- }
402
- if (typeof value === 'object') {
403
- for (const property of Object.values(value)) {
404
- walkValue(property, transferrables, isTransferrable);
1577
+
1578
+ const {
1579
+ get,
1580
+ set
1581
+ } = create$1();
1582
+
1583
+ const doRender = async uid => {
1584
+ const {
1585
+ oldState,
1586
+ newState
1587
+ } = get(uid);
1588
+ const commands = [];
1589
+ for (const item of render) {
1590
+ if (!item.isEqual(oldState, newState)) {
1591
+ commands.push(item.apply(oldState, newState));
405
1592
  }
406
- return;
407
1593
  }
1594
+ return commands;
408
1595
  };
409
- const isMessagePort = value => {
410
- return value && value instanceof MessagePort;
1596
+
1597
+ const commandsIds = ['closeMenu', 'focus', 'focusFirst', 'focusIndex', 'focusLast', 'focusNext', 'focusPrevious', 'handleKeyArrowDown', 'handleKeyArrowLeft', 'handleKeyArrowRight', 'handleKeyArrowUp', 'handleKeyEnd', 'handleKeyEnter', 'handleKeyEscape', 'handleKeyHome', 'handleKeySpace', 'handleMenuClick', 'handleMenuMouseOver', 'handleMouseOver', 'handleMouseOut', 'toggleIndex', 'toggleMenu', 'handleClick', 'handleFocus'];
1598
+ const getCommandIds = () => {
1599
+ return commandsIds;
411
1600
  };
412
- const isMessagePortMain = value => {
413
- return value && value.constructor && value.constructor.name === 'MessagePortMain';
1601
+
1602
+ const FocusTitleBarMenuBar = 26;
1603
+
1604
+ const getKeyBindings = () => {
1605
+ return [{
1606
+ key: DownArrow$1,
1607
+ command: 'TitleBarMenuBar.handleKeyArrowDown',
1608
+ when: FocusTitleBarMenuBar
1609
+ }, {
1610
+ key: UpArrow$1,
1611
+ command: 'TitleBarMenuBar.handleKeyArrowUp',
1612
+ when: FocusTitleBarMenuBar
1613
+ }, {
1614
+ key: RightArrow$1,
1615
+ command: 'TitleBarMenuBar.handleKeyArrowRight',
1616
+ when: FocusTitleBarMenuBar
1617
+ }, {
1618
+ key: LeftArrow$1,
1619
+ command: 'TitleBarMenuBar.handleKeyArrowLeft',
1620
+ when: FocusTitleBarMenuBar
1621
+ }, {
1622
+ key: Space$1,
1623
+ command: 'TitleBarMenuBar.handleKeySpace',
1624
+ when: FocusTitleBarMenuBar
1625
+ }, {
1626
+ key: Home$1,
1627
+ command: 'TitleBarMenuBar.handleKeyHome',
1628
+ when: FocusTitleBarMenuBar
1629
+ }, {
1630
+ key: End$1,
1631
+ command: 'TitleBarMenuBar.handleKeyEnd',
1632
+ when: FocusTitleBarMenuBar
1633
+ }, {
1634
+ key: Escape$1,
1635
+ command: 'TitleBarMenuBar.handleKeyEscape',
1636
+ when: FocusTitleBarMenuBar
1637
+ }];
414
1638
  };
415
- const isOffscreenCanvas = value => {
416
- return typeof OffscreenCanvas !== 'undefined' && value instanceof OffscreenCanvas;
1639
+
1640
+ const getFontString = (fontWeight, fontSize, fontFamily) => {
1641
+ return `${fontWeight} ${fontSize}px ${fontFamily}`;
417
1642
  };
418
- const isInstanceOf = (value, constructorName) => {
419
- return value?.constructor?.name === constructorName;
1643
+
1644
+ const state$1 = {
1645
+ ctx: undefined
420
1646
  };
421
- const isSocket = value => {
422
- return isInstanceOf(value, 'Socket');
1647
+ const getOrCreate = createCtx => {
1648
+ if (state$1.ctx) {
1649
+ return state$1.ctx;
1650
+ }
1651
+ state$1.ctx = createCtx();
1652
+ return state$1.ctx;
423
1653
  };
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
- }
1654
+
1655
+ const createCtx = () => {
1656
+ const canvas = new OffscreenCanvas(0, 0);
1657
+ const ctx = /** @type {OffscreenCanvasRenderingContext2D} */canvas.getContext('2d');
1658
+ if (!ctx) {
1659
+ throw new Error('Failed to get canvas context 2d');
430
1660
  }
431
- return false;
1661
+ return ctx;
432
1662
  };
433
- const getTransferrables = value => {
434
- const transferrables = [];
435
- walkValue(value, transferrables, isTransferrable);
436
- return transferrables;
1663
+ const getContext = () => {
1664
+ const ctx = getOrCreate(createCtx);
1665
+ return ctx;
437
1666
  };
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;
1667
+
1668
+ const px = value => {
1669
+ return `${value}px`;
444
1670
  };
445
- const signal$2 = global => {
446
- global.postMessage(readyMessage);
1671
+
1672
+ const getLetterSpacingString = letterSpacing => {
1673
+ return px(letterSpacing);
447
1674
  };
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);
1675
+ const measureTextWidth = (text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth) => {
1676
+ string(text);
1677
+ number(fontWeight);
1678
+ number(fontSize);
1679
+ string(fontFamily);
1680
+ boolean(isMonoSpaceFont);
1681
+ number(charWidth);
1682
+ if (typeof letterSpacing !== 'number') {
1683
+ throw new TypeError('letterSpacing must be of type number');
469
1684
  }
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);
1685
+ const letterSpacingString = getLetterSpacingString(letterSpacing);
1686
+ const fontString = getFontString(fontWeight, fontSize, fontFamily);
1687
+ const ctx = getContext();
1688
+ // @ts-ignore
1689
+ ctx.letterSpacing = letterSpacingString;
1690
+ ctx.font = fontString;
1691
+ const metrics = ctx.measureText(text);
1692
+ const {
1693
+ width
1694
+ } = metrics;
1695
+ return width;
480
1696
  };
481
- const splitLines = lines => {
482
- return lines.split(NewLine$1);
1697
+
1698
+ const measureTitleBarEntryWidth = (label, fontWeight, fontSize, fontFamily, letterSpacing) => {
1699
+ const isMonospaceFont = false;
1700
+ const charWidth = 0;
1701
+ return measureTextWidth(label, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
483
1702
  };
484
- const isModuleNotFoundMessage = line => {
485
- return line.includes('[ERR_MODULE_NOT_FOUND]');
1703
+
1704
+ const addWidths = (entries, labelPadding, fontWeight, fontSize, fontFamily, letterSpacing) => {
1705
+ const withWidths = [];
1706
+ for (const entry of entries) {
1707
+ const textWidth = measureTitleBarEntryWidth(entry.label, fontWeight, fontSize, fontFamily, letterSpacing);
1708
+ const width = textWidth + labelPadding * 2;
1709
+ withWidths.push({
1710
+ ...entry,
1711
+ width
1712
+ });
1713
+ }
1714
+ return withWidths;
486
1715
  };
487
- const getModuleNotFoundError = stderr => {
488
- const lines = splitLines(stderr);
489
- const messageIndex = lines.findIndex(isModuleNotFoundMessage);
490
- const message = lines[messageIndex];
1716
+
1717
+ const loadContent = async (state, titleBarEntries) => {
1718
+ const {
1719
+ labelFontFamily,
1720
+ labelFontSize,
1721
+ labelFontWeight,
1722
+ labelLetterSpacing,
1723
+ labelPadding
1724
+ } = state;
1725
+ const withWidths = addWidths(titleBarEntries, labelPadding, labelFontWeight, labelFontSize, labelFontFamily, labelLetterSpacing);
491
1726
  return {
492
- message,
493
- code: ERR_MODULE_NOT_FOUND
1727
+ ...state,
1728
+ titleBarEntries: withWidths
494
1729
  };
495
1730
  };
496
- const RE_NATIVE_MODULE_ERROR = /^innerError Error: Cannot find module '.*.node'/;
497
- 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
- const isUnhelpfulNativeModuleError = stderr => {
503
- return RE_NATIVE_MODULE_ERROR.test(stderr) && RE_NATIVE_MODULE_ERROR_2.test(stderr);
1731
+
1732
+ /**
1733
+ * @enum {string}
1734
+ */
1735
+ const UiStrings$2 = {
1736
+ Copy: 'Copy',
1737
+ CopyLineDown: 'Copy Line Down',
1738
+ CopyLineUp: 'Copy Line Up',
1739
+ Cut: 'Cut',
1740
+ MoveLineDown: 'Move Line Down',
1741
+ MoveLineUp: 'Move Line Up',
1742
+ Paste: 'Paste',
1743
+ Redo: 'Redo',
1744
+ SelectAll: 'Select All',
1745
+ ToggleBlockComment: 'Toggle Block Comment',
1746
+ ToggleLineComment: 'Toggle Line Comment',
1747
+ Undo: 'Undo'};
1748
+ const cut = () => {
1749
+ return i18nString(UiStrings$2.Cut);
1750
+ };
1751
+ const copy = () => {
1752
+ return i18nString(UiStrings$2.Copy);
1753
+ };
1754
+ const paste = () => {
1755
+ return i18nString(UiStrings$2.Paste);
1756
+ };
1757
+ const undo = () => {
1758
+ return i18nString(UiStrings$2.Undo);
1759
+ };
1760
+ const redo = () => {
1761
+ return i18nString(UiStrings$2.Redo);
1762
+ };
1763
+ const toggleLineComment = () => {
1764
+ return i18nString(UiStrings$2.ToggleLineComment);
1765
+ };
1766
+ const toggleBlockComment = () => {
1767
+ return i18nString(UiStrings$2.ToggleBlockComment);
1768
+ };
1769
+ const selectAll = () => {
1770
+ return i18nString(UiStrings$2.SelectAll);
1771
+ };
1772
+ const copyLineUp = () => {
1773
+ return i18nString(UiStrings$2.CopyLineUp);
1774
+ };
1775
+ const copyLineDown = () => {
1776
+ return i18nString(UiStrings$2.CopyLineDown);
1777
+ };
1778
+ const moveLineUp = () => {
1779
+ return i18nString(UiStrings$2.MoveLineUp);
1780
+ };
1781
+ const moveLineDown = () => {
1782
+ return i18nString(UiStrings$2.MoveLineDown);
504
1783
  };
505
- const isMessageCodeBlockStartIndex = line => {
506
- return RE_MESSAGE_CODE_BLOCK_START.test(line);
1784
+
1785
+ const Edit = 2;
1786
+ const File = 5;
1787
+ const Go = 6;
1788
+ const Help = 7;
1789
+ const OpenRecent = 9;
1790
+ const Run = 10;
1791
+ const Selection = 11;
1792
+ const Terminal = 14;
1793
+ const TitleBar = 15;
1794
+ const View = 16;
1795
+
1796
+ const menuEntrySeparator = {
1797
+ id: 'separator',
1798
+ label: '',
1799
+ flags: Separator,
1800
+ command: ''
507
1801
  };
508
- const isMessageCodeBlockEndIndex = line => {
509
- return RE_MESSAGE_CODE_BLOCK_END.test(line);
1802
+
1803
+ const id$9 = Edit;
1804
+ const getMenuEntries$c = () => {
1805
+ return [{
1806
+ id: 'undo',
1807
+ label: undo(),
1808
+ flags: Disabled,
1809
+ command: /* TODO */-1
1810
+ }, {
1811
+ id: 'redo',
1812
+ label: redo(),
1813
+ flags: Disabled,
1814
+ command: /* TODO */-1
1815
+ }, menuEntrySeparator, {
1816
+ id: 'cut',
1817
+ label: cut(),
1818
+ flags: None,
1819
+ command: /* Editor.cut */'Editor.cut'
1820
+ }, {
1821
+ id: 'copy',
1822
+ label: copy(),
1823
+ flags: None,
1824
+ command: /* Editor.copy */'Editor.copy'
1825
+ }, {
1826
+ id: 'paste',
1827
+ label: paste(),
1828
+ flags: None,
1829
+ command: /* Editor.paste */'Editor.paste'
1830
+ }, menuEntrySeparator, {
1831
+ id: 'toggle-line-comment',
1832
+ label: toggleLineComment(),
1833
+ flags: None,
1834
+ command: /* Editor.toggleLineComment */'Editor.toggleLineComment'
1835
+ }, {
1836
+ id: 'toggle-block-comment',
1837
+ label: toggleBlockComment(),
1838
+ flags: None,
1839
+ command: /* Editor.toggleBlockComment */'Editor.toggleBlockComment'
1840
+ }];
510
1841
  };
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;
1842
+
1843
+ const MenuEntriesEdit = {
1844
+ __proto__: null,
1845
+ getMenuEntries: getMenuEntries$c,
1846
+ id: id$9
518
1847
  };
519
- const getNativeModuleErrorMessage = stderr => {
520
- const message = getMessageCodeBlock(stderr);
521
- return {
522
- message: `Incompatible native node module: ${message}`,
523
- code: E_INCOMPATIBLE_NATIVE_MODULE
524
- };
1848
+
1849
+ /**
1850
+ * @enum {string}
1851
+ */
1852
+ const UiStrings$1 = {
1853
+ NewFile: 'New File',
1854
+ NewWindow: 'New Window',
1855
+ OpenFile: 'Open File',
1856
+ OpenFolder: 'Open Folder',
1857
+ OpenRecent: 'Open Recent',
1858
+ Save: 'Save',
1859
+ SaveAll: 'Save All'
525
1860
  };
526
- const isModulesSyntaxError = stderr => {
527
- if (!stderr) {
528
- return false;
529
- }
530
- return stderr.includes('SyntaxError: Cannot use import statement outside a module');
1861
+ const newFile = () => {
1862
+ return i18nString(UiStrings$1.NewFile);
531
1863
  };
532
- const getModuleSyntaxError = () => {
533
- return {
534
- message: `ES Modules are not supported in electron`,
535
- code: E_MODULES_NOT_SUPPORTED_IN_ELECTRON
536
- };
1864
+ const newWindow = () => {
1865
+ return i18nString(UiStrings$1.NewWindow);
537
1866
  };
538
- const isModuleNotFoundError = stderr => {
539
- if (!stderr) {
540
- return false;
541
- }
542
- return stderr.includes('ERR_MODULE_NOT_FOUND');
1867
+ const openFile = () => {
1868
+ return i18nString(UiStrings$1.OpenFile);
543
1869
  };
544
- const isNormalStackLine = line => {
545
- return RE_AT.test(line) && !RE_AT_PROMISE_INDEX.test(line);
1870
+ const openFolder = () => {
1871
+ return i18nString(UiStrings$1.OpenFolder);
546
1872
  };
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
- };
1873
+ const openRecent = () => {
1874
+ return i18nString(UiStrings$1.OpenRecent);
565
1875
  };
566
- const getHelpfulChildProcessError = (stdout, stderr) => {
567
- if (isUnhelpfulNativeModuleError(stderr)) {
568
- return getNativeModuleErrorMessage(stderr);
569
- }
570
- if (isModulesSyntaxError(stderr)) {
571
- return getModuleSyntaxError();
572
- }
573
- if (isModuleNotFoundError(stderr)) {
574
- return getModuleNotFoundError(stderr);
575
- }
576
- const lines = splitLines(stderr);
577
- const {
578
- actualMessage,
579
- rest
580
- } = getDetails(lines);
581
- return {
582
- message: `${actualMessage}`,
583
- code: '',
584
- stack: rest
585
- };
1876
+ const save = () => {
1877
+ return i18nString(UiStrings$1.Save);
586
1878
  };
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;
1879
+ const saveAll = () => {
1880
+ return i18nString(UiStrings$1.SaveAll);
595
1881
  };
596
- const getCombinedMessage = (error, message) => {
597
- const stringifiedError = normalizeLine(`${error}`);
598
- if (message) {
599
- return `${message}: ${stringifiedError}`;
600
- }
601
- return stringifiedError;
1882
+
1883
+ const platform = 1;
1884
+
1885
+ const Web = 1;
1886
+
1887
+ const id$8 = File;
1888
+ const getMenuEntries$b = () => {
1889
+ const entries = [{
1890
+ id: 'newFile',
1891
+ label: newFile(),
1892
+ flags: None,
1893
+ command: -1
1894
+ }, {
1895
+ id: 'newWindow',
1896
+ label: newWindow(),
1897
+ flags: None,
1898
+ command: /* Window.openNew */'Window.openNew'
1899
+ }, menuEntrySeparator, {
1900
+ id: 'openFile',
1901
+ label: openFile(),
1902
+ flags: None,
1903
+ command: 'Dialog.openFile'
1904
+ }, {
1905
+ id: 'openFolder',
1906
+ label: openFolder(),
1907
+ flags: RestoreFocus,
1908
+ command: 'Dialog.openFolder'
1909
+ }, {
1910
+ id: OpenRecent,
1911
+ label: openRecent(),
1912
+ flags: SubMenu,
1913
+ command: ''
1914
+ }, menuEntrySeparator, {
1915
+ id: 'save',
1916
+ label: save(),
1917
+ flags: None,
1918
+ command: 'Main.save'
1919
+ }, {
1920
+ id: 'saveAll',
1921
+ label: saveAll(),
1922
+ flags: None,
1923
+ command: 'Main.saveAll'
1924
+ }];
1925
+ return entries;
602
1926
  };
603
- const NewLine = '\n';
604
- const getNewLineIndex = (string, startIndex = undefined) => {
605
- return string.indexOf(NewLine, startIndex);
1927
+
1928
+ const MenuEntriesFile = {
1929
+ __proto__: null,
1930
+ getMenuEntries: getMenuEntries$b,
1931
+ id: id$8
606
1932
  };
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;
1933
+
1934
+ const id$7 = Go;
1935
+ const getMenuEntries$a = () => {
1936
+ return [];
623
1937
  };
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
- class IpcError extends VError {
643
- // @ts-ignore
644
- constructor(betterMessage, stdout = '', stderr = '') {
645
- if (stdout || stderr) {
646
- // @ts-ignore
647
- const {
648
- message,
649
- code,
650
- stack
651
- } = getHelpfulChildProcessError(stdout, stderr);
652
- const cause = new Error(message);
653
- // @ts-ignore
654
- cause.code = code;
655
- cause.stack = stack;
656
- super(cause, betterMessage);
657
- } else {
658
- super(betterMessage);
659
- }
660
- // @ts-ignore
661
- this.name = 'IpcError';
662
- // @ts-ignore
663
- this.stdout = stdout;
664
- // @ts-ignore
665
- this.stderr = stderr;
666
- }
667
- }
668
- const withResolvers = () => {
669
- let _resolve;
670
- const promise = new Promise(resolve => {
671
- _resolve = resolve;
672
- });
673
- return {
674
- resolve: _resolve,
675
- promise
676
- };
1938
+
1939
+ const MenuEntriesGo = {
1940
+ __proto__: null,
1941
+ getMenuEntries: getMenuEntries$a,
1942
+ id: id$7
677
1943
  };
678
- const waitForFirstMessage = async port => {
679
- const {
680
- resolve,
681
- promise
682
- } = withResolvers();
683
- port.addEventListener('message', resolve, {
684
- once: true
685
- });
686
- const event = await promise;
687
- // @ts-ignore
688
- return event.data;
1944
+
1945
+ const checkForUpdates = () => {
1946
+ return i18nString(CheckForUpdates);
689
1947
  };
690
- const listen$1$1 = async () => {
691
- const parentIpcRaw = listen$2();
692
- signal$2(parentIpcRaw);
693
- const parentIpc = wrap$5(parentIpcRaw);
694
- const firstMessage = await waitForFirstMessage(parentIpc);
695
- if (firstMessage.method !== 'initialize') {
696
- throw new IpcError('unexpected first message');
697
- }
698
- const type = firstMessage.params[0];
699
- if (type === 'message-port') {
700
- parentIpc.send({
701
- jsonrpc: '2.0',
702
- id: firstMessage.id,
703
- result: null
704
- });
705
- parentIpc.dispose();
706
- const port = firstMessage.params[1];
707
- return port;
708
- }
709
- return globalThis;
1948
+ const about = () => {
1949
+ return i18nString(About);
710
1950
  };
711
- class IpcChildWithModuleWorkerAndMessagePort extends Ipc {
712
- constructor(port) {
713
- super(port);
714
- }
715
- getData(event) {
716
- return getData$1(event);
717
- }
718
- send(message) {
719
- this._rawIpc.postMessage(message);
720
- }
721
- sendAndTransfer(message) {
722
- const transfer = getTransferrables(message);
723
- this._rawIpc.postMessage(message, transfer);
724
- }
725
- dispose() {
726
- if (this._rawIpc.close) {
727
- this._rawIpc.close();
728
- }
1951
+
1952
+ const isAutoUpdateSupported = () => {
1953
+ {
1954
+ return false;
729
1955
  }
730
- onClose(callback) {
731
- // ignore
1956
+ };
1957
+
1958
+ const id$6 = Help;
1959
+ const getMenuEntries$9 = async () => {
1960
+ const autoUpdateSupported = isAutoUpdateSupported();
1961
+ const entries = [];
1962
+ if (autoUpdateSupported) {
1963
+ entries.push(menuEntrySeparator, {
1964
+ id: 'checkForUpdates',
1965
+ label: checkForUpdates(),
1966
+ flags: RestoreFocus,
1967
+ command: 'AutoUpdater.checkForUpdates'
1968
+ });
732
1969
  }
733
- onMessage(callback) {
734
- this._rawIpc.addEventListener('message', callback);
735
- this._rawIpc.start();
1970
+ if (entries.length > 0) {
1971
+ entries.push(menuEntrySeparator);
736
1972
  }
737
- }
738
- const wrap$4 = port => {
739
- return new IpcChildWithModuleWorkerAndMessagePort(port);
740
- };
741
- const IpcChildWithModuleWorkerAndMessagePort$1 = {
742
- __proto__: null,
743
- listen: listen$1$1,
744
- wrap: wrap$4
1973
+ entries.push({
1974
+ id: 'about',
1975
+ label: about(),
1976
+ flags: RestoreFocus,
1977
+ command: 'About.showAbout'
1978
+ });
1979
+ return entries;
745
1980
  };
746
1981
 
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
- }
761
- };
762
- return rpc;
763
- };
764
- const requiresSocket = () => {
765
- return false;
1982
+ const MenuEntriesHelp = {
1983
+ __proto__: null,
1984
+ getMenuEntries: getMenuEntries$9,
1985
+ id: id$6
766
1986
  };
767
- const preparePrettyError = error => {
768
- return error;
1987
+
1988
+ const state = {
1989
+ rpc: undefined
769
1990
  };
770
- const logError = () => {
771
- // handled by renderer worker
1991
+ const invoke = (method, ...params) => {
1992
+ const {
1993
+ rpc
1994
+ } = state;
1995
+ // @ts-ignore
1996
+ return rpc.invoke(method, ...params);
772
1997
  };
773
- const handleMessage = event => {
774
- return handleJsonRpcMessage(event.target, event.data, execute, resolve, preparePrettyError, logError, requiresSocket);
1998
+ const setRpc = rpc => {
1999
+ state.rpc = rpc;
775
2000
  };
776
- const handleIpc = ipc => {
777
- ipc.addEventListener('message', handleMessage);
2001
+
2002
+ const getTitle = uri => {
2003
+ if (!uri) {
2004
+ return '';
2005
+ }
2006
+ return uri;
778
2007
  };
779
2008
 
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;
2009
+ const MAX_MENU_RECENT_ENTRIES = 10;
2010
+ const toMenuItem = folder => {
2011
+ const label = getTitle(folder);
2012
+ return {
2013
+ label,
2014
+ flags: None,
2015
+ command: 'Workspace.setPath',
2016
+ args: [folder]
2017
+ };
786
2018
  };
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;
2019
+ const getRecentlyOpened = () => {
2020
+ return invoke(/* RecentlyOpened.getRecentlyOpened */'RecentlyOpened.getRecentlyOpened');
2021
+ };
2022
+ const id$5 = OpenRecent;
2023
+ const getMenuEntries$8 = async () => {
2024
+ const allItems = await getRecentlyOpened();
2025
+ const itemsToShow = allItems.slice(0, MAX_MENU_RECENT_ENTRIES);
2026
+ const items = [];
2027
+ if (itemsToShow.length > 0) {
2028
+ items.push(...itemsToShow.map(toMenuItem), menuEntrySeparator);
2029
+ }
2030
+ items.push({
2031
+ id: 'more',
2032
+ label: moreDot(),
2033
+ flags: None,
2034
+ command: 'QuickPick.showRecent'
2035
+ }, menuEntrySeparator, {
2036
+ id: 'clearRecentlyOpened',
2037
+ label: clearRecentlyOpened(),
2038
+ flags: None,
2039
+ command: 'RecentlyOpened.clearRecentlyOpened'
2040
+ });
2041
+ return items;
796
2042
  };
797
- const WebWorkerRpcClient = {
2043
+
2044
+ const MenuEntriesOpenRecent = {
798
2045
  __proto__: null,
799
- create: create$1
800
- };
801
-
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;
2046
+ getMenuEntries: getMenuEntries$8,
2047
+ id: id$5
2048
+ };
816
2049
 
817
- const CtrlCmd = 1 << 11 >>> 0;
818
- const Alt = 1 << 9 >>> 0;
2050
+ const id$4 = Run;
2051
+ const getMenuEntries$7 = () => {
2052
+ return [];
2053
+ };
819
2054
 
820
- const FocusExplorer = 13;
821
- const FocusExplorerEditBox = 14;
2055
+ const MenuEntriesRun = {
2056
+ __proto__: null,
2057
+ getMenuEntries: getMenuEntries$7,
2058
+ id: id$4
2059
+ };
822
2060
 
823
- const getKeyBindings = () => {
2061
+ const id$3 = Selection;
2062
+ const getMenuEntries$6 = () => {
824
2063
  return [{
825
- key: RightArrow,
826
- command: 'Explorer.handleArrowRight',
827
- when: FocusExplorer
828
- }, {
829
- key: LeftArrow,
830
- command: 'Explorer.handleArrowLeft',
831
- when: FocusExplorer
2064
+ id: 'selectAll',
2065
+ label: selectAll(),
2066
+ flags: None,
2067
+ command: 'Editor.selectAll'
832
2068
  }, {
833
- key: Home,
834
- command: 'Explorer.focusFirst',
835
- when: FocusExplorer
2069
+ id: 'copyLineUp',
2070
+ label: copyLineUp(),
2071
+ flags: None,
2072
+ command: 'Editor.copyLineUp'
836
2073
  }, {
837
- key: End,
838
- command: 'Explorer.focusLast',
839
- when: FocusExplorer
2074
+ id: 'copyLineDown',
2075
+ label: copyLineDown(),
2076
+ flags: None,
2077
+ command: 'Editor.copyLineDown'
840
2078
  }, {
841
- key: UpArrow,
842
- command: 'Explorer.focusPrevious',
843
- when: FocusExplorer
2079
+ id: 'moveLineUp',
2080
+ label: moveLineUp(),
2081
+ flags: Disabled,
2082
+ command: 'Editor.moveLineUp'
844
2083
  }, {
845
- key: DownArrow,
846
- command: 'Explorer.focusNext',
847
- when: FocusExplorer
2084
+ id: 'moveLineDown',
2085
+ label: moveLineDown(),
2086
+ flags: Disabled,
2087
+ command: 'Editor.moveLineDown'
2088
+ }];
2089
+ };
2090
+
2091
+ const MenuEntriesSelection = {
2092
+ __proto__: null,
2093
+ getMenuEntries: getMenuEntries$6,
2094
+ id: id$3
2095
+ };
2096
+
2097
+ /**
2098
+ * @enum {string}
2099
+ */
2100
+ const UiStrings = {
2101
+ NewTerminal: 'New Terminal'
2102
+ };
2103
+ const newTerminal = () => {
2104
+ return i18nString(UiStrings.NewTerminal);
2105
+ };
2106
+
2107
+ const id$2 = Terminal;
2108
+ const getMenuEntries$5 = () => {
2109
+ return [{
2110
+ id: 'newTerminal',
2111
+ label: newTerminal(),
2112
+ flags: None,
2113
+ command: 'Layout.togglePanel',
2114
+ args: ['Terminal']
2115
+ }];
2116
+ };
2117
+
2118
+ const MenuEntriesTerminal = {
2119
+ __proto__: null,
2120
+ getMenuEntries: getMenuEntries$5,
2121
+ id: id$2
2122
+ };
2123
+
2124
+ const file = () => {
2125
+ return i18nString(File$1);
2126
+ };
2127
+ const edit = () => {
2128
+ return i18nString(Edit$1);
2129
+ };
2130
+ const selection = () => {
2131
+ return i18nString(Selection$1);
2132
+ };
2133
+ const view = () => {
2134
+ return i18nString(View$1);
2135
+ };
2136
+ const go = () => {
2137
+ return i18nString(Go$1);
2138
+ };
2139
+ const run = () => {
2140
+ return i18nString(Run$1);
2141
+ };
2142
+ const terminal = () => {
2143
+ return i18nString(Terminal$1);
2144
+ };
2145
+ const help = () => {
2146
+ return i18nString(Help$1);
2147
+ };
2148
+
2149
+ const getMenuEntries$4 = () => {
2150
+ return [{
2151
+ id: File,
2152
+ label: file(),
2153
+ flags: SubMenu
848
2154
  }, {
849
- key: CtrlCmd | Star,
850
- command: 'Explorer.expandAll',
851
- when: FocusExplorer
2155
+ id: Edit,
2156
+ label: edit(),
2157
+ flags: SubMenu
852
2158
  }, {
853
- key: Alt | RightArrow,
854
- command: 'Explorer.expandRecursively',
855
- when: FocusExplorer
2159
+ id: Selection,
2160
+ label: selection(),
2161
+ flags: SubMenu
856
2162
  }, {
857
- key: CtrlCmd | LeftArrow,
858
- command: 'Explorer.collapseAll',
859
- when: FocusExplorer
2163
+ id: View,
2164
+ label: view(),
2165
+ flags: SubMenu
860
2166
  }, {
861
- key: CtrlCmd | KeyV,
862
- command: 'Explorer.handlePaste',
863
- when: FocusExplorer
2167
+ id: Go,
2168
+ label: go(),
2169
+ flags: SubMenu
864
2170
  }, {
865
- key: CtrlCmd | KeyC,
866
- command: 'Explorer.handleCopy',
867
- when: FocusExplorer
2171
+ id: Run,
2172
+ label: run(),
2173
+ keyboardShortCut: 'Alt+r',
2174
+ flags: SubMenu
868
2175
  }, {
869
- key: F2,
870
- command: 'Explorer.rename',
871
- when: FocusExplorer
2176
+ id: Terminal,
2177
+ label: terminal(),
2178
+ keyboardShortCut: 'Alt+t',
2179
+ flags: SubMenu
872
2180
  }, {
873
- key: Escape,
874
- command: 'Explorer.cancelEdit',
875
- when: FocusExplorerEditBox
2181
+ id: Help,
2182
+ label: help(),
2183
+ keyboardShortCut: 'Alt+h',
2184
+ flags: SubMenu
2185
+ }];
2186
+ };
2187
+
2188
+ const getMenuEntries$3 = () => {
2189
+ return [{
2190
+ id: File,
2191
+ label: file(),
2192
+ flags: None
876
2193
  }, {
877
- key: Enter,
878
- command: 'Explorer.acceptEdit',
879
- when: FocusExplorerEditBox
2194
+ id: Edit,
2195
+ label: edit(),
2196
+ flags: None
880
2197
  }, {
881
- key: Delete,
882
- command: 'Explorer.removeDirent',
883
- when: FocusExplorer
2198
+ id: Selection,
2199
+ label: selection(),
2200
+ flags: None
884
2201
  }, {
885
- key: Escape,
886
- command: 'Explorer.focusNone',
887
- when: FocusExplorer
2202
+ id: View,
2203
+ label: view(),
2204
+ flags: None
888
2205
  }, {
889
- key: Space,
890
- command: 'Explorer.handleClickCurrentButKeepFocus',
891
- when: FocusExplorer
2206
+ id: Go,
2207
+ label: go(),
2208
+ flags: None
892
2209
  }, {
893
- key: Enter,
894
- command: 'Explorer.handleClickCurrent',
895
- when: FocusExplorer
2210
+ id: Help,
2211
+ label: help(),
2212
+ flags: None
896
2213
  }];
897
2214
  };
898
2215
 
899
- const MenuBar = 'menubar';
900
- const MenuItem = 'menuitem';
2216
+ const getFn = () => {
2217
+ switch (platform) {
2218
+ case Web:
2219
+ return getMenuEntries$3;
2220
+ default:
2221
+ return getMenuEntries$4;
2222
+ }
2223
+ };
2224
+ const id$1 = TitleBar;
2225
+ const getMenuEntries$2 = async () => {
2226
+ const fn = getFn();
2227
+ return fn();
2228
+ };
901
2229
 
902
- const HandleClick = 'handleClick';
903
- const HandleFocusIn = 'handleFocusIn';
904
- const HandleFocusOut = 'handleFocusOut';
905
- const HandlePointerOut = 'handlePointerOut';
906
- const HandlePointerOver = 'handlePointerOver';
2230
+ const MenuEntriesTitleBar = {
2231
+ __proto__: null,
2232
+ getMenuEntries: getMenuEntries$2,
2233
+ id: id$1
2234
+ };
907
2235
 
908
- const TitleBarEntryActive = 'TitleBarEntryActive';
909
- const TitleBarTopLevelEntry = 'TitleBarTopLevelEntry';
910
- const TitleBarTopLevelEntryLabel = 'TitleBarTopLevelEntryLabel';
2236
+ const id = View;
2237
+ const getMenuEntries$1 = () => {
2238
+ return [];
2239
+ };
911
2240
 
912
- const Div = 4;
913
- const Text = 12;
2241
+ const MenuEntriesView = {
2242
+ __proto__: null,
2243
+ getMenuEntries: getMenuEntries$1,
2244
+ id
2245
+ };
914
2246
 
915
- const text = data => {
2247
+ const menus = [MenuEntriesEdit, MenuEntriesFile, MenuEntriesGo, MenuEntriesHelp, MenuEntriesRun, MenuEntriesSelection, MenuEntriesTerminal, MenuEntriesTitleBar, MenuEntriesView, MenuEntriesOpenRecent];
2248
+ const getMenus = () => {
2249
+ return menus;
2250
+ };
2251
+ const getMenuEntries = async (id, ...args) => {
2252
+ try {
2253
+ const module = menus[0];
2254
+ // @ts-ignore
2255
+ const inject = module.inject || [];
2256
+ // @ts-ignore
2257
+ return module.getMenuEntries(...args);
2258
+ } catch (error) {
2259
+ throw new VError(error, `Failed to load menu entries for id ${id}`);
2260
+ }
2261
+ };
2262
+
2263
+ const saveState = uid => {
2264
+ return {};
2265
+ };
2266
+
2267
+ const terminate = () => {
2268
+ globalThis.close();
2269
+ };
2270
+
2271
+ const create = (id, uri, x, y, width, height) => {
2272
+ const state = {
2273
+ uid: id,
2274
+ titleBarEntries: [],
2275
+ focusedIndex: -1,
2276
+ isMenuOpen: false,
2277
+ menus: [],
2278
+ labelFontWeight: 400,
2279
+ labelFontSize: 13,
2280
+ labelFontFamily: 'system-ui, Ubuntu, Droid Sans, sans-serif',
2281
+ labelPadding: 8,
2282
+ labelLetterSpacing: 0,
2283
+ titleBarHeight: height,
2284
+ x,
2285
+ y,
2286
+ width,
2287
+ height
2288
+ };
2289
+ set(id, state, state);
2290
+ return state;
2291
+ };
2292
+
2293
+ const closeMenu = (state, keepFocus) => {
2294
+ const {
2295
+ focusedIndex
2296
+ } = state;
2297
+ // TODO send to renderer process
2298
+ // 1. close menu
2299
+ // 2. focus top level entry
2300
+ const newFocusedIndex = keepFocus ? focusedIndex : -1;
916
2301
  return {
917
- type: Text,
918
- text: data,
919
- childCount: 0
2302
+ ...state,
2303
+ menus: [],
2304
+ isMenuOpen: false,
2305
+ focusedIndex: newFocusedIndex
920
2306
  };
921
2307
  };
922
2308
 
923
- const getItemVirtualDom = item => {
924
- // @ts-ignore
2309
+ const first = () => {
2310
+ return 0;
2311
+ };
2312
+ const last = items => {
2313
+ return items.length - 1;
2314
+ };
2315
+ const next = (items, index) => {
2316
+ return (index + 1) % items.length;
2317
+ };
2318
+ const previous = (items, index) => {
2319
+ return index === 0 ? items.length - 1 : index - 1;
2320
+ };
2321
+
2322
+ const getTotalWidth = entries => {
2323
+ let total = 0;
2324
+ for (const entry of entries) {
2325
+ total += entry.width;
2326
+ }
2327
+ return total;
2328
+ };
2329
+
2330
+ // TODO lazyload menuEntries and use Command.execute (maybe)
2331
+ const MENU_WIDTH = 150;
2332
+ const CONTEXT_MENU_ITEM_HEIGHT = 26;
2333
+ const CONTEXT_MENU_SEPARATOR_HEIGHT = 11;
2334
+ const CONTEXT_MENU_PADDING = 8;
2335
+ const CONTEXT_MENU_WIDTH = 250;
2336
+ const getMenuWidth = () => {
2337
+ return CONTEXT_MENU_WIDTH;
2338
+ };
2339
+ const getMenuHeight = items => {
2340
+ let height = CONTEXT_MENU_PADDING;
2341
+ for (const item of items) {
2342
+ switch (item.flags) {
2343
+ case Separator:
2344
+ height += CONTEXT_MENU_SEPARATOR_HEIGHT;
2345
+ break;
2346
+ default:
2347
+ height += CONTEXT_MENU_ITEM_HEIGHT;
2348
+ break;
2349
+ }
2350
+ }
2351
+ return height;
2352
+ };
2353
+
2354
+ // TODO difference between focusing with mouse or keyboard
2355
+ // with mouse -> open submenu
2356
+ // with keyboard -> don't open submenu, only focus
2357
+
2358
+ const getIndexToFocusNextStartingAt = (items, startIndex) => {
2359
+ for (let i = startIndex; i < startIndex + items.length; i++) {
2360
+ const index = i % items.length;
2361
+ const item = items[index];
2362
+ if (canBeFocused(item)) {
2363
+ return index;
2364
+ }
2365
+ }
2366
+ return -1;
2367
+ };
2368
+ const getIndexToFocusFirst = items => {
2369
+ return getIndexToFocusNextStartingAt(items, 0);
2370
+ };
2371
+ const getIndexToFocusLast = items => {
2372
+ return getIndexToFocusPreviousStartingAt(items, items.length - 1);
2373
+ };
2374
+
2375
+ // TODO this code seems a bit too complicated, maybe it can be simplified
2376
+ const getIndexToFocusPreviousStartingAt = (items, startIndex) => {
2377
+ for (let i = startIndex; i > startIndex - items.length; i--) {
2378
+ const index = (i + items.length) % items.length;
2379
+ const item = items[index];
2380
+ if (canBeFocused(item)) {
2381
+ return index;
2382
+ }
2383
+ }
2384
+ return -1;
2385
+ };
2386
+ const getIndexToFocusPrevious = menu => {
2387
+ const startIndex = menu.focusedIndex === -1 ? menu.items.length - 1 : menu.focusedIndex - 1;
2388
+ return getIndexToFocusPreviousStartingAt(menu.items, startIndex);
2389
+ };
2390
+ const canBeFocused = item => {
2391
+ switch (item.flags) {
2392
+ case Separator:
2393
+ case Disabled:
2394
+ return false;
2395
+ default:
2396
+ return true;
2397
+ }
2398
+ };
2399
+ const getIndexToFocusNext = menu => {
2400
+ const startIndex = menu.focusedIndex + 1;
2401
+ return getIndexToFocusNextStartingAt(menu.items, startIndex);
2402
+ };
2403
+
2404
+ // TODO handle printable letter and focus item that starts with that letter
2405
+
2406
+ // TODO pageup / pagedown keys
2407
+
2408
+ // TODO more tests
2409
+
2410
+ const openMenuAtIndex = async (state, index, shouldBeFocused) => {
925
2411
  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';
2412
+ titleBarEntries,
2413
+ titleBarHeight,
2414
+ x
2415
+ } = state;
2416
+ // TODO race conditions
2417
+ // TODO send renderer process
2418
+ // 1. open menu, items to show
2419
+ // 2. focus menu
2420
+ const titleBarEntry = titleBarEntries[index];
2421
+ const {
2422
+ id
2423
+ } = titleBarEntry;
2424
+ const items = await getMenuEntries(id);
2425
+ const relevantEntries = titleBarEntries.slice(0, index);
2426
+ const totalWidths = getTotalWidth(relevantEntries);
2427
+ const offset = totalWidths;
2428
+ // TODO race condition: another menu might already be open at this point
2429
+
2430
+ const menuX = x + offset;
2431
+ const menuY = titleBarHeight;
2432
+ const width = getMenuWidth();
2433
+ const height = getMenuHeight(items);
2434
+ const menuFocusedIndex = shouldBeFocused ? getIndexToFocusNextStartingAt(items, 0) : -1;
2435
+ const menu = {
2436
+ id,
2437
+ items,
2438
+ focusedIndex: menuFocusedIndex,
2439
+ level: 0,
2440
+ x: menuX,
2441
+ y: menuY,
2442
+ width,
2443
+ height
2444
+ };
2445
+ const menus = [menu];
2446
+ return {
2447
+ ...state,
2448
+ isMenuOpen: true,
2449
+ focusedIndex: index,
2450
+ menus
2451
+ };
2452
+ };
2453
+
2454
+ const focusIndex = async (state, index) => {
2455
+ object(state);
2456
+ number(index);
2457
+ const {
2458
+ isMenuOpen
2459
+ } = state;
2460
+ if (isMenuOpen) {
2461
+ return openMenuAtIndex(state, index, /* focus */false);
945
2462
  }
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
- });
2463
+ return {
2464
+ ...state,
2465
+ focusedIndex: index
2466
+ };
2467
+ };
2468
+
2469
+ const focusFirst = state => {
2470
+ const indexToFocus = first();
2471
+ return focusIndex(state, indexToFocus);
2472
+ };
2473
+
2474
+ const focus = state => {
2475
+ return focusFirst(state);
2476
+ };
2477
+
2478
+ const focusLast = state => {
2479
+ const {
2480
+ titleBarEntries
2481
+ } = state;
2482
+ const indexToFocus = last(titleBarEntries);
2483
+ return focusIndex(state, indexToFocus);
2484
+ };
2485
+
2486
+ const focusNext = state => {
2487
+ const {
2488
+ titleBarEntries,
2489
+ focusedIndex
2490
+ } = state;
2491
+ const indexToFocus = next(titleBarEntries, focusedIndex);
2492
+ return focusIndex(state, indexToFocus);
2493
+ };
2494
+
2495
+ const focusPrevious = state => {
2496
+ const {
2497
+ titleBarEntries,
2498
+ focusedIndex
2499
+ } = state;
2500
+ const indexToFocus = previous(titleBarEntries, focusedIndex);
2501
+ return focusIndex(state, indexToFocus);
2502
+ };
2503
+
2504
+ const LeftClick = 0;
2505
+
2506
+ const toggleIndex = (state, index) => {
2507
+ const {
2508
+ isMenuOpen,
2509
+ focusedIndex
2510
+ } = state;
2511
+ if (isMenuOpen && focusedIndex === index) {
2512
+ return closeMenu(state, /* keepFocus */true);
2513
+ }
2514
+ return openMenuAtIndex(state, index, /* focus */false);
2515
+ };
2516
+
2517
+ const handleClick = (state, button, index) => {
2518
+ if (button !== LeftClick) {
2519
+ return state;
2520
+ }
2521
+ if (index === -1) {
2522
+ return state;
2523
+ }
2524
+ return toggleIndex(state, index);
2525
+ };
2526
+
2527
+ // TODO remove this file and merge with whenExpressions
2528
+ const TitleBarMenuBar = FocusTitleBarMenuBar;
2529
+
2530
+ const handleFocus = async state => {
2531
+ await invoke('Focus.setFocus', TitleBarMenuBar);
2532
+ return state;
2533
+ };
2534
+
2535
+ /**
2536
+ * @param {boolean} focus
2537
+ */
2538
+ const openMenu = (state, focus) => {
2539
+ const {
2540
+ focusedIndex
2541
+ } = state;
2542
+ if (focusedIndex === -1) {
2543
+ return state;
955
2544
  }
956
- dom.push(text(label));
957
- return dom;
2545
+ return openMenuAtIndex(state, focusedIndex, focus);
958
2546
  };
959
- const getTitleBarMenuBarItemsVirtualDom = visibleItems => {
960
- const dom = visibleItems.flatMap(getItemVirtualDom);
961
- return dom;
2547
+
2548
+ const handleKeyArrowDownMenuClosed = state => {
2549
+ return openMenu(state, /* focus */true);
962
2550
  };
963
2551
 
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)];
2552
+ const handleKeyArrowDownMenuOpen = state => {
2553
+ const {
2554
+ menus
2555
+ } = state;
2556
+ const menu = menus.at(-1);
2557
+ const newFocusedIndex = getIndexToFocusNext(menu);
2558
+ const newMenus = [...menus.slice(0, -1), {
2559
+ ...menu,
2560
+ focusedIndex: newFocusedIndex
2561
+ }];
2562
+ return {
2563
+ ...state,
2564
+ menus: newMenus
2565
+ };
977
2566
  };
978
2567
 
979
- class AssertionError extends Error {
980
- constructor(message) {
981
- super(message);
982
- this.name = 'AssertionError';
983
- }
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';
1005
- }
2568
+ const ifElse = (menuOpenFunction, menuClosedFunction) => {
2569
+ const ifElseFunction = (state, ...args) => {
2570
+ const {
2571
+ isMenuOpen
2572
+ } = state;
2573
+ if (isMenuOpen) {
2574
+ return menuOpenFunction(state, ...args);
2575
+ }
2576
+ return menuClosedFunction(state, ...args);
2577
+ };
2578
+ return ifElseFunction;
1006
2579
  };
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
- }
2580
+
2581
+ const handleKeyArrowDown = ifElse(handleKeyArrowDownMenuOpen, handleKeyArrowDownMenuClosed);
2582
+
2583
+ const handleKeyArrowLeftMenuClosed = state => {
2584
+ // TODO menu collapse
2585
+ return focusPrevious(state);
1012
2586
  };
1013
- const string = value => {
1014
- const type = getType(value);
1015
- if (type !== 'string') {
1016
- throw new AssertionError('expected value to be of type string');
2587
+
2588
+ const closeOneMenu = state => {
2589
+ const {
2590
+ menus
2591
+ } = state;
2592
+ const parentMenu = menus.at(-2);
2593
+ const newParentMenu = {
2594
+ ...parentMenu,
2595
+ expanded: false
2596
+ };
2597
+ const newMenus = [...menus.slice(0, -2), newParentMenu];
2598
+ return {
2599
+ ...state,
2600
+ menus: newMenus
2601
+ };
2602
+ };
2603
+
2604
+ const handleKeyArrowLeftMenuOpen = state => {
2605
+ const {
2606
+ menus
2607
+ } = state;
2608
+ if (menus.length > 1) {
2609
+ return closeOneMenu(state);
1017
2610
  }
2611
+ return focusPrevious(state);
1018
2612
  };
1019
- const boolean = value => {
1020
- const type = getType(value);
1021
- if (type !== 'boolean') {
1022
- throw new AssertionError('expected value to be of type boolean');
2613
+
2614
+ const handleKeyArrowLeft = ifElse(handleKeyArrowLeftMenuOpen, handleKeyArrowLeftMenuClosed);
2615
+
2616
+ // TODO menu should not be needed initially, only when item is selected and menu is opened
2617
+ const handleKeyArrowRightMenuOpen = async state => {
2618
+ const {
2619
+ menus
2620
+ } = state;
2621
+ // if menu can open sub menu to the right -> do that
2622
+ const menu = menus.at(-1);
2623
+ const {
2624
+ items,
2625
+ focusedIndex,
2626
+ x,
2627
+ y
2628
+ } = menu;
2629
+ if (focusedIndex === -1) {
2630
+ return focusNext(state);
2631
+ }
2632
+ const item = items[focusedIndex];
2633
+ if (item.flags === SubMenu) {
2634
+ const subMenuEntries = await getMenuEntries(item.id);
2635
+ const subMenu = {
2636
+ level: menus.length,
2637
+ items: subMenuEntries,
2638
+ focusedIndex: 0,
2639
+ y: y + focusedIndex * 25,
2640
+ x: x + MENU_WIDTH
2641
+ };
2642
+ const newParentMenu = {
2643
+ ...menu,
2644
+ expanded: true
2645
+ };
2646
+ const newMenus = [...menus.slice(0, -1), newParentMenu, subMenu];
2647
+ return {
2648
+ ...state,
2649
+ menus: newMenus
2650
+ };
1023
2651
  }
2652
+ return focusNext(state);
1024
2653
  };
1025
2654
 
1026
- const getFontString = (fontWeight, fontSize, fontFamily) => {
1027
- return `${fontWeight} ${fontSize}px ${fontFamily}`;
2655
+ const handleKeyArrowRight = ifElse(handleKeyArrowRightMenuOpen, focusNext);
2656
+
2657
+ const noop = state => {
2658
+ return state;
1028
2659
  };
1029
2660
 
1030
- const state = {
1031
- ctx: undefined
2661
+ const handleKeyArrowUpMenuOpen = state => {
2662
+ const {
2663
+ menus
2664
+ } = state;
2665
+ const menu = menus.at(-1);
2666
+ const previousIndex = getIndexToFocusPrevious(menu);
2667
+ const newMenus = [...menus.slice(0, -1), {
2668
+ ...menu,
2669
+ focusedIndex: previousIndex
2670
+ }];
2671
+ return {
2672
+ ...state,
2673
+ menus: newMenus
2674
+ };
1032
2675
  };
1033
- const getOrCreate = createCtx => {
1034
- if (state.ctx) {
1035
- return state.ctx;
1036
- }
1037
- state.ctx = createCtx();
1038
- return state.ctx;
2676
+
2677
+ const handleKeyArrowUp = ifElse(handleKeyArrowUpMenuOpen, noop);
2678
+
2679
+ const handleKeyEndMenuOpen = state => {
2680
+ const {
2681
+ menus
2682
+ } = state;
2683
+ const menu = menus[0];
2684
+ const newFocusedIndex = getIndexToFocusLast(menu.items);
2685
+ const newMenus = [{
2686
+ ...menu,
2687
+ focusedIndex: newFocusedIndex
2688
+ }];
2689
+ return {
2690
+ ...state,
2691
+ menus: newMenus
2692
+ };
1039
2693
  };
1040
2694
 
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');
2695
+ // TODO this is also use for pagedown -> maybe find a better name for this function
2696
+ const handleKeyEnd = ifElse(handleKeyEndMenuOpen, focusLast);
2697
+
2698
+ const handleKeyEnterMenuClosed = state => {
2699
+ return openMenu(state, /* focus */true);
2700
+ };
2701
+
2702
+ const handleKeyEnterMenuOpen = state => {
2703
+ // TODO
2704
+ // await Menu.selectCurrent()
2705
+ return state;
2706
+ };
2707
+
2708
+ const handleKeyEnter = ifElse(handleKeyEnterMenuOpen, handleKeyEnterMenuClosed);
2709
+
2710
+ const handleKeyEscapeMenuOpen = state => {
2711
+ const {
2712
+ menus
2713
+ } = state;
2714
+ if (menus.length > 1) {
2715
+ return closeOneMenu(state);
1046
2716
  }
1047
- return ctx;
2717
+ return closeMenu(state, /* keepFocus */true);
1048
2718
  };
1049
- const getContext = () => {
1050
- const ctx = getOrCreate(createCtx);
1051
- return ctx;
2719
+
2720
+ const handleKeyEscape = ifElse(handleKeyEscapeMenuOpen, noop);
2721
+
2722
+ const handleKeyHomeMenuOpen = state => {
2723
+ const {
2724
+ menus
2725
+ } = state;
2726
+ const menu = menus[0];
2727
+ const newFocusedIndex = getIndexToFocusFirst(menu.items);
2728
+ const newMenus = [{
2729
+ ...menu,
2730
+ focusedIndex: newFocusedIndex
2731
+ }];
2732
+ return {
2733
+ ...state,
2734
+ menus: newMenus
2735
+ };
1052
2736
  };
1053
2737
 
1054
- const px = value => {
1055
- return `${value}px`;
2738
+ const handleKeyHome = ifElse(handleKeyHomeMenuOpen, focusFirst);
2739
+
2740
+ const handleKeySpaceMenuClosed = state => {
2741
+ return openMenu(state, /* focus */true);
1056
2742
  };
1057
2743
 
1058
- const getLetterSpacingString = letterSpacing => {
1059
- return px(letterSpacing);
2744
+ const handleKeySpaceMenuOpen = state => {
2745
+ // TODO
2746
+ // await Menu.selectCurrent()
2747
+ return state;
1060
2748
  };
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');
1070
- }
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;
2749
+
2750
+ // TODO this is same as handle key enter -> merge the functions
2751
+ const handleKeySpace = ifElse(handleKeySpaceMenuOpen, handleKeySpaceMenuClosed);
2752
+
2753
+ const executeMenuItemCommand = async item => {
2754
+ // TODO
2755
+ throw new Error('not implemented');
1080
2756
  };
1081
2757
 
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);
2758
+ const selectIndexIgnore = async (state, item) => {
2759
+ await executeMenuItemCommand();
2760
+ return state;
1086
2761
  };
1087
2762
 
1088
- const create = (id, uri, x, y, width, height) => {
2763
+ const selectIndexNone = async (state, item) => {
2764
+ await executeMenuItemCommand();
1089
2765
  return {
1090
- uid: id,
1091
- titleBarEntries: [],
1092
- focusedIndex: -1,
1093
- isMenuOpen: false,
2766
+ ...state,
1094
2767
  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,
2768
+ isMenuOpen: false
2769
+ };
2770
+ };
2771
+
2772
+ const selectIndexRestoreFocus = async (state, item) => {
2773
+ await executeMenuItemCommand();
2774
+ return {
2775
+ ...state,
2776
+ menus: [],
2777
+ isMenuOpen: false
2778
+ };
2779
+ };
2780
+
2781
+ const selectIndexSubMenu = async (state, menu, index) => {
2782
+ const {
2783
+ menus
2784
+ } = state;
2785
+ const {
2786
+ items,
1101
2787
  x,
1102
2788
  y,
1103
- width,
1104
- height
2789
+ level
2790
+ } = menu;
2791
+ const item = items[index];
2792
+ const subMenuEntries = await getMenuEntries(item.id);
2793
+ const subMenu = {
2794
+ level: menus.length,
2795
+ items: subMenuEntries,
2796
+ focusedIndex: -1,
2797
+ x: x + MENU_WIDTH,
2798
+ y: y + index * 25
2799
+ };
2800
+ const newParentMenu = {
2801
+ ...menu,
2802
+ focusedIndex: index
2803
+ };
2804
+ const newMenus = [...menus.slice(0, level - 1), newParentMenu, subMenu];
2805
+ return {
2806
+ ...state,
2807
+ menus: newMenus
1105
2808
  };
1106
2809
  };
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
- });
2810
+
2811
+ const handleMenuClick = (state, level, index) => {
2812
+ const {
2813
+ menus
2814
+ } = state;
2815
+ const menu = menus[level];
2816
+ const item = menu.items[index];
2817
+ switch (item.flags) {
2818
+ case None:
2819
+ return selectIndexNone(state);
2820
+ case SubMenu:
2821
+ return selectIndexSubMenu(state, menu, index);
2822
+ case RestoreFocus:
2823
+ return selectIndexRestoreFocus(state);
2824
+ case Ignore:
2825
+ return selectIndexIgnore(state);
2826
+ default:
2827
+ return state;
1116
2828
  }
1117
- return withWidths;
1118
2829
  };
1119
- const loadContent = async state => {
2830
+
2831
+ const handleMenuMouseOver = async (state, level, index) => {
2832
+ object(state);
2833
+ number(level);
2834
+ number(index);
1120
2835
  const {
1121
- labelFontFamily,
1122
- labelFontSize,
1123
- labelFontWeight,
1124
- labelLetterSpacing,
1125
- labelPadding
2836
+ menus
1126
2837
  } = 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 })
2838
+ const menu = menus[level];
2839
+ const {
2840
+ items,
2841
+ focusedIndex,
2842
+ y,
2843
+ x
2844
+ } = menu;
2845
+ const item = items[index];
2846
+ if (focusedIndex === index) {
2847
+ if (index === -1) {
2848
+ return state;
2849
+ }
2850
+ if (item.flags === SubMenu && level === menus.length - 2) {
2851
+ const subMenu = menus[level + 1];
2852
+ if (subMenu.focusedIndex !== -1) {
2853
+ const newSubMenu = {
2854
+ ...subMenu,
2855
+ focusedIndex: -1
2856
+ };
2857
+ const newMenus = [...menus.slice(0, -1), newSubMenu];
2858
+ return {
2859
+ ...state,
2860
+ menus: newMenus
2861
+ };
2862
+ }
2863
+ }
2864
+ return state;
2865
+ }
2866
+ if (index === -1) {
2867
+ const newMenus = [...menus.slice(0, level), {
2868
+ ...menu,
2869
+ focusedIndex: -1
2870
+ }];
2871
+ return {
2872
+ ...state,
2873
+ menus: newMenus
2874
+ };
2875
+ }
2876
+ if (item.flags === SubMenu) {
2877
+ const item = items[index];
2878
+ const subMenuEntries = await getMenuEntries(item.id);
2879
+ const subMenu = {
2880
+ level: menus.length,
2881
+ items: subMenuEntries,
2882
+ focusedIndex: -1,
2883
+ y: y + index * 25,
2884
+ x: x + MENU_WIDTH
2885
+ };
2886
+ const newParentMenu = {
2887
+ ...menu,
2888
+ focusedIndex: index
2889
+ };
2890
+ const newMenus = [...menus.slice(0, level - 1), newParentMenu, subMenu];
2891
+ return {
2892
+ ...state,
2893
+ menus: newMenus
2894
+ };
2895
+ }
2896
+ const newMenus = [...menus.slice(0, level), {
2897
+ ...menu,
2898
+ focusedIndex: index
2899
+ }];
1131
2900
  return {
1132
2901
  ...state,
1133
- titleBarEntries: withWidths
2902
+ menus: newMenus
2903
+ };
2904
+ };
2905
+
2906
+ const handleMouseOutMenuClosed = state => {
2907
+ return focusIndex(state, -1);
2908
+ };
2909
+
2910
+ const handleMouseOutMenuOpen = state => {
2911
+ return state;
2912
+ };
2913
+
2914
+ const handleMouseOut = ifElse(handleMouseOutMenuOpen, handleMouseOutMenuClosed);
2915
+
2916
+ const handleMouseOverMenuClosed = (state, index) => {
2917
+ return focusIndex(state, index);
2918
+ };
2919
+
2920
+ const handleMouseOverMenuOpen = async (state, index) => {
2921
+ if (index === -1) {
2922
+ return state;
2923
+ }
2924
+ return focusIndex(state, index);
2925
+ };
2926
+
2927
+ const handleMouseOver = ifElse(handleMouseOverMenuOpen, handleMouseOverMenuClosed);
2928
+
2929
+ const toggleMenu = state => {
2930
+ const {
2931
+ isMenuOpen
2932
+ } = state;
2933
+ if (isMenuOpen) {
2934
+ return closeMenu(state, /* keepFocus */true);
2935
+ }
2936
+ return openMenu(state, /* focus */false);
2937
+ };
2938
+
2939
+ const wrapCommand = fn => {
2940
+ const wrapped = async (uid, ...args) => {
2941
+ const {
2942
+ newState
2943
+ } = get(uid);
2944
+ const newerState = await fn(newState, ...args);
2945
+ set(uid, newState, newerState);
1134
2946
  };
2947
+ return wrapped;
1135
2948
  };
1136
2949
 
1137
2950
  const commandMap = {
2951
+ 'TitleBarMenuBar.closeMenu': closeMenu,
1138
2952
  'TitleBarMenuBar.create': create,
2953
+ 'TitleBarMenuBar.focus': wrapCommand(focus),
2954
+ 'TitleBarMenuBar.focusFirst': wrapCommand(focusFirst),
2955
+ 'TitleBarMenuBar.focusIndex': wrapCommand(focusLast),
2956
+ 'TitleBarMenuBar.focusLast': wrapCommand(focusIndex),
2957
+ 'TitleBarMenuBar.focusNext': wrapCommand(focusNext),
2958
+ 'TitleBarMenuBar.focusPrevious': wrapCommand(focusPrevious),
2959
+ 'TitleBarMenuBar.getCommands': getCommandIds,
2960
+ 'TitleBarMenuBar.getKeyBindings': getKeyBindings,
2961
+ 'TitleBarMenuBar.getMenus': getMenus,
1139
2962
  'TitleBarMenuBar.getVirtualDom': getTitleBarMenuBarVirtualDom,
1140
- 'TitleBarMenuBar.loadContent': loadContent,
1141
- 'TitleBarMenuBar.getKeyBindings': getKeyBindings
2963
+ 'TitleBarMenuBar.handleClick': wrapCommand(handleClick),
2964
+ 'TitleBarMenuBar.handleFocus': wrapCommand(handleFocus),
2965
+ 'TitleBarMenuBar.handleKeyArrowDown': wrapCommand(handleKeyArrowDown),
2966
+ 'TitleBarMenuBar.handleKeyArrowLeft': wrapCommand(handleKeyArrowLeft),
2967
+ 'TitleBarMenuBar.handleKeyArrowRight': wrapCommand(handleKeyArrowRight),
2968
+ 'TitleBarMenuBar.handleKeyArrowUp': wrapCommand(handleKeyArrowUp),
2969
+ 'TitleBarMenuBar.handleKeyEnd': wrapCommand(handleKeyEnd),
2970
+ 'TitleBarMenuBar.handleKeyEnter': wrapCommand(handleKeyEnter),
2971
+ 'TitleBarMenuBar.handleKeyEscape': wrapCommand(handleKeyEscape),
2972
+ 'TitleBarMenuBar.saveState': saveState,
2973
+ 'TitleBarMenuBar.handleKeyHome': wrapCommand(handleKeyHome),
2974
+ 'TitleBarMenuBar.handleKeySpace': wrapCommand(handleKeySpace),
2975
+ 'TitleBarMenuBar.handleMenuClick': wrapCommand(handleMenuClick),
2976
+ 'TitleBarMenuBar.handleMenuMouseOver': wrapCommand(handleMenuMouseOver),
2977
+ 'TitleBarMenuBar.handleMouseOut': wrapCommand(handleMouseOut),
2978
+ 'TitleBarMenuBar.handleMouseOver': wrapCommand(handleMouseOver),
2979
+ 'TitleBarMenuBar.loadContent': wrapCommand(loadContent),
2980
+ 'TitleBarMenuBar.render': doRender,
2981
+ 'TitleBarMenuBar.toggleIndex': wrapCommand(toggleIndex),
2982
+ 'TitleBarMenuBar.toggleMenu': wrapCommand(toggleMenu),
2983
+ 'TitleBarMenuBar.terminate': terminate
1142
2984
  };
1143
2985
 
1144
2986
  const listen = async () => {
1145
- await WebWorkerRpcClient.create({
2987
+ const rpc = await WebWorkerRpcClient.create({
1146
2988
  commandMap: commandMap
1147
2989
  });
2990
+ setRpc(rpc);
1148
2991
  };
1149
2992
 
1150
2993
  const main = async () => {