@lvce-editor/title-bar-worker 1.10.0 → 2.0.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.
@@ -815,6 +815,9 @@ const createRpc = ipc => {
815
815
  },
816
816
  invokeAndTransfer(method, ...params) {
817
817
  return invokeAndTransfer(ipc, method, ...params);
818
+ },
819
+ async dispose() {
820
+ await ipc?.dispose();
818
821
  }
819
822
  };
820
823
  return rpc;
@@ -941,215 +944,12 @@ const diff2 = uid => {
941
944
  return diff(oldState, newState);
942
945
  };
943
946
 
944
- const Menu$1 = 'menu';
945
- const MenuBar = 'menubar';
946
- const MenuItem$1 = 'menuitem';
947
- const MenuItemCheckBox = 'menuitemcheckbox';
948
- const None$1 = 'none';
949
- const Separator$1 = 'separator';
950
-
951
- const HandleClick = 'handleClick';
952
- const HandleClickMinimize = 'handleClickMinimize';
953
- const HandleClickToggleClose = 'handleClickToggleClose';
954
- const HandleClickToggleMaximize = 'handleClickToggleMaximize';
955
- const HandleFocusIn = 'handleFocusIn';
956
- const HandleFocusOut = 'handleFocusOut';
957
- const HandlePointerOut = 'handlePointerOut';
958
- const HandlePointerOver = 'handlePointerOver';
959
- const HandleMenuClick = 'handleMenuClick';
960
- const HandleMenuMouseOver = 'handleMenuMouseOver';
961
-
962
- const Menu = 'Menu';
963
- const MenuItem = 'MenuItem';
964
- const MenuItemFocused = 'MenuItemFocused';
965
- const MenuItemSeparator = 'MenuItemSeparator';
966
- const MenuItemSeparatorLine = 'MenuItemSeparatorLine';
967
- const MenuItemSubMenu = 'MenuItemSubMenu';
968
- const MenuItemSubMenuArrowRight = 'MenuItemSubMenuArrowRight';
969
- const TitleBarEntryActive = 'TitleBarEntryActive';
970
- const TitleBarTopLevelEntry = 'TitleBarTopLevelEntry';
971
- const TitleBarTopLevelEntryLabel = 'TitleBarTopLevelEntryLabel';
972
-
973
- const Button = 1;
974
- const Div = 4;
975
- const Text = 12;
976
- const I = 16;
977
- const Img = 17;
978
- const Span = 8;
979
-
980
- const text = data => {
981
- return {
982
- type: Text,
983
- text: data,
984
- childCount: 0
985
- };
986
- };
987
-
988
- const getItemVirtualDom = item => {
989
- // @ts-ignore
990
- const {
991
- keyboardShortCut,
992
- label,
993
- isOpen,
994
- isFocused
995
- } = item;
996
- const dom = [];
997
- dom.push({
998
- type: Div,
999
- className: TitleBarTopLevelEntry,
1000
- ariaHasPopup: true,
1001
- ariaExpanded: isOpen,
1002
- role: MenuItem$1,
1003
- childCount: 1,
1004
- ariaKeyShortcuts: keyboardShortCut
1005
- });
1006
- if (isOpen) {
1007
- // @ts-ignore
1008
- dom[0].ariaOwns = 'Menu-0';
1009
- }
1010
- if (isFocused) {
1011
- dom[0].className += ' ' + TitleBarEntryActive;
1012
- // @ts-ignore
1013
- dom[0].id = 'TitleBarEntryActive';
1014
- dom.push({
1015
- type: Div,
1016
- className: TitleBarTopLevelEntryLabel,
1017
- childCount: 1
1018
- });
1019
- }
1020
- dom.push(text(label));
1021
- return dom;
1022
- };
1023
- const getTitleBarMenuBarItemsVirtualDom = visibleItems => {
1024
- const dom = visibleItems.flatMap(getItemVirtualDom);
1025
- return dom;
1026
- };
1027
-
1028
- const activeId = 'TitleBarEntryActive';
1029
- const getTitleBarMenuBarVirtualDom = (visibleItems, focusedIndex) => {
1030
- return [{
1031
- type: Div,
1032
- className: 'Viewlet TitleBarMenuBar',
1033
- role: MenuBar,
1034
- tabIndex: 0,
1035
- childCount: visibleItems.length,
1036
- onMouseDown: HandleClick,
1037
- onFocusOut: HandleFocusOut,
1038
- onFocusIn: HandleFocusIn,
1039
- onPointerOver: HandlePointerOver,
1040
- onPointerOut: HandlePointerOut,
1041
- ariaActivedescendant: focusedIndex === -1 ? '' : activeId
1042
- }, ...getTitleBarMenuBarItemsVirtualDom(visibleItems)];
1043
- };
1044
-
1045
- const Ellipsis = 'Ellipsis';
1046
-
1047
- const emptyObject = {};
1048
- const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
1049
- const i18nString = (key, placeholders = emptyObject) => {
1050
- if (placeholders === emptyObject) {
1051
- return key;
1052
- }
1053
- const replacer = (match, rest) => {
1054
- return placeholders[rest];
1055
- };
1056
- return key.replaceAll(RE_PLACEHOLDER, replacer);
1057
- };
1058
-
1059
- const About = 'About';
1060
- const CheckForUpdates = 'Check For Updates';
1061
- const ClearRecentlyOpened = 'Clear Recently Opened';
1062
- const Edit$1 = 'Edit';
1063
- const File$1 = 'File';
1064
- const Go$1 = 'Go';
1065
- const Help$1 = 'Help';
1066
- const MoreDot = 'More ...';
1067
- const OpenProcessExplorer = 'Open Process Explorer';
1068
- const Run$1 = 'Run';
1069
- const Selection$1 = 'Selection';
1070
- const Terminal$1 = 'Terminal';
1071
- const ToggleDeveloperTools = 'Toggle Developer Tools';
1072
- const View$1 = 'View';
1073
-
1074
- const moreDot = () => {
1075
- return i18nString(MoreDot);
1076
- };
1077
- const clearRecentlyOpened = () => {
1078
- return i18nString(ClearRecentlyOpened);
1079
- };
1080
-
1081
- const getVisibleTitleBarEntries = (entries, width, focusedIndex, isMenuOpen) => {
1082
- array(entries);
1083
- number(width);
1084
- let total = 0;
1085
- const visible = [];
1086
- for (let i = 0; i < entries.length; i++) {
1087
- const entry = entries[i];
1088
- total += entry.width;
1089
- if (total >= width) {
1090
- break;
1091
- }
1092
- const isOpen = i === focusedIndex && isMenuOpen;
1093
- const isFocused = i === focusedIndex;
1094
- visible.push({
1095
- ...entry,
1096
- isOpen,
1097
- isFocused
1098
- });
1099
- }
1100
- const hasOverflow = visible.length < entries.length;
1101
- if (hasOverflow) {
1102
- const padding = 8;
1103
- const moreIconWidth = 22;
1104
- const totalPadding = padding * 2;
1105
- const hasStillOverflow = total + moreIconWidth + totalPadding > width;
1106
- if (hasStillOverflow) {
1107
- visible.pop();
1108
- }
1109
- visible.push({
1110
- ariaLabel: moreDot(),
1111
- icon: Ellipsis,
1112
- label: '',
1113
- width: moreIconWidth + totalPadding
1114
- });
1115
- }
1116
- return visible;
1117
- };
1118
-
1119
- const renderEntries = (oldState, newState) => {
1120
- const visibleEntries = getVisibleTitleBarEntries(newState.titleBarEntries, newState.width, newState.focusedIndex, newState.isMenuOpen);
1121
- const dom = getTitleBarMenuBarVirtualDom(visibleEntries, newState.focusedIndex);
1122
- return ['Viewlet.setDom2', newState.uid, dom];
1123
- };
1124
-
1125
- const SetFocusedIndex = 'setFocusedIndex';
1126
- const SetMenus = 'setMenus';
1127
-
1128
- const renderFocusedIndex = (oldState, newState) => {
1129
- return ['Viewlet.send', newState.uid, /* method */SetFocusedIndex, /* oldFocusedIndex */oldState.focusedIndex, /* newfocusedIndex */newState.focusedIndex, /* oldIsMenuOpen */oldState.isMenuOpen, /* newIsMenuOpen */newState.isMenuOpen];
1130
- };
947
+ const commandsIds = ['closeMenu', 'focus', 'focusFirst', 'focusIndex', 'focusLast', 'focusNext', 'focusPrevious', 'handleClick', 'handleFocus', 'handleKeyArrowDown', 'handleKeyArrowLeft', 'handleKeyArrowRight', 'handleKeyArrowUp', 'handleKeyEnd', 'handleKeyEnter', 'handleKeyEscape', 'handleKeyHome', 'handleKeySpace', 'handleMenuClick', 'handleMenuMouseOver', 'handleMouseOut', 'handleMouseOver', 'handlePointerOver', 'handlePointerOut', 'toggleIndex', 'toggleMenu'];
1131
948
 
1132
- const getKeyBindingString = (key, altKey, ctrlKey, shiftKey, metaKey) => {
1133
- let string = '';
1134
- if (ctrlKey) {
1135
- string += 'Ctrl+';
1136
- }
1137
- if (shiftKey) {
1138
- string += 'Shift+';
1139
- }
1140
- string += key.toUpperCase();
1141
- return string;
949
+ const getCommandIds = () => {
950
+ return commandsIds;
1142
951
  };
1143
952
 
1144
- const Separator = 1;
1145
- const None = 0;
1146
- const SubMenu = 4;
1147
- const Checked = 2;
1148
- const Unchecked = 3;
1149
- const Disabled = 5;
1150
- const RestoreFocus = 6;
1151
- const Ignore = 7;
1152
-
1153
953
  const Backspace$1 = 1;
1154
954
  const Tab$1 = 2;
1155
955
  const Enter$1 = 3;
@@ -1215,1480 +1015,1688 @@ const Backslash$1 = 91;
1215
1015
  const Star$1 = 131;
1216
1016
  const Plus$1 = 132;
1217
1017
 
1218
- const Unknown = 'Unknown';
1219
- const Backspace = 'Backspace';
1220
- const Tab = 'Tab';
1221
- const Enter = 'Enter';
1222
- const Escape = 'Escape';
1223
- const Space = 'Space';
1224
- const PageUp = 'PageUp';
1225
- const PageDown = 'PageDown';
1226
- const End = 'End';
1227
- const Home = 'Home';
1228
- const LeftArrow = 'LeftArrow';
1229
- const UpArrow = 'UpArrow';
1230
- const RightArrow = 'RightArrow';
1231
- const DownArrow = 'DownArrow';
1232
- const Insert = 'Insert';
1233
- const Delete = 'Delete';
1234
- const Digit0 = '0';
1235
- const Digit1 = '1';
1236
- const Digit2 = '2';
1237
- const Digit3 = '3';
1238
- const Digit4 = '4';
1239
- const Digit5 = '5';
1240
- const Digit6 = '6';
1241
- const Digit7 = '7';
1242
- const Digit8 = '8';
1243
- const Digit9 = '9';
1244
- const KeyA = 'a';
1245
- const KeyB = 'b';
1246
- const KeyC = 'c';
1247
- const KeyD = 'd';
1248
- const KeyE = 'e';
1249
- const KeyF = 'f';
1250
- const KeyG = 'g';
1251
- const KeyH = 'h';
1252
- const KeyI = 'i';
1253
- const KeyJ = 'j';
1254
- const KeyK = 'k';
1255
- const KeyL = 'l';
1256
- const KeyM = 'm';
1257
- const KeyN = 'n';
1258
- const KeyO = 'o';
1259
- const KeyP = 'p';
1260
- const KeyQ = 'q';
1261
- const KeyR = 'r';
1262
- const KeyS = 's';
1263
- const KeyT = 't';
1264
- const KeyU = 'u';
1265
- const KeyV = 'v';
1266
- const KeyW = 'w';
1267
- const KeyX = 'x';
1268
- const KeyY = 'y';
1269
- const KeyZ = 'z';
1270
- const F1 = 'F1';
1271
- const F2 = 'F2';
1272
- const F3 = 'F3';
1273
- const F4 = 'F4';
1274
- const F5 = 'F5';
1275
- const F6 = 'F6';
1276
- const Equal = '=';
1277
- const Comma = ',';
1278
- const Minus = 'Minus';
1279
- const Backquote = 'Backquote';
1280
- const Backslash = 'Backslash';
1281
- const Star = '*';
1282
- const Plus = '+';
1018
+ const FocusTitleBarMenuBar = 26;
1283
1019
 
1284
- const getKeyCodeString = keyCode => {
1285
- switch (keyCode) {
1286
- case Backspace$1:
1287
- return Backspace;
1288
- case Tab$1:
1289
- return Tab;
1290
- case Escape$1:
1291
- return Escape;
1292
- case Enter$1:
1293
- return Enter;
1294
- case Space$1:
1295
- return Space;
1296
- case PageUp$1:
1297
- return PageUp;
1298
- case PageDown$1:
1299
- return PageDown;
1300
- case End$1:
1301
- return End;
1302
- case Home$1:
1303
- return Home;
1304
- case LeftArrow$1:
1305
- return LeftArrow;
1306
- case UpArrow$1:
1307
- return UpArrow;
1308
- case RightArrow$1:
1309
- return RightArrow;
1310
- case DownArrow$1:
1311
- return DownArrow;
1312
- case Insert$1:
1313
- return Insert;
1314
- case Delete$1:
1315
- return Delete;
1316
- case Digit0$1:
1317
- return Digit0;
1318
- case Digit1$1:
1319
- return Digit1;
1320
- case Digit2$1:
1321
- return Digit2;
1322
- case Digit3$1:
1323
- return Digit3;
1324
- case Digit4$1:
1325
- return Digit4;
1326
- case Digit5$1:
1327
- return Digit5;
1328
- case Digit6$1:
1329
- return Digit6;
1330
- case Digit7$1:
1331
- return Digit7;
1332
- case Digit8$1:
1333
- return Digit8;
1334
- case Digit9$1:
1335
- return Digit9;
1336
- case KeyA$1:
1337
- return KeyA;
1338
- case KeyB$1:
1339
- return KeyB;
1340
- case KeyC$1:
1341
- return KeyC;
1342
- case KeyD$1:
1343
- return KeyD;
1344
- case KeyE$1:
1345
- return KeyE;
1346
- case KeyF$1:
1347
- return KeyF;
1348
- case KeyG$1:
1349
- return KeyG;
1350
- case KeyH$1:
1351
- return KeyH;
1352
- case KeyI$1:
1353
- return KeyI;
1354
- case KeyJ$1:
1355
- return KeyJ;
1356
- case KeyK$1:
1357
- return KeyK;
1358
- case KeyL$1:
1359
- return KeyL;
1360
- case KeyM$1:
1361
- return KeyM;
1362
- case KeyN$1:
1363
- return KeyN;
1364
- case KeyO$1:
1365
- return KeyO;
1366
- case KeyP$1:
1367
- return KeyP;
1368
- case KeyQ$1:
1369
- return KeyQ;
1370
- case KeyR$1:
1371
- return KeyR;
1372
- case KeyS$1:
1373
- return KeyS;
1374
- case KeyT$1:
1375
- return KeyT;
1376
- case KeyU$1:
1377
- return KeyU;
1378
- case KeyV$1:
1379
- return KeyV;
1380
- case KeyW$1:
1381
- return KeyW;
1382
- case KeyX$1:
1383
- return KeyX;
1384
- case KeyY$1:
1385
- return KeyY;
1386
- case KeyZ$1:
1387
- return KeyZ;
1388
- case F1$1:
1389
- return F1;
1390
- case F2$1:
1391
- return F2;
1392
- case F3$1:
1393
- return F3;
1394
- case F4$1:
1395
- return F4;
1396
- case F5$1:
1397
- return F5;
1398
- case F6$1:
1399
- return F6;
1400
- case Backslash$1:
1401
- return Backslash;
1402
- case Equal$1:
1403
- return Equal;
1404
- case Comma$1:
1405
- return Comma;
1406
- case Backquote$1:
1407
- return Backquote;
1408
- case Plus$1:
1409
- return Plus;
1410
- case Star$1:
1411
- return Star;
1412
- case Minus$1:
1413
- return Minus;
1414
- default:
1415
- return Unknown;
1416
- }
1020
+ const getKeyBindings = () => {
1021
+ return [{
1022
+ key: DownArrow$1,
1023
+ command: 'TitleBarMenuBar.handleKeyArrowDown',
1024
+ when: FocusTitleBarMenuBar
1025
+ }, {
1026
+ key: UpArrow$1,
1027
+ command: 'TitleBarMenuBar.handleKeyArrowUp',
1028
+ when: FocusTitleBarMenuBar
1029
+ }, {
1030
+ key: RightArrow$1,
1031
+ command: 'TitleBarMenuBar.handleKeyArrowRight',
1032
+ when: FocusTitleBarMenuBar
1033
+ }, {
1034
+ key: LeftArrow$1,
1035
+ command: 'TitleBarMenuBar.handleKeyArrowLeft',
1036
+ when: FocusTitleBarMenuBar
1037
+ }, {
1038
+ key: Space$1,
1039
+ command: 'TitleBarMenuBar.handleKeySpace',
1040
+ when: FocusTitleBarMenuBar
1041
+ }, {
1042
+ key: Home$1,
1043
+ command: 'TitleBarMenuBar.handleKeyHome',
1044
+ when: FocusTitleBarMenuBar
1045
+ }, {
1046
+ key: End$1,
1047
+ command: 'TitleBarMenuBar.handleKeyEnd',
1048
+ when: FocusTitleBarMenuBar
1049
+ }, {
1050
+ key: Escape$1,
1051
+ command: 'TitleBarMenuBar.handleKeyEscape',
1052
+ when: FocusTitleBarMenuBar
1053
+ }];
1417
1054
  };
1418
1055
 
1419
- const CtrlCmd = 1 << 11 >>> 0;
1420
- const Shift = 1 << 10 >>> 0;
1421
-
1422
- const parseKey = rawKey => {
1423
- number(rawKey);
1424
- const isCtrl = Boolean(rawKey & CtrlCmd);
1425
- const isShift = Boolean(rawKey & Shift);
1426
- const keyCode = rawKey & 0x00_00_00_ff;
1427
- const key = getKeyCodeString(keyCode);
1428
- return {
1429
- key,
1430
- isCtrl,
1431
- isShift
1056
+ const emptyObject = {};
1057
+ const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
1058
+ const i18nString = (key, placeholders = emptyObject) => {
1059
+ if (placeholders === emptyObject) {
1060
+ return key;
1061
+ }
1062
+ const replacer = (match, rest) => {
1063
+ return placeholders[rest];
1432
1064
  };
1065
+ return key.replaceAll(RE_PLACEHOLDER, replacer);
1433
1066
  };
1434
1067
 
1435
- const separator = {
1436
- type: Div,
1437
- className: MenuItemSeparator,
1438
- role: Separator$1,
1439
- childCount: 1
1068
+ /**
1069
+ * @enum {string}
1070
+ */
1071
+ const UiStrings$2 = {
1072
+ Copy: 'Copy',
1073
+ CopyLineDown: 'Copy Line Down',
1074
+ CopyLineUp: 'Copy Line Up',
1075
+ Cut: 'Cut',
1076
+ MoveLineDown: 'Move Line Down',
1077
+ MoveLineUp: 'Move Line Up',
1078
+ Paste: 'Paste',
1079
+ Redo: 'Redo',
1080
+ SelectAll: 'Select All',
1081
+ ToggleBlockComment: 'Toggle Block Comment',
1082
+ ToggleLineComment: 'Toggle Line Comment',
1083
+ Undo: 'Undo'};
1084
+ const cut = () => {
1085
+ return i18nString(UiStrings$2.Cut);
1440
1086
  };
1441
- const separatorLine = {
1442
- type: Div,
1443
- className: MenuItemSeparatorLine,
1444
- childCount: 0
1087
+ const copy = () => {
1088
+ return i18nString(UiStrings$2.Copy);
1445
1089
  };
1446
- const checkboxUnchecked = {
1447
- type: Div,
1448
- className: MenuItem,
1449
- role: MenuItemCheckBox,
1450
- ariaChecked: false,
1451
- tabIndex: -1,
1452
- childCount: 1
1090
+ const paste = () => {
1091
+ return i18nString(UiStrings$2.Paste);
1453
1092
  };
1454
- const checkboxChecked = {
1455
- type: Div,
1456
- className: `${MenuItem} MenuItemCheckMark`,
1457
- role: MenuItemCheckBox,
1458
- ariaChecked: true,
1459
- tabIndex: -1,
1460
- childCount: 2
1093
+ const undo = () => {
1094
+ return i18nString(UiStrings$2.Undo);
1461
1095
  };
1462
- const disabled = {
1463
- type: Div,
1464
- className: MenuItem,
1465
- role: MenuItem$1,
1466
- tabIndex: -1,
1467
- disabled: true,
1468
- childCount: 1
1096
+ const redo = () => {
1097
+ return i18nString(UiStrings$2.Redo);
1469
1098
  };
1470
- const arrowRight = {
1471
- type: Div,
1472
- className: MenuItemSubMenuArrowRight,
1473
- childCount: 0
1099
+ const toggleLineComment = () => {
1100
+ return i18nString(UiStrings$2.ToggleLineComment);
1474
1101
  };
1475
- const getMenuItemSeparatorDom = menuItem => {
1476
- return [separator, separatorLine];
1102
+ const toggleBlockComment = () => {
1103
+ return i18nString(UiStrings$2.ToggleBlockComment);
1477
1104
  };
1478
- const getMenuItemCheckedDom = menuItem => {
1479
- const {
1480
- label
1481
- } = menuItem;
1482
- return [checkboxChecked, {
1483
- type: Div,
1484
- className: 'MenuItemCheckmarkIcon MaskIconCheck'
1485
- }, text(label)];
1105
+ const selectAll = () => {
1106
+ return i18nString(UiStrings$2.SelectAll);
1486
1107
  };
1487
- const getMenuItemUncheckedDom = menuItem => {
1488
- const {
1489
- label
1490
- } = menuItem;
1491
- return [checkboxUnchecked, text(label)];
1108
+ const copyLineUp = () => {
1109
+ return i18nString(UiStrings$2.CopyLineUp);
1492
1110
  };
1493
- const getMenuItemDisabledDom = menuItem => {
1494
- const {
1495
- label
1496
- } = menuItem;
1497
- return [disabled, text(label)];
1111
+ const copyLineDown = () => {
1112
+ return i18nString(UiStrings$2.CopyLineDown);
1498
1113
  };
1499
- const getMenuItemDefaultDom = menuItem => {
1500
- const {
1501
- label,
1502
- isFocused,
1503
- key
1504
- } = menuItem;
1505
- let className = MenuItem;
1506
- if (isFocused) {
1507
- className += ' ' + MenuItemFocused;
1508
- }
1509
- const dom = [];
1510
- dom.push({
1511
- type: Div,
1512
- className,
1513
- role: MenuItem$1,
1514
- tabIndex: -1,
1515
- childCount: 1
1516
- }, text(label));
1517
- if (key) {
1518
- dom[0].childCount++;
1519
- const parsedKey = parseKey(key);
1520
- const keyBindingsString = getKeyBindingString(parsedKey.key, false, parsedKey.isCtrl, parsedKey.isShift);
1521
- dom.push({
1522
- type: Span,
1523
- className: 'MenuItemKeyBinding',
1524
- childCount: 1
1525
- }, text(keyBindingsString));
1526
- }
1527
- return dom;
1114
+ const moveLineUp = () => {
1115
+ return i18nString(UiStrings$2.MoveLineUp);
1528
1116
  };
1529
- const getMenuItemSubMenuDom = menuItem => {
1530
- const {
1531
- label,
1532
- isFocused,
1533
- isExpanded,
1534
- level
1535
- } = menuItem;
1536
- let className = MenuItem;
1537
- className += ' ' + MenuItemSubMenu;
1538
- if (isFocused) {
1539
- className += ' ' + MenuItemFocused;
1540
- }
1117
+ const moveLineDown = () => {
1118
+ return i18nString(UiStrings$2.MoveLineDown);
1119
+ };
1120
+
1121
+ const Edit$1 = 2;
1122
+ const File$1 = 5;
1123
+ const Go$1 = 6;
1124
+ const Help$1 = 7;
1125
+ const OpenRecent = 9;
1126
+ const Run$1 = 10;
1127
+ const Selection$1 = 11;
1128
+ const Terminal$1 = 14;
1129
+ const TitleBar = 15;
1130
+ const View$1 = 16;
1131
+
1132
+ const Separator$1 = 1;
1133
+ const None$1 = 0;
1134
+ const SubMenu = 4;
1135
+ const Checked = 2;
1136
+ const Unchecked = 3;
1137
+ const Disabled = 5;
1138
+ const RestoreFocus = 6;
1139
+ const Ignore = 7;
1140
+
1141
+ const menuEntrySeparator = {
1142
+ id: 'separator',
1143
+ label: '',
1144
+ flags: Separator$1,
1145
+ command: ''
1146
+ };
1147
+
1148
+ const id$9 = Edit$1;
1149
+ const getMenuEntries$d = () => {
1541
1150
  return [{
1542
- type: Div,
1543
- className,
1544
- role: MenuItem$1,
1545
- tabIndex: -1,
1546
- ariaHasPopup: true,
1547
- ariaExpanded: isExpanded,
1548
- ariaOwns: isExpanded ? `Menu-${level + 1}` : undefined,
1549
- childCount: 2
1550
- }, text(label), arrowRight];
1151
+ id: 'undo',
1152
+ label: undo(),
1153
+ flags: Disabled,
1154
+ command: /* TODO */-1
1155
+ }, {
1156
+ id: 'redo',
1157
+ label: redo(),
1158
+ flags: Disabled,
1159
+ command: /* TODO */-1
1160
+ }, menuEntrySeparator, {
1161
+ id: 'cut',
1162
+ label: cut(),
1163
+ flags: None$1,
1164
+ command: /* Editor.cut */'Editor.cut'
1165
+ }, {
1166
+ id: 'copy',
1167
+ label: copy(),
1168
+ flags: None$1,
1169
+ command: /* Editor.copy */'Editor.copy'
1170
+ }, {
1171
+ id: 'paste',
1172
+ label: paste(),
1173
+ flags: None$1,
1174
+ command: /* Editor.paste */'Editor.paste'
1175
+ }, menuEntrySeparator, {
1176
+ id: 'toggle-line-comment',
1177
+ label: toggleLineComment(),
1178
+ flags: None$1,
1179
+ command: /* Editor.toggleLineComment */'Editor.toggleLineComment'
1180
+ }, {
1181
+ id: 'toggle-block-comment',
1182
+ label: toggleBlockComment(),
1183
+ flags: None$1,
1184
+ command: /* Editor.toggleBlockComment */'Editor.toggleBlockComment'
1185
+ }];
1186
+ };
1187
+
1188
+ const MenuEntriesEdit = {
1189
+ __proto__: null,
1190
+ getMenuEntries: getMenuEntries$d,
1191
+ id: id$9
1192
+ };
1193
+
1194
+ /**
1195
+ * @enum {string}
1196
+ */
1197
+ const UiStrings$1 = {
1198
+ NewFile: 'New File',
1199
+ NewWindow: 'New Window',
1200
+ OpenFile: 'Open File',
1201
+ OpenFolder: 'Open Folder',
1202
+ OpenRecent: 'Open Recent',
1203
+ Exit: 'Exit',
1204
+ Save: 'Save',
1205
+ SaveAll: 'Save All'
1206
+ };
1207
+ const newFile = () => {
1208
+ return i18nString(UiStrings$1.NewFile);
1209
+ };
1210
+ const newWindow = () => {
1211
+ return i18nString(UiStrings$1.NewWindow);
1551
1212
  };
1552
- const getMenuItemVirtualDom = menuItem => {
1553
- const {
1554
- flags
1555
- } = menuItem;
1556
- switch (flags) {
1557
- case None:
1558
- case RestoreFocus:
1559
- case Ignore:
1560
- return getMenuItemDefaultDom(menuItem);
1561
- case Separator:
1562
- return getMenuItemSeparatorDom();
1563
- case Checked:
1564
- return getMenuItemCheckedDom(menuItem);
1565
- case Unchecked:
1566
- return getMenuItemUncheckedDom(menuItem);
1567
- case Disabled:
1568
- return getMenuItemDisabledDom(menuItem);
1569
- case SubMenu:
1570
- return getMenuItemSubMenuDom(menuItem);
1571
- default:
1572
- return [];
1573
- }
1213
+ const openFile = () => {
1214
+ return i18nString(UiStrings$1.OpenFile);
1574
1215
  };
1575
- const getMenuVirtualDom = menuItems => {
1576
- const dom = [];
1577
- dom.push({
1578
- type: Div,
1579
- className: Menu,
1580
- role: Menu$1,
1581
- tabIndex: -1,
1582
- childCount: menuItems.length
1583
- });
1584
- dom.push(...menuItems.flatMap(getMenuItemVirtualDom));
1585
- return dom;
1216
+ const openFolder = () => {
1217
+ return i18nString(UiStrings$1.OpenFolder);
1586
1218
  };
1587
-
1588
- const getVisible = (items, focusedIndex, expanded, level) => {
1589
- const visibleItems = [];
1590
- const {
1591
- length
1592
- } = items;
1593
- for (let i = 0; i < length; i++) {
1594
- const item = items[i];
1595
- const {
1596
- flags,
1597
- label
1598
- } = item;
1599
- visibleItems.push({
1600
- label,
1601
- flags,
1602
- isFocused: i === focusedIndex,
1603
- isExpanded: i === focusedIndex && expanded,
1604
- level,
1605
- key: item.key
1606
- });
1607
- }
1608
- return visibleItems;
1219
+ const openRecent = () => {
1220
+ return i18nString(UiStrings$1.OpenRecent);
1221
+ };
1222
+ const save = () => {
1223
+ return i18nString(UiStrings$1.Save);
1224
+ };
1225
+ const saveAll = () => {
1226
+ return i18nString(UiStrings$1.SaveAll);
1227
+ };
1228
+ const exit = () => {
1229
+ return i18nString(UiStrings$1.Exit);
1609
1230
  };
1610
1231
 
1611
- const renderMEnus = (oldState, newState) => {
1612
- const oldMenus = oldState.menus;
1613
- const newMenus = newState.menus;
1614
- const oldLength = oldMenus.length;
1615
- const newLength = newMenus.length;
1616
- const commonLength = Math.min(oldLength, newLength);
1617
- const changes = [];
1618
- for (let i = 0; i < commonLength; i++) {
1619
- const oldMenu = oldMenus[i];
1620
- const newMenu = newMenus[i];
1621
- if (oldMenu !== newMenu) {
1622
- const visible = getVisible(newMenu.items, newMenu.focusedIndex, newMenu.expanded, newMenu.level);
1623
- const dom = getMenuVirtualDom(visible).slice(1);
1624
- changes.push([/* method */'updateMenu', newMenu, /* newLength */newLength, dom]);
1625
- }
1626
- }
1627
- const difference = newLength - oldLength;
1628
- if (difference > 0) {
1629
- const newMenu = newMenus.at(-1);
1630
- const visible = getVisible(newMenu.items, newMenu.focusedIndex, newMenu.expanded, newMenu.level);
1631
- const dom = getMenuVirtualDom(visible).slice(1);
1632
- changes.push(['addMenu', newMenu, dom]);
1633
- } else if (difference < 0) {
1634
- changes.push(['closeMenus', newLength]);
1232
+ const Web = 1;
1233
+ const Electron = 2;
1234
+
1235
+ const id$8 = File$1;
1236
+ const getMenuEntries$c = platform => {
1237
+ const entries = [{
1238
+ id: 'newFile',
1239
+ label: newFile(),
1240
+ flags: None$1,
1241
+ command: -1
1242
+ }, {
1243
+ id: 'newWindow',
1244
+ label: newWindow(),
1245
+ flags: None$1,
1246
+ command: /* Window.openNew */'Window.openNew'
1247
+ }, menuEntrySeparator, {
1248
+ id: 'openFile',
1249
+ label: openFile(),
1250
+ flags: None$1,
1251
+ command: 'Dialog.openFile'
1252
+ }, {
1253
+ id: 'openFolder',
1254
+ label: openFolder(),
1255
+ flags: RestoreFocus,
1256
+ command: 'Dialog.openFolder'
1257
+ }, {
1258
+ id: OpenRecent,
1259
+ label: openRecent(),
1260
+ flags: SubMenu,
1261
+ command: ''
1262
+ }, menuEntrySeparator, {
1263
+ id: 'save',
1264
+ label: save(),
1265
+ flags: None$1,
1266
+ command: 'Main.save'
1267
+ }, {
1268
+ id: 'saveAll',
1269
+ label: saveAll(),
1270
+ flags: None$1,
1271
+ command: 'Main.saveAll'
1272
+ }];
1273
+ if (platform === Electron) {
1274
+ entries.push(menuEntrySeparator, {
1275
+ id: 'exit',
1276
+ label: exit(),
1277
+ flags: Ignore,
1278
+ command: 'Chrome.exit'
1279
+ });
1635
1280
  }
1636
- return ['Viewlet.send', newState.uid, /* method */SetMenus, /* changes */changes, newState.uid];
1281
+ return entries;
1637
1282
  };
1638
1283
 
1639
- const getRenderer = diffType => {
1640
- switch (diffType) {
1641
- case RenderEntries:
1642
- return renderEntries;
1643
- case RenderFocusedIndex:
1644
- return renderFocusedIndex;
1645
- case RenderMenus:
1646
- return renderMEnus;
1647
- default:
1648
- throw new Error('unknown renderer');
1649
- }
1284
+ const MenuEntriesFile = {
1285
+ __proto__: null,
1286
+ getMenuEntries: getMenuEntries$c,
1287
+ id: id$8
1650
1288
  };
1651
1289
 
1652
- const applyRender = (oldState, newState, diffResult) => {
1653
- const commands = [];
1654
- for (const item of diffResult) {
1655
- const fn = getRenderer(item);
1656
- commands.push(fn(oldState, newState));
1657
- }
1658
- return commands;
1290
+ const id$7 = Go$1;
1291
+ const getMenuEntries$b = () => {
1292
+ return [];
1659
1293
  };
1660
1294
 
1661
- const doRender = async uid => {
1662
- const {
1663
- oldState,
1664
- newState
1665
- } = get(uid);
1666
- const diffResult = diff(oldState, newState);
1667
- const commands = applyRender(oldState, newState, diffResult);
1668
- return commands;
1295
+ const MenuEntriesGo = {
1296
+ __proto__: null,
1297
+ getMenuEntries: getMenuEntries$b,
1298
+ id: id$7
1669
1299
  };
1670
1300
 
1671
- const commandsIds = ['closeMenu', 'focus', 'focusFirst', 'focusIndex', 'focusLast', 'focusNext', 'focusPrevious', 'handleClick', 'handleFocus', 'handleKeyArrowDown', 'handleKeyArrowLeft', 'handleKeyArrowRight', 'handleKeyArrowUp', 'handleKeyEnd', 'handleKeyEnter', 'handleKeyEscape', 'handleKeyHome', 'handleKeySpace', 'handleMenuClick', 'handleMenuMouseOver', 'handleMouseOut', 'handleMouseOver', 'handlePointerOver', 'handlePointerOut', 'toggleIndex', 'toggleMenu'];
1301
+ const About = 'About';
1302
+ const CheckForUpdates = 'Check For Updates';
1303
+ const ClearRecentlyOpened = 'Clear Recently Opened';
1304
+ const Edit = 'Edit';
1305
+ const File = 'File';
1306
+ const Go = 'Go';
1307
+ const Help = 'Help';
1308
+ const MoreDot = 'More ...';
1309
+ const OpenProcessExplorer = 'Open Process Explorer';
1310
+ const Run = 'Run';
1311
+ const Selection = 'Selection';
1312
+ const Terminal = 'Terminal';
1313
+ const ToggleDeveloperTools = 'Toggle Developer Tools';
1314
+ const View = 'View';
1672
1315
 
1673
- const getCommandIds = () => {
1674
- return commandsIds;
1316
+ const toggleDeveloperTools = () => {
1317
+ return i18nString(ToggleDeveloperTools);
1318
+ };
1319
+ const openProcessExplorer = () => {
1320
+ return i18nString(OpenProcessExplorer);
1321
+ };
1322
+ const checkForUpdates = () => {
1323
+ return i18nString(CheckForUpdates);
1324
+ };
1325
+ const about = () => {
1326
+ return i18nString(About);
1675
1327
  };
1676
1328
 
1677
- const FocusTitleBarMenuBar = 26;
1329
+ const isAutoUpdateSupported = platform => {
1330
+ if (platform !== Electron) {
1331
+ return false;
1332
+ }
1333
+ return false;
1334
+ };
1678
1335
 
1679
- const getKeyBindings = () => {
1680
- return [{
1681
- key: DownArrow$1,
1682
- command: 'TitleBarMenuBar.handleKeyArrowDown',
1683
- when: FocusTitleBarMenuBar
1684
- }, {
1685
- key: UpArrow$1,
1686
- command: 'TitleBarMenuBar.handleKeyArrowUp',
1687
- when: FocusTitleBarMenuBar
1688
- }, {
1689
- key: RightArrow$1,
1690
- command: 'TitleBarMenuBar.handleKeyArrowRight',
1691
- when: FocusTitleBarMenuBar
1692
- }, {
1693
- key: LeftArrow$1,
1694
- command: 'TitleBarMenuBar.handleKeyArrowLeft',
1695
- when: FocusTitleBarMenuBar
1696
- }, {
1697
- key: Space$1,
1698
- command: 'TitleBarMenuBar.handleKeySpace',
1699
- when: FocusTitleBarMenuBar
1700
- }, {
1701
- key: Home$1,
1702
- command: 'TitleBarMenuBar.handleKeyHome',
1703
- when: FocusTitleBarMenuBar
1704
- }, {
1705
- key: End$1,
1706
- command: 'TitleBarMenuBar.handleKeyEnd',
1707
- when: FocusTitleBarMenuBar
1708
- }, {
1709
- key: Escape$1,
1710
- command: 'TitleBarMenuBar.handleKeyEscape',
1711
- when: FocusTitleBarMenuBar
1712
- }];
1336
+ const id$6 = Help$1;
1337
+ const getMenuEntries$a = async platform => {
1338
+ const autoUpdateSupported = isAutoUpdateSupported(platform);
1339
+ const entries = [];
1340
+ if (platform !== Web) {
1341
+ entries.push({
1342
+ id: 'toggleDeveloperTools',
1343
+ label: toggleDeveloperTools(),
1344
+ flags: None$1,
1345
+ command: 'Developer.toggleDeveloperTools'
1346
+ }, {
1347
+ id: 'openProcessExplorer',
1348
+ label: openProcessExplorer(),
1349
+ flags: RestoreFocus,
1350
+ command: 'Developer.openProcessExplorer'
1351
+ });
1352
+ }
1353
+ if (autoUpdateSupported) {
1354
+ entries.push(menuEntrySeparator, {
1355
+ id: 'checkForUpdates',
1356
+ label: checkForUpdates(),
1357
+ flags: RestoreFocus,
1358
+ command: 'AutoUpdater.checkForUpdates'
1359
+ });
1360
+ }
1361
+ if (entries.length > 0) {
1362
+ entries.push(menuEntrySeparator);
1363
+ }
1364
+ entries.push({
1365
+ id: 'about',
1366
+ label: about(),
1367
+ flags: RestoreFocus,
1368
+ command: 'About.showAbout'
1369
+ });
1370
+ return entries;
1713
1371
  };
1714
1372
 
1715
- /**
1716
- * @enum {string}
1717
- */
1718
- const UiStrings$2 = {
1719
- Copy: 'Copy',
1720
- CopyLineDown: 'Copy Line Down',
1721
- CopyLineUp: 'Copy Line Up',
1722
- Cut: 'Cut',
1723
- MoveLineDown: 'Move Line Down',
1724
- MoveLineUp: 'Move Line Up',
1725
- Paste: 'Paste',
1726
- Redo: 'Redo',
1727
- SelectAll: 'Select All',
1728
- ToggleBlockComment: 'Toggle Block Comment',
1729
- ToggleLineComment: 'Toggle Line Comment',
1730
- Undo: 'Undo'};
1731
- const cut = () => {
1732
- return i18nString(UiStrings$2.Cut);
1733
- };
1734
- const copy = () => {
1735
- return i18nString(UiStrings$2.Copy);
1373
+ const MenuEntriesHelp = {
1374
+ __proto__: null,
1375
+ getMenuEntries: getMenuEntries$a,
1376
+ id: id$6
1736
1377
  };
1737
- const paste = () => {
1738
- return i18nString(UiStrings$2.Paste);
1378
+
1379
+ const state$1 = {
1380
+ rpc: undefined
1739
1381
  };
1740
- const undo = () => {
1741
- return i18nString(UiStrings$2.Undo);
1382
+ const invoke = (method, ...params) => {
1383
+ const {
1384
+ rpc
1385
+ } = state$1;
1386
+ // @ts-ignore
1387
+ return rpc.invoke(method, ...params);
1742
1388
  };
1743
- const redo = () => {
1744
- return i18nString(UiStrings$2.Redo);
1389
+ const setRpc = rpc => {
1390
+ state$1.rpc = rpc;
1745
1391
  };
1746
- const toggleLineComment = () => {
1747
- return i18nString(UiStrings$2.ToggleLineComment);
1392
+
1393
+ const getRecentlyOpened = () => {
1394
+ return invoke(/* RecentlyOpened.getRecentlyOpened */'RecentlyOpened.getRecentlyOpened');
1748
1395
  };
1749
- const toggleBlockComment = () => {
1750
- return i18nString(UiStrings$2.ToggleBlockComment);
1396
+
1397
+ const getTitle = uri => {
1398
+ if (!uri) {
1399
+ return '';
1400
+ }
1401
+ return uri;
1751
1402
  };
1752
- const selectAll = () => {
1753
- return i18nString(UiStrings$2.SelectAll);
1403
+
1404
+ const moreDot = () => {
1405
+ return i18nString(MoreDot);
1754
1406
  };
1755
- const copyLineUp = () => {
1756
- return i18nString(UiStrings$2.CopyLineUp);
1407
+ const clearRecentlyOpened = () => {
1408
+ return i18nString(ClearRecentlyOpened);
1757
1409
  };
1758
- const copyLineDown = () => {
1759
- return i18nString(UiStrings$2.CopyLineDown);
1410
+
1411
+ const MAX_MENU_RECENT_ENTRIES = 10;
1412
+ const toMenuItem = folder => {
1413
+ const label = getTitle(folder);
1414
+ return {
1415
+ label,
1416
+ flags: None$1,
1417
+ command: 'Workspace.setPath',
1418
+ args: [folder]
1419
+ };
1760
1420
  };
1761
- const moveLineUp = () => {
1762
- return i18nString(UiStrings$2.MoveLineUp);
1421
+ const id$5 = OpenRecent;
1422
+ const getMenuEntries$9 = async () => {
1423
+ const allItems = await getRecentlyOpened();
1424
+ const itemsToShow = allItems.slice(0, MAX_MENU_RECENT_ENTRIES);
1425
+ const items = [];
1426
+ if (itemsToShow.length > 0) {
1427
+ items.push(...itemsToShow.map(toMenuItem), menuEntrySeparator);
1428
+ }
1429
+ items.push({
1430
+ id: 'more',
1431
+ label: moreDot(),
1432
+ flags: None$1,
1433
+ command: 'QuickPick.showRecent'
1434
+ }, menuEntrySeparator, {
1435
+ id: 'clearRecentlyOpened',
1436
+ label: clearRecentlyOpened(),
1437
+ flags: None$1,
1438
+ command: 'RecentlyOpened.clearRecentlyOpened'
1439
+ });
1440
+ return items;
1763
1441
  };
1764
- const moveLineDown = () => {
1765
- return i18nString(UiStrings$2.MoveLineDown);
1442
+
1443
+ const MenuEntriesOpenRecent = {
1444
+ __proto__: null,
1445
+ getMenuEntries: getMenuEntries$9,
1446
+ id: id$5
1766
1447
  };
1767
1448
 
1768
- const Edit = 2;
1769
- const File = 5;
1770
- const Go = 6;
1771
- const Help = 7;
1772
- const OpenRecent = 9;
1773
- const Run = 10;
1774
- const Selection = 11;
1775
- const Terminal = 14;
1776
- const TitleBar = 15;
1777
- const View = 16;
1449
+ const id$4 = Run$1;
1450
+ const getMenuEntries$8 = () => {
1451
+ return [];
1452
+ };
1778
1453
 
1779
- const menuEntrySeparator = {
1780
- id: 'separator',
1781
- label: '',
1782
- flags: Separator,
1783
- command: ''
1454
+ const MenuEntriesRun = {
1455
+ __proto__: null,
1456
+ getMenuEntries: getMenuEntries$8,
1457
+ id: id$4
1784
1458
  };
1785
1459
 
1786
- const id$9 = Edit;
1787
- const getMenuEntries$d = () => {
1460
+ const id$3 = Selection$1;
1461
+ const getMenuEntries$7 = () => {
1788
1462
  return [{
1789
- id: 'undo',
1790
- label: undo(),
1791
- flags: Disabled,
1792
- command: /* TODO */-1
1463
+ id: 'selectAll',
1464
+ label: selectAll(),
1465
+ flags: None$1,
1466
+ command: 'Editor.selectAll'
1793
1467
  }, {
1794
- id: 'redo',
1795
- label: redo(),
1796
- flags: Disabled,
1797
- command: /* TODO */-1
1798
- }, menuEntrySeparator, {
1799
- id: 'cut',
1800
- label: cut(),
1801
- flags: None,
1802
- command: /* Editor.cut */'Editor.cut'
1468
+ id: 'copyLineUp',
1469
+ label: copyLineUp(),
1470
+ flags: None$1,
1471
+ command: 'Editor.copyLineUp'
1803
1472
  }, {
1804
- id: 'copy',
1805
- label: copy(),
1806
- flags: None,
1807
- command: /* Editor.copy */'Editor.copy'
1473
+ id: 'copyLineDown',
1474
+ label: copyLineDown(),
1475
+ flags: None$1,
1476
+ command: 'Editor.copyLineDown'
1808
1477
  }, {
1809
- id: 'paste',
1810
- label: paste(),
1811
- flags: None,
1812
- command: /* Editor.paste */'Editor.paste'
1813
- }, menuEntrySeparator, {
1814
- id: 'toggle-line-comment',
1815
- label: toggleLineComment(),
1816
- flags: None,
1817
- command: /* Editor.toggleLineComment */'Editor.toggleLineComment'
1478
+ id: 'moveLineUp',
1479
+ label: moveLineUp(),
1480
+ flags: Disabled,
1481
+ command: 'Editor.moveLineUp'
1818
1482
  }, {
1819
- id: 'toggle-block-comment',
1820
- label: toggleBlockComment(),
1821
- flags: None,
1822
- command: /* Editor.toggleBlockComment */'Editor.toggleBlockComment'
1483
+ id: 'moveLineDown',
1484
+ label: moveLineDown(),
1485
+ flags: Disabled,
1486
+ command: 'Editor.moveLineDown'
1823
1487
  }];
1824
1488
  };
1825
1489
 
1826
- const MenuEntriesEdit = {
1490
+ const MenuEntriesSelection = {
1827
1491
  __proto__: null,
1828
- getMenuEntries: getMenuEntries$d,
1829
- id: id$9
1492
+ getMenuEntries: getMenuEntries$7,
1493
+ id: id$3
1830
1494
  };
1831
1495
 
1832
1496
  /**
1833
1497
  * @enum {string}
1834
1498
  */
1835
- const UiStrings$1 = {
1836
- NewFile: 'New File',
1837
- NewWindow: 'New Window',
1838
- OpenFile: 'Open File',
1839
- OpenFolder: 'Open Folder',
1840
- OpenRecent: 'Open Recent',
1841
- Exit: 'Exit',
1842
- Save: 'Save',
1843
- SaveAll: 'Save All'
1499
+ const UiStrings = {
1500
+ NewTerminal: 'New Terminal'
1844
1501
  };
1845
- const newFile = () => {
1846
- return i18nString(UiStrings$1.NewFile);
1502
+ const newTerminal = () => {
1503
+ return i18nString(UiStrings.NewTerminal);
1847
1504
  };
1848
- const newWindow = () => {
1849
- return i18nString(UiStrings$1.NewWindow);
1505
+
1506
+ const id$2 = Terminal$1;
1507
+ const getMenuEntries$6 = () => {
1508
+ return [{
1509
+ id: 'newTerminal',
1510
+ label: newTerminal(),
1511
+ flags: None$1,
1512
+ command: 'Layout.togglePanel',
1513
+ args: ['Terminal']
1514
+ }];
1850
1515
  };
1851
- const openFile = () => {
1852
- return i18nString(UiStrings$1.OpenFile);
1516
+
1517
+ const MenuEntriesTerminal = {
1518
+ __proto__: null,
1519
+ getMenuEntries: getMenuEntries$6,
1520
+ id: id$2
1853
1521
  };
1854
- const openFolder = () => {
1855
- return i18nString(UiStrings$1.OpenFolder);
1522
+
1523
+ const file = () => {
1524
+ return i18nString(File);
1856
1525
  };
1857
- const openRecent = () => {
1858
- return i18nString(UiStrings$1.OpenRecent);
1526
+ const edit = () => {
1527
+ return i18nString(Edit);
1859
1528
  };
1860
- const save = () => {
1861
- return i18nString(UiStrings$1.Save);
1529
+ const selection = () => {
1530
+ return i18nString(Selection);
1862
1531
  };
1863
- const saveAll = () => {
1864
- return i18nString(UiStrings$1.SaveAll);
1532
+ const view = () => {
1533
+ return i18nString(View);
1865
1534
  };
1866
- const exit = () => {
1867
- return i18nString(UiStrings$1.Exit);
1535
+ const go = () => {
1536
+ return i18nString(Go);
1537
+ };
1538
+ const run = () => {
1539
+ return i18nString(Run);
1540
+ };
1541
+ const terminal = () => {
1542
+ return i18nString(Terminal);
1543
+ };
1544
+ const help = () => {
1545
+ return i18nString(Help);
1868
1546
  };
1869
1547
 
1870
- const Web = 1;
1871
- const Electron = 2;
1548
+ const getMenuEntries$5 = () => {
1549
+ return [{
1550
+ id: File$1,
1551
+ label: file(),
1552
+ flags: SubMenu
1553
+ }, {
1554
+ id: Edit$1,
1555
+ label: edit(),
1556
+ flags: SubMenu
1557
+ }, {
1558
+ id: Selection$1,
1559
+ label: selection(),
1560
+ flags: SubMenu
1561
+ }, {
1562
+ id: View$1,
1563
+ label: view(),
1564
+ flags: SubMenu
1565
+ }, {
1566
+ id: Go$1,
1567
+ label: go(),
1568
+ flags: SubMenu
1569
+ }, {
1570
+ id: Run$1,
1571
+ label: run(),
1572
+ keyboardShortCut: 'Alt+r',
1573
+ flags: SubMenu
1574
+ }, {
1575
+ id: Terminal$1,
1576
+ label: terminal(),
1577
+ keyboardShortCut: 'Alt+t',
1578
+ flags: SubMenu
1579
+ }, {
1580
+ id: Help$1,
1581
+ label: help(),
1582
+ keyboardShortCut: 'Alt+h',
1583
+ flags: SubMenu
1584
+ }];
1585
+ };
1872
1586
 
1873
- const id$8 = File;
1874
- const getMenuEntries$c = platform => {
1875
- const entries = [{
1876
- id: 'newFile',
1877
- label: newFile(),
1878
- flags: None,
1879
- command: -1
1587
+ const getMenuEntries$4 = () => {
1588
+ return [{
1589
+ id: File$1,
1590
+ label: file(),
1591
+ flags: None$1
1880
1592
  }, {
1881
- id: 'newWindow',
1882
- label: newWindow(),
1883
- flags: None,
1884
- command: /* Window.openNew */'Window.openNew'
1885
- }, menuEntrySeparator, {
1886
- id: 'openFile',
1887
- label: openFile(),
1888
- flags: None,
1889
- command: 'Dialog.openFile'
1593
+ id: Edit$1,
1594
+ label: edit(),
1595
+ flags: None$1
1890
1596
  }, {
1891
- id: 'openFolder',
1892
- label: openFolder(),
1893
- flags: RestoreFocus,
1894
- command: 'Dialog.openFolder'
1597
+ id: Selection$1,
1598
+ label: selection(),
1599
+ flags: None$1
1895
1600
  }, {
1896
- id: OpenRecent,
1897
- label: openRecent(),
1898
- flags: SubMenu,
1899
- command: ''
1900
- }, menuEntrySeparator, {
1901
- id: 'save',
1902
- label: save(),
1903
- flags: None,
1904
- command: 'Main.save'
1601
+ id: View$1,
1602
+ label: view(),
1603
+ flags: None$1
1905
1604
  }, {
1906
- id: 'saveAll',
1907
- label: saveAll(),
1908
- flags: None,
1909
- command: 'Main.saveAll'
1605
+ id: Go$1,
1606
+ label: go(),
1607
+ flags: None$1
1608
+ }, {
1609
+ id: Help$1,
1610
+ label: help(),
1611
+ flags: None$1
1910
1612
  }];
1911
- if (platform === Electron) {
1912
- entries.push(menuEntrySeparator, {
1913
- id: 'exit',
1914
- label: exit(),
1915
- flags: Ignore,
1916
- command: 'Chrome.exit'
1917
- });
1613
+ };
1614
+
1615
+ const getFn = platform => {
1616
+ switch (platform) {
1617
+ case Web:
1618
+ return getMenuEntries$4;
1619
+ default:
1620
+ return getMenuEntries$5;
1918
1621
  }
1919
- return entries;
1622
+ };
1623
+ const id$1 = TitleBar;
1624
+ const getMenuEntries$3 = async platform => {
1625
+ const fn = getFn(platform);
1626
+ return fn();
1920
1627
  };
1921
1628
 
1922
- const MenuEntriesFile = {
1629
+ const MenuEntriesTitleBar = {
1923
1630
  __proto__: null,
1924
- getMenuEntries: getMenuEntries$c,
1925
- id: id$8
1631
+ getMenuEntries: getMenuEntries$3,
1632
+ id: id$1
1926
1633
  };
1927
1634
 
1928
- const id$7 = Go;
1929
- const getMenuEntries$b = () => {
1635
+ const id = View$1;
1636
+ const getMenuEntries$2 = () => {
1930
1637
  return [];
1931
1638
  };
1932
1639
 
1933
- const MenuEntriesGo = {
1640
+ const MenuEntriesView = {
1934
1641
  __proto__: null,
1935
- getMenuEntries: getMenuEntries$b,
1936
- id: id$7
1642
+ getMenuEntries: getMenuEntries$2,
1643
+ id
1937
1644
  };
1938
1645
 
1939
- const toggleDeveloperTools = () => {
1940
- return i18nString(ToggleDeveloperTools);
1646
+ const menus$1 = [MenuEntriesEdit, MenuEntriesFile, MenuEntriesGo, MenuEntriesHelp, MenuEntriesRun, MenuEntriesSelection, MenuEntriesTerminal, MenuEntriesTitleBar, MenuEntriesView, MenuEntriesOpenRecent];
1647
+
1648
+ const getMenuIds = () => {
1649
+ return menus$1.map(menu => menu.id);
1941
1650
  };
1942
- const openProcessExplorer = () => {
1943
- return i18nString(OpenProcessExplorer);
1651
+ const getMenuEntries$1 = (id, platform) => {
1652
+ const menu = menus$1.find(item => item.id === id);
1653
+ if (!menu) {
1654
+ return [];
1655
+ }
1656
+ return menu.getMenuEntries(platform);
1944
1657
  };
1945
- const checkForUpdates = () => {
1946
- return i18nString(CheckForUpdates);
1658
+
1659
+ const MaskIconCheck = 'MaskIconCheck';
1660
+ const Menu$1 = 'Menu';
1661
+ const MenuItem$1 = 'MenuItem';
1662
+ const MenuItemCheckMark = 'MenuItemCheckMark';
1663
+ const MenuItemCheckmarkIcon = 'MenuItemCheckmarkIcon';
1664
+ const MenuItemFocused = 'MenuItemFocused';
1665
+ const MenuItemKeyBinding = 'MenuItemKeyBinding';
1666
+ const MenuItemSeparator = 'MenuItemSeparator';
1667
+ const MenuItemSeparatorLine = 'MenuItemSeparatorLine';
1668
+ const MenuItemSubMenu = 'MenuItemSubMenu';
1669
+ const MenuItemSubMenuArrowRight = 'MenuItemSubMenuArrowRight';
1670
+ const TitleBarButtons = 'TitleBarButtons';
1671
+ const TitleBarEntryActive = 'TitleBarEntryActive';
1672
+ const TitleBarIcon = 'TitleBarIcon';
1673
+ const TitleBarIconIcon = 'TitleBarIconIcon';
1674
+ const TitleBarMenuBar$1 = 'TitleBarMenuBar';
1675
+ const TitleBarTopLevelEntry = 'TitleBarTopLevelEntry';
1676
+ const TitleBarTopLevelEntryLabel = 'TitleBarTopLevelEntryLabel';
1677
+ const Viewlet = 'Viewlet';
1678
+
1679
+ const Menu = 'menu';
1680
+ const MenuBar = 'menubar';
1681
+ const MenuItem = 'menuitem';
1682
+ const MenuItemCheckBox = 'menuitemcheckbox';
1683
+ const None = 'none';
1684
+ const Separator = 'separator';
1685
+
1686
+ const Button = 1;
1687
+ const Div = 4;
1688
+ const Text = 12;
1689
+ const I = 16;
1690
+ const Img = 17;
1691
+ const Span = 8;
1692
+
1693
+ const getIconVirtualDom = (icon, type = Div) => {
1694
+ return {
1695
+ type,
1696
+ className: `MaskIcon MaskIcon${icon}`,
1697
+ role: None,
1698
+ childCount: 0
1699
+ };
1947
1700
  };
1948
- const about = () => {
1949
- return i18nString(About);
1701
+
1702
+ const createTitleBarButton = button => {
1703
+ const {
1704
+ id,
1705
+ icon,
1706
+ label,
1707
+ onClick
1708
+ } = button;
1709
+ const dom = [{
1710
+ type: Button,
1711
+ className: `TitleBarButton TitleBarButton${id}`,
1712
+ ariaLabel: label,
1713
+ childCount: 1,
1714
+ onClick
1715
+ }, getIconVirtualDom(icon, I)];
1716
+ return dom;
1950
1717
  };
1951
1718
 
1952
- const isAutoUpdateSupported = platform => {
1953
- if (platform !== Electron) {
1954
- return false;
1955
- }
1956
- return false;
1719
+ const mergeClassNames = (...classNames) => {
1720
+ return classNames.filter(Boolean).join(' ');
1957
1721
  };
1958
1722
 
1959
- const id$6 = Help;
1960
- const getMenuEntries$a = async platform => {
1961
- const autoUpdateSupported = isAutoUpdateSupported(platform);
1962
- const entries = [];
1963
- if (platform !== Web) {
1964
- entries.push({
1965
- id: 'toggleDeveloperTools',
1966
- label: toggleDeveloperTools(),
1967
- flags: None,
1968
- command: 'Developer.toggleDeveloperTools'
1969
- }, {
1970
- id: 'openProcessExplorer',
1971
- label: openProcessExplorer(),
1972
- flags: RestoreFocus,
1973
- command: 'Developer.openProcessExplorer'
1974
- });
1723
+ const getTitleBarButtonsVirtualDom = buttons => {
1724
+ const dom = [{
1725
+ type: Div,
1726
+ className: mergeClassNames(Viewlet, TitleBarButtons),
1727
+ childCount: buttons.length
1728
+ }, ...buttons.flatMap(createTitleBarButton)];
1729
+ return dom;
1730
+ };
1731
+
1732
+ const getTitleBarIconVirtualDom = iconSrc => {
1733
+ return [{
1734
+ type: Div,
1735
+ className: mergeClassNames(Viewlet, TitleBarIcon),
1736
+ childCount: 1
1737
+ }, {
1738
+ type: Img,
1739
+ className: TitleBarIconIcon,
1740
+ src: iconSrc,
1741
+ alt: '',
1742
+ childCount: 0
1743
+ }];
1744
+ };
1745
+
1746
+ const HandleClick = 'handleClick';
1747
+ const HandleClickMinimize = 'handleClickMinimize';
1748
+ const HandleClickToggleClose = 'handleClickToggleClose';
1749
+ const HandleClickToggleMaximize = 'handleClickToggleMaximize';
1750
+ const HandleFocusIn = 'handleFocusIn';
1751
+ const HandleFocusOut = 'handleFocusOut';
1752
+ const HandlePointerOut = 'handlePointerOut';
1753
+ const HandlePointerOver = 'handlePointerOver';
1754
+ const HandleMenuClick = 'handleMenuClick';
1755
+ const HandleMenuMouseOver = 'handleMenuMouseOver';
1756
+
1757
+ const text = data => {
1758
+ return {
1759
+ type: Text,
1760
+ text: data,
1761
+ childCount: 0
1762
+ };
1763
+ };
1764
+
1765
+ const getItemVirtualDom = item => {
1766
+ // @ts-ignore
1767
+ const {
1768
+ keyboardShortCut,
1769
+ label,
1770
+ isOpen,
1771
+ isFocused
1772
+ } = item;
1773
+ const dom = [];
1774
+ dom.push({
1775
+ type: Div,
1776
+ className: TitleBarTopLevelEntry,
1777
+ ariaHasPopup: true,
1778
+ ariaExpanded: isOpen,
1779
+ role: MenuItem,
1780
+ childCount: 1,
1781
+ ariaKeyShortcuts: keyboardShortCut
1782
+ });
1783
+ if (isOpen) {
1784
+ // @ts-ignore
1785
+ dom[0].ariaOwns = 'Menu-0';
1975
1786
  }
1976
- if (autoUpdateSupported) {
1977
- entries.push(menuEntrySeparator, {
1978
- id: 'checkForUpdates',
1979
- label: checkForUpdates(),
1980
- flags: RestoreFocus,
1981
- command: 'AutoUpdater.checkForUpdates'
1787
+ if (isFocused) {
1788
+ dom[0].className += ' ' + TitleBarEntryActive;
1789
+ // @ts-ignore
1790
+ dom[0].id = 'TitleBarEntryActive';
1791
+ dom.push({
1792
+ type: Div,
1793
+ className: TitleBarTopLevelEntryLabel,
1794
+ childCount: 1
1982
1795
  });
1983
1796
  }
1984
- if (entries.length > 0) {
1985
- entries.push(menuEntrySeparator);
1986
- }
1987
- entries.push({
1988
- id: 'about',
1989
- label: about(),
1990
- flags: RestoreFocus,
1991
- command: 'About.showAbout'
1992
- });
1993
- return entries;
1797
+ dom.push(text(label));
1798
+ return dom;
1799
+ };
1800
+ const getTitleBarMenuBarItemsVirtualDom = visibleItems => {
1801
+ const dom = visibleItems.flatMap(getItemVirtualDom);
1802
+ return dom;
1994
1803
  };
1995
1804
 
1996
- const MenuEntriesHelp = {
1997
- __proto__: null,
1998
- getMenuEntries: getMenuEntries$a,
1999
- id: id$6
1805
+ const activeId = 'TitleBarEntryActive';
1806
+ const getTitleBarMenuBarVirtualDom = (visibleItems, focusedIndex) => {
1807
+ return [{
1808
+ type: Div,
1809
+ className: mergeClassNames(Viewlet, TitleBarMenuBar$1),
1810
+ role: MenuBar,
1811
+ tabIndex: 0,
1812
+ childCount: visibleItems.length,
1813
+ onMouseDown: HandleClick,
1814
+ onFocusOut: HandleFocusOut,
1815
+ onFocusIn: HandleFocusIn,
1816
+ onPointerOver: HandlePointerOver,
1817
+ onPointerOut: HandlePointerOut,
1818
+ ariaActivedescendant: focusedIndex === -1 ? '' : activeId
1819
+ }, ...getTitleBarMenuBarItemsVirtualDom(visibleItems)];
2000
1820
  };
2001
1821
 
2002
- const state$1 = {
2003
- rpc: undefined
1822
+ const getTitleVirtualDom = title => {
1823
+ return [text(title)];
2004
1824
  };
2005
- const invoke = (method, ...params) => {
2006
- const {
2007
- rpc
2008
- } = state$1;
2009
- // @ts-ignore
2010
- return rpc.invoke(method, ...params);
1825
+
1826
+ const handleClickClose = async state => {
1827
+ await invoke('ElectronWindow.close');
1828
+ return state;
2011
1829
  };
2012
- const setRpc = rpc => {
2013
- state$1.rpc = rpc;
1830
+
1831
+ const handleClickMinimize = async state => {
1832
+ await invoke('ElectronWindow.minimize');
1833
+ return state;
2014
1834
  };
2015
1835
 
2016
- const getRecentlyOpened = () => {
2017
- return invoke(/* RecentlyOpened.getRecentlyOpened */'RecentlyOpened.getRecentlyOpened');
1836
+ const handleClickToggleMaximize = async state => {
1837
+ await (invoke('ElectronWindow.maximize'));
1838
+ return state;
2018
1839
  };
2019
1840
 
2020
- const getTitle = uri => {
2021
- if (!uri) {
2022
- return '';
1841
+ const handleClick$1 = (state, className) => {
1842
+ if (className.includes('Minimize')) {
1843
+ return handleClickMinimize(state);
2023
1844
  }
2024
- return uri;
1845
+ if (className.includes('Maximize') || className.includes('Restore')) {
1846
+ return handleClickToggleMaximize(state);
1847
+ }
1848
+ if (className.includes('Close')) {
1849
+ return handleClickClose(state);
1850
+ }
1851
+ return state;
2025
1852
  };
2026
1853
 
2027
- const MAX_MENU_RECENT_ENTRIES = 10;
2028
- const toMenuItem = folder => {
2029
- const label = getTitle(folder);
2030
- return {
2031
- label,
2032
- flags: None,
2033
- command: 'Workspace.setPath',
2034
- args: [folder]
2035
- };
1854
+ const handleContextMenu = state => {
1855
+ return state;
2036
1856
  };
2037
- const id$5 = OpenRecent;
2038
- const getMenuEntries$9 = async () => {
2039
- const allItems = await getRecentlyOpened();
2040
- const itemsToShow = allItems.slice(0, MAX_MENU_RECENT_ENTRIES);
2041
- const items = [];
2042
- if (itemsToShow.length > 0) {
2043
- items.push(...itemsToShow.map(toMenuItem), menuEntrySeparator);
1857
+
1858
+ const getTitleBarIndexFromPosition = (titleBarEntries, x, y) => {
1859
+ let currentX = 0;
1860
+ for (let i = 0; i < titleBarEntries.length; i++) {
1861
+ const entry = titleBarEntries[i];
1862
+ const entryWidth = entry.width;
1863
+ if (x >= currentX && x < currentX + entryWidth) {
1864
+ return i;
1865
+ }
1866
+ currentX += entryWidth;
2044
1867
  }
2045
- items.push({
2046
- id: 'more',
2047
- label: moreDot(),
2048
- flags: None,
2049
- command: 'QuickPick.showRecent'
2050
- }, menuEntrySeparator, {
2051
- id: 'clearRecentlyOpened',
2052
- label: clearRecentlyOpened(),
2053
- flags: None,
2054
- command: 'RecentlyOpened.clearRecentlyOpened'
2055
- });
2056
- return items;
1868
+ return -1;
2057
1869
  };
2058
1870
 
2059
- const MenuEntriesOpenRecent = {
2060
- __proto__: null,
2061
- getMenuEntries: getMenuEntries$9,
2062
- id: id$5
1871
+ const getTotalWidth = entries => {
1872
+ let total = 0;
1873
+ for (const entry of entries) {
1874
+ total += entry.width;
1875
+ }
1876
+ return total;
2063
1877
  };
2064
1878
 
2065
- const id$4 = Run;
2066
- const getMenuEntries$8 = () => {
2067
- return [];
1879
+ // TODO lazyload menuEntries and use Command.execute (maybe)
1880
+ const CONTEXT_MENU_ITEM_HEIGHT = 26;
1881
+ const CONTEXT_MENU_SEPARATOR_HEIGHT = 11;
1882
+ const CONTEXT_MENU_PADDING = 8;
1883
+ const getMenuHeight = items => {
1884
+ let height = CONTEXT_MENU_PADDING;
1885
+ for (const item of items) {
1886
+ switch (item.flags) {
1887
+ case Separator$1:
1888
+ height += CONTEXT_MENU_SEPARATOR_HEIGHT;
1889
+ break;
1890
+ default:
1891
+ height += CONTEXT_MENU_ITEM_HEIGHT;
1892
+ break;
1893
+ }
1894
+ }
1895
+ return height;
2068
1896
  };
2069
1897
 
2070
- const MenuEntriesRun = {
2071
- __proto__: null,
2072
- getMenuEntries: getMenuEntries$8,
2073
- id: id$4
1898
+ // TODO lazyload menuEntries and use Command.execute (maybe)
1899
+ const MENU_WIDTH = 150;
1900
+ const CONTEXT_MENU_WIDTH = 250;
1901
+ const getMenuWidth = () => {
1902
+ return CONTEXT_MENU_WIDTH;
2074
1903
  };
2075
1904
 
2076
- const id$3 = Selection;
2077
- const getMenuEntries$7 = () => {
2078
- return [{
2079
- id: 'selectAll',
2080
- label: selectAll(),
2081
- flags: None,
2082
- command: 'Editor.selectAll'
2083
- }, {
2084
- id: 'copyLineUp',
2085
- label: copyLineUp(),
2086
- flags: None,
2087
- command: 'Editor.copyLineUp'
2088
- }, {
2089
- id: 'copyLineDown',
2090
- label: copyLineDown(),
2091
- flags: None,
2092
- command: 'Editor.copyLineDown'
2093
- }, {
2094
- id: 'moveLineUp',
2095
- label: moveLineUp(),
2096
- flags: Disabled,
2097
- command: 'Editor.moveLineUp'
2098
- }, {
2099
- id: 'moveLineDown',
2100
- label: moveLineDown(),
2101
- flags: Disabled,
2102
- command: 'Editor.moveLineDown'
2103
- }];
1905
+ // TODO difference between focusing with mouse or keyboard
1906
+ // with mouse -> open submenu
1907
+ // with keyboard -> don't open submenu, only focus
1908
+
1909
+ const getIndexToFocusNextStartingAt = (items, startIndex) => {
1910
+ for (let i = startIndex; i < startIndex + items.length; i++) {
1911
+ const index = i % items.length;
1912
+ const item = items[index];
1913
+ if (canBeFocused(item)) {
1914
+ return index;
1915
+ }
1916
+ }
1917
+ return -1;
1918
+ };
1919
+ const getIndexToFocusFirst = items => {
1920
+ return getIndexToFocusNextStartingAt(items, 0);
1921
+ };
1922
+ const getIndexToFocusLast = items => {
1923
+ return getIndexToFocusPreviousStartingAt(items, items.length - 1);
2104
1924
  };
2105
1925
 
2106
- const MenuEntriesSelection = {
2107
- __proto__: null,
2108
- getMenuEntries: getMenuEntries$7,
2109
- id: id$3
1926
+ // TODO this code seems a bit too complicated, maybe it can be simplified
1927
+ const getIndexToFocusPreviousStartingAt = (items, startIndex) => {
1928
+ for (let i = startIndex; i > startIndex - items.length; i--) {
1929
+ const index = (i + items.length) % items.length;
1930
+ const item = items[index];
1931
+ if (canBeFocused(item)) {
1932
+ return index;
1933
+ }
1934
+ }
1935
+ return -1;
1936
+ };
1937
+ const getIndexToFocusPrevious = menu => {
1938
+ const startIndex = menu.focusedIndex === -1 ? menu.items.length - 1 : menu.focusedIndex - 1;
1939
+ return getIndexToFocusPreviousStartingAt(menu.items, startIndex);
1940
+ };
1941
+ const canBeFocused = item => {
1942
+ switch (item.flags) {
1943
+ case Separator$1:
1944
+ case Disabled:
1945
+ return false;
1946
+ default:
1947
+ return true;
1948
+ }
1949
+ };
1950
+ const getIndexToFocusNext = menu => {
1951
+ const startIndex = menu.focusedIndex + 1;
1952
+ return getIndexToFocusNextStartingAt(menu.items, startIndex);
2110
1953
  };
2111
1954
 
2112
- /**
2113
- * @enum {string}
2114
- */
2115
- const UiStrings = {
2116
- NewTerminal: 'New Terminal'
1955
+ // TODO handle printable letter and focus item that starts with that letter
1956
+
1957
+ // TODO pageup / pagedown keys
1958
+
1959
+ // TODO more tests
1960
+
1961
+ const menus = [MenuEntriesEdit, MenuEntriesFile, MenuEntriesGo, MenuEntriesHelp, MenuEntriesRun, MenuEntriesSelection, MenuEntriesTerminal, MenuEntriesTitleBar, MenuEntriesView, MenuEntriesOpenRecent];
1962
+ const getMenus = () => {
1963
+ return menus;
2117
1964
  };
2118
- const newTerminal = () => {
2119
- return i18nString(UiStrings.NewTerminal);
1965
+ const getModule = id => {
1966
+ for (const module of menus) {
1967
+ if (module.id === id) {
1968
+ return module;
1969
+ }
1970
+ }
1971
+ return undefined;
1972
+ };
1973
+ const getMenuEntries = async (id, ...args) => {
1974
+ try {
1975
+ const module = getModule(id);
1976
+ // @ts-ignore
1977
+ const inject = module.inject || [];
1978
+ // @ts-ignore
1979
+ return module.getMenuEntries(...args);
1980
+ } catch (error) {
1981
+ throw new VError(error, `Failed to load menu entries for id ${id}`);
1982
+ }
1983
+ };
1984
+
1985
+ const openMenuAtIndex = async (state, index, shouldBeFocused) => {
1986
+ const {
1987
+ titleBarEntries,
1988
+ titleBarHeight,
1989
+ x
1990
+ } = state;
1991
+ // TODO race conditions
1992
+ // TODO send renderer process
1993
+ // 1. open menu, items to show
1994
+ // 2. focus menu
1995
+ const titleBarEntry = titleBarEntries[index];
1996
+ const {
1997
+ id
1998
+ } = titleBarEntry;
1999
+ const items = await getMenuEntries(id);
2000
+ const relevantEntries = titleBarEntries.slice(0, index);
2001
+ const totalWidths = getTotalWidth(relevantEntries);
2002
+ const offset = totalWidths;
2003
+ // TODO race condition: another menu might already be open at this point
2004
+
2005
+ const menuX = x + offset;
2006
+ const menuY = titleBarHeight;
2007
+ const width = getMenuWidth();
2008
+ const height = getMenuHeight(items);
2009
+ const menuFocusedIndex = shouldBeFocused ? getIndexToFocusNextStartingAt(items, 0) : -1;
2010
+ const menu = {
2011
+ id,
2012
+ items,
2013
+ focusedIndex: menuFocusedIndex,
2014
+ level: 0,
2015
+ x: menuX,
2016
+ y: menuY,
2017
+ width,
2018
+ height
2019
+ };
2020
+ const menus = [menu];
2021
+ return {
2022
+ ...state,
2023
+ isMenuOpen: true,
2024
+ focusedIndex: index,
2025
+ menus
2026
+ };
2120
2027
  };
2121
2028
 
2122
- const id$2 = Terminal;
2123
- const getMenuEntries$6 = () => {
2124
- return [{
2125
- id: 'newTerminal',
2126
- label: newTerminal(),
2127
- flags: None,
2128
- command: 'Layout.togglePanel',
2129
- args: ['Terminal']
2130
- }];
2029
+ const focusIndex = async (state, index) => {
2030
+ object(state);
2031
+ number(index);
2032
+ const {
2033
+ isMenuOpen,
2034
+ focusedIndex
2035
+ } = state;
2036
+ if (index === focusedIndex) {
2037
+ return state;
2038
+ }
2039
+ if (isMenuOpen) {
2040
+ return openMenuAtIndex(state, index, /* focus */false);
2041
+ }
2042
+ return {
2043
+ ...state,
2044
+ focusedIndex: index
2045
+ };
2131
2046
  };
2132
2047
 
2133
- const MenuEntriesTerminal = {
2134
- __proto__: null,
2135
- getMenuEntries: getMenuEntries$6,
2136
- id: id$2
2048
+ const handleMouseOutMenuClosed = state => {
2049
+ return focusIndex(state, -1);
2137
2050
  };
2138
2051
 
2139
- const file = () => {
2140
- return i18nString(File$1);
2141
- };
2142
- const edit = () => {
2143
- return i18nString(Edit$1);
2144
- };
2145
- const selection = () => {
2146
- return i18nString(Selection$1);
2147
- };
2148
- const view = () => {
2149
- return i18nString(View$1);
2150
- };
2151
- const go = () => {
2152
- return i18nString(Go$1);
2153
- };
2154
- const run = () => {
2155
- return i18nString(Run$1);
2156
- };
2157
- const terminal = () => {
2158
- return i18nString(Terminal$1);
2159
- };
2160
- const help = () => {
2161
- return i18nString(Help$1);
2052
+ const handleMouseOutMenuOpen = state => {
2053
+ return state;
2162
2054
  };
2163
2055
 
2164
- const getMenuEntries$5 = () => {
2165
- return [{
2166
- id: File,
2167
- label: file(),
2168
- flags: SubMenu
2169
- }, {
2170
- id: Edit,
2171
- label: edit(),
2172
- flags: SubMenu
2173
- }, {
2174
- id: Selection,
2175
- label: selection(),
2176
- flags: SubMenu
2177
- }, {
2178
- id: View,
2179
- label: view(),
2180
- flags: SubMenu
2181
- }, {
2182
- id: Go,
2183
- label: go(),
2184
- flags: SubMenu
2185
- }, {
2186
- id: Run,
2187
- label: run(),
2188
- keyboardShortCut: 'Alt+r',
2189
- flags: SubMenu
2190
- }, {
2191
- id: Terminal,
2192
- label: terminal(),
2193
- keyboardShortCut: 'Alt+t',
2194
- flags: SubMenu
2195
- }, {
2196
- id: Help,
2197
- label: help(),
2198
- keyboardShortCut: 'Alt+h',
2199
- flags: SubMenu
2200
- }];
2056
+ const ifElse = (menuOpenFunction, menuClosedFunction) => {
2057
+ const ifElseFunction = (state, ...args) => {
2058
+ const {
2059
+ isMenuOpen
2060
+ } = state;
2061
+ if (isMenuOpen) {
2062
+ return menuOpenFunction(state, ...args);
2063
+ }
2064
+ return menuClosedFunction(state, ...args);
2065
+ };
2066
+ return ifElseFunction;
2201
2067
  };
2202
2068
 
2203
- const getMenuEntries$4 = () => {
2204
- return [{
2205
- id: File,
2206
- label: file(),
2207
- flags: None
2208
- }, {
2209
- id: Edit,
2210
- label: edit(),
2211
- flags: None
2212
- }, {
2213
- id: Selection,
2214
- label: selection(),
2215
- flags: None
2216
- }, {
2217
- id: View,
2218
- label: view(),
2219
- flags: None
2220
- }, {
2221
- id: Go,
2222
- label: go(),
2223
- flags: None
2224
- }, {
2225
- id: Help,
2226
- label: help(),
2227
- flags: None
2228
- }];
2229
- };
2069
+ const handleMouseOut = ifElse(handleMouseOutMenuOpen, handleMouseOutMenuClosed);
2230
2070
 
2231
- const getFn = platform => {
2232
- switch (platform) {
2233
- case Web:
2234
- return getMenuEntries$4;
2235
- default:
2236
- return getMenuEntries$5;
2071
+ const handlePointerOut = (state, clientX, clientY) => {
2072
+ const index = getTitleBarIndexFromPosition(state.titleBarEntries, clientX - state.x);
2073
+ if (index === -1) {
2074
+ return state;
2237
2075
  }
2238
- };
2239
- const id$1 = TitleBar;
2240
- const getMenuEntries$3 = async platform => {
2241
- const fn = getFn(platform);
2242
- return fn();
2243
- };
2244
-
2245
- const MenuEntriesTitleBar = {
2246
- __proto__: null,
2247
- getMenuEntries: getMenuEntries$3,
2248
- id: id$1
2076
+ return handleMouseOut(state, index);
2249
2077
  };
2250
2078
 
2251
- const id = View;
2252
- const getMenuEntries$2 = () => {
2253
- return [];
2079
+ const handleMouseOverMenuClosed = (state, index) => {
2080
+ return focusIndex(state, index);
2254
2081
  };
2255
2082
 
2256
- const MenuEntriesView = {
2257
- __proto__: null,
2258
- getMenuEntries: getMenuEntries$2,
2259
- id
2083
+ const handleMouseOverMenuOpen = async (state, index) => {
2084
+ if (index === -1) {
2085
+ return state;
2086
+ }
2087
+ return focusIndex(state, index);
2260
2088
  };
2261
2089
 
2262
- const menus$1 = [MenuEntriesEdit, MenuEntriesFile, MenuEntriesGo, MenuEntriesHelp, MenuEntriesRun, MenuEntriesSelection, MenuEntriesTerminal, MenuEntriesTitleBar, MenuEntriesView, MenuEntriesOpenRecent];
2090
+ const handleMouseOver = ifElse(handleMouseOverMenuOpen, handleMouseOverMenuClosed);
2263
2091
 
2264
- const getMenuIds = () => {
2265
- return menus$1.map(menu => menu.id);
2266
- };
2267
- const getMenuEntries$1 = (id, platform) => {
2268
- const menu = menus$1.find(item => item.id === id);
2269
- if (!menu) {
2270
- return [];
2092
+ const handlePointerOver = (state, clientX, clientY) => {
2093
+ const index = getTitleBarIndexFromPosition(state.titleBarEntries, clientX - state.x);
2094
+ if (index === -1) {
2095
+ return state;
2271
2096
  }
2272
- return menu.getMenuEntries(platform);
2097
+ return handleMouseOver(state, index);
2273
2098
  };
2274
2099
 
2275
- const getIconVirtualDom = (icon, type = Div) => {
2276
- return {
2277
- type,
2278
- className: `MaskIcon MaskIcon${icon}`,
2279
- role: None$1,
2280
- childCount: 0
2281
- };
2100
+ const getFontString = (fontWeight, fontSize, fontFamily) => {
2101
+ return `${fontWeight} ${fontSize}px ${fontFamily}`;
2282
2102
  };
2283
2103
 
2284
- const createTitleBarButton = button => {
2285
- const {
2286
- id,
2287
- icon,
2288
- label,
2289
- onClick
2290
- } = button;
2291
- const dom = [{
2292
- type: Button,
2293
- className: `TitleBarButton TitleBarButton${id}`,
2294
- ariaLabel: label,
2295
- childCount: 1,
2296
- onClick
2297
- }, getIconVirtualDom(icon, I)];
2298
- return dom;
2104
+ const state = {
2105
+ ctx: undefined
2299
2106
  };
2300
-
2301
- const getTitleBarButtonsVirtualDom = buttons => {
2302
- const dom = [{
2303
- type: Div,
2304
- className: 'Viewlet TitleBarButtons',
2305
- childCount: buttons.length
2306
- }, ...buttons.flatMap(createTitleBarButton)];
2307
- return dom;
2107
+ const getOrCreate = createCtx => {
2108
+ if (state.ctx) {
2109
+ return state.ctx;
2110
+ }
2111
+ state.ctx = createCtx();
2112
+ return state.ctx;
2308
2113
  };
2309
2114
 
2310
- const getTitleBarIconVirtualDom = iconSrc => {
2311
- return [{
2312
- type: Div,
2313
- className: 'Viewlet TitleBarIcon',
2314
- childCount: 1
2315
- }, {
2316
- type: Img,
2317
- className: 'TitleBarIconIcon',
2318
- src: iconSrc,
2319
- alt: '',
2320
- childCount: 0
2321
- }];
2115
+ const createCtx = () => {
2116
+ const canvas = new OffscreenCanvas(0, 0);
2117
+ const ctx = /** @type {OffscreenCanvasRenderingContext2D} */canvas.getContext('2d');
2118
+ if (!ctx) {
2119
+ throw new Error('Failed to get canvas context 2d');
2120
+ }
2121
+ return ctx;
2322
2122
  };
2323
-
2324
- const getTitleVirtualDom = title => {
2325
- return [text(title)];
2123
+ const getContext = () => {
2124
+ const ctx = getOrCreate(createCtx);
2125
+ return ctx;
2326
2126
  };
2327
2127
 
2328
- const handleClickClose = async state => {
2329
- await invoke('ElectronWindow.close');
2330
- return state;
2128
+ const px = value => {
2129
+ return `${value}px`;
2331
2130
  };
2332
2131
 
2333
- const handleClickMinimize = async state => {
2334
- await invoke('ElectronWindow.minimize');
2335
- return state;
2132
+ const getLetterSpacingString = letterSpacing => {
2133
+ return px(letterSpacing);
2134
+ };
2135
+ const measureTextWidth = (text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth) => {
2136
+ string(text);
2137
+ number(fontWeight);
2138
+ number(fontSize);
2139
+ string(fontFamily);
2140
+ boolean(isMonoSpaceFont);
2141
+ number(charWidth);
2142
+ if (typeof letterSpacing !== 'number') {
2143
+ throw new TypeError('letterSpacing must be of type number');
2144
+ }
2145
+ const letterSpacingString = getLetterSpacingString(letterSpacing);
2146
+ const fontString = getFontString(fontWeight, fontSize, fontFamily);
2147
+ const ctx = getContext();
2148
+ // @ts-ignore
2149
+ ctx.letterSpacing = letterSpacingString;
2150
+ ctx.font = fontString;
2151
+ const metrics = ctx.measureText(text);
2152
+ const {
2153
+ width
2154
+ } = metrics;
2155
+ return width;
2336
2156
  };
2337
2157
 
2338
- const handleClickToggleMaximize = async state => {
2339
- await (invoke('ElectronWindow.maximize'));
2340
- return state;
2158
+ const measureTitleBarEntryWidth = (label, fontWeight, fontSize, fontFamily, letterSpacing) => {
2159
+ const isMonospaceFont = false;
2160
+ const charWidth = 0;
2161
+ return measureTextWidth(label, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
2341
2162
  };
2342
2163
 
2343
- const handleClick$1 = (state, className) => {
2344
- if (className.includes('Minimize')) {
2345
- return handleClickMinimize(state);
2346
- }
2347
- if (className.includes('Maximize') || className.includes('Restore')) {
2348
- return handleClickToggleMaximize(state);
2349
- }
2350
- if (className.includes('Close')) {
2351
- return handleClickClose(state);
2164
+ const addWidths = (entries, labelPadding, fontWeight, fontSize, fontFamily, letterSpacing) => {
2165
+ const withWidths = [];
2166
+ for (const entry of entries) {
2167
+ const textWidth = measureTitleBarEntryWidth(entry.label, fontWeight, fontSize, fontFamily, letterSpacing);
2168
+ const width = textWidth + labelPadding * 2;
2169
+ withWidths.push({
2170
+ ...entry,
2171
+ width
2172
+ });
2352
2173
  }
2353
- return state;
2174
+ return withWidths;
2354
2175
  };
2355
2176
 
2356
- const handleContextMenu = state => {
2357
- return state;
2177
+ const loadContent = async (state, titleBarEntries) => {
2178
+ const {
2179
+ labelFontFamily,
2180
+ labelFontSize,
2181
+ labelFontWeight,
2182
+ labelLetterSpacing,
2183
+ labelPadding
2184
+ } = state;
2185
+ const withWidths = addWidths(titleBarEntries, labelPadding, labelFontWeight, labelFontSize, labelFontFamily, labelLetterSpacing);
2186
+ return {
2187
+ ...state,
2188
+ titleBarEntries: withWidths
2189
+ };
2358
2190
  };
2359
2191
 
2360
- const getTitleBarIndexFromPosition = (titleBarEntries, x, y) => {
2361
- let currentX = 0;
2362
- for (let i = 0; i < titleBarEntries.length; i++) {
2363
- const entry = titleBarEntries[i];
2364
- const entryWidth = entry.width;
2365
- if (x >= currentX && x < currentX + entryWidth) {
2366
- return i;
2367
- }
2368
- currentX += entryWidth;
2369
- }
2370
- return -1;
2371
- };
2192
+ const Ellipsis = 'Ellipsis';
2372
2193
 
2373
- const getTotalWidth = entries => {
2194
+ const getVisibleTitleBarEntries = (entries, width, focusedIndex, isMenuOpen) => {
2195
+ array(entries);
2196
+ number(width);
2374
2197
  let total = 0;
2375
- for (const entry of entries) {
2198
+ const visible = [];
2199
+ for (let i = 0; i < entries.length; i++) {
2200
+ const entry = entries[i];
2376
2201
  total += entry.width;
2202
+ if (total >= width) {
2203
+ break;
2204
+ }
2205
+ const isOpen = i === focusedIndex && isMenuOpen;
2206
+ const isFocused = i === focusedIndex;
2207
+ visible.push({
2208
+ ...entry,
2209
+ isOpen,
2210
+ isFocused
2211
+ });
2377
2212
  }
2378
- return total;
2379
- };
2380
-
2381
- // TODO lazyload menuEntries and use Command.execute (maybe)
2382
- const CONTEXT_MENU_ITEM_HEIGHT = 26;
2383
- const CONTEXT_MENU_SEPARATOR_HEIGHT = 11;
2384
- const CONTEXT_MENU_PADDING = 8;
2385
- const getMenuHeight = items => {
2386
- let height = CONTEXT_MENU_PADDING;
2387
- for (const item of items) {
2388
- switch (item.flags) {
2389
- case Separator:
2390
- height += CONTEXT_MENU_SEPARATOR_HEIGHT;
2391
- break;
2392
- default:
2393
- height += CONTEXT_MENU_ITEM_HEIGHT;
2394
- break;
2213
+ const hasOverflow = visible.length < entries.length;
2214
+ if (hasOverflow) {
2215
+ const padding = 8;
2216
+ const moreIconWidth = 22;
2217
+ const totalPadding = padding * 2;
2218
+ const hasStillOverflow = total + moreIconWidth + totalPadding > width;
2219
+ if (hasStillOverflow) {
2220
+ visible.pop();
2395
2221
  }
2222
+ visible.push({
2223
+ ariaLabel: moreDot(),
2224
+ icon: Ellipsis,
2225
+ label: '',
2226
+ width: moreIconWidth + totalPadding
2227
+ });
2396
2228
  }
2397
- return height;
2229
+ return visible;
2398
2230
  };
2399
2231
 
2400
- // TODO lazyload menuEntries and use Command.execute (maybe)
2401
- const MENU_WIDTH = 150;
2402
- const CONTEXT_MENU_WIDTH = 250;
2403
- const getMenuWidth = () => {
2404
- return CONTEXT_MENU_WIDTH;
2232
+ const renderEntries = (oldState, newState) => {
2233
+ const visibleEntries = getVisibleTitleBarEntries(newState.titleBarEntries, newState.width, newState.focusedIndex, newState.isMenuOpen);
2234
+ const dom = getTitleBarMenuBarVirtualDom(visibleEntries, newState.focusedIndex);
2235
+ return ['Viewlet.setDom2', newState.uid, dom];
2405
2236
  };
2406
2237
 
2407
- // TODO difference between focusing with mouse or keyboard
2408
- // with mouse -> open submenu
2409
- // with keyboard -> don't open submenu, only focus
2410
-
2411
- const getIndexToFocusNextStartingAt = (items, startIndex) => {
2412
- for (let i = startIndex; i < startIndex + items.length; i++) {
2413
- const index = i % items.length;
2414
- const item = items[index];
2415
- if (canBeFocused(item)) {
2416
- return index;
2417
- }
2238
+ const renderFocusedIndex = (oldState, newState) => {
2239
+ if (newState.focusedIndex === -1) {
2240
+ return [];
2418
2241
  }
2419
- return -1;
2420
- };
2421
- const getIndexToFocusFirst = items => {
2422
- return getIndexToFocusNextStartingAt(items, 0);
2423
- };
2424
- const getIndexToFocusLast = items => {
2425
- return getIndexToFocusPreviousStartingAt(items, items.length - 1);
2242
+ return ['Viewlet.focusSelector', '.ViewletTitleBarMenuBar'];
2426
2243
  };
2427
2244
 
2428
- // TODO this code seems a bit too complicated, maybe it can be simplified
2429
- const getIndexToFocusPreviousStartingAt = (items, startIndex) => {
2430
- for (let i = startIndex; i > startIndex - items.length; i--) {
2431
- const index = (i + items.length) % items.length;
2432
- const item = items[index];
2433
- if (canBeFocused(item)) {
2434
- return index;
2435
- }
2245
+ const getKeyBindingString = (key, altKey, ctrlKey, shiftKey, metaKey) => {
2246
+ let string = '';
2247
+ if (ctrlKey) {
2248
+ string += 'Ctrl+';
2436
2249
  }
2437
- return -1;
2438
- };
2439
- const getIndexToFocusPrevious = menu => {
2440
- const startIndex = menu.focusedIndex === -1 ? menu.items.length - 1 : menu.focusedIndex - 1;
2441
- return getIndexToFocusPreviousStartingAt(menu.items, startIndex);
2250
+ if (shiftKey) {
2251
+ string += 'Shift+';
2252
+ }
2253
+ string += key.toUpperCase();
2254
+ return string;
2442
2255
  };
2443
- const canBeFocused = item => {
2444
- switch (item.flags) {
2445
- case Separator:
2446
- case Disabled:
2447
- return false;
2256
+
2257
+ const Unknown = 'Unknown';
2258
+ const Backspace = 'Backspace';
2259
+ const Tab = 'Tab';
2260
+ const Enter = 'Enter';
2261
+ const Escape = 'Escape';
2262
+ const Space = 'Space';
2263
+ const PageUp = 'PageUp';
2264
+ const PageDown = 'PageDown';
2265
+ const End = 'End';
2266
+ const Home = 'Home';
2267
+ const LeftArrow = 'LeftArrow';
2268
+ const UpArrow = 'UpArrow';
2269
+ const RightArrow = 'RightArrow';
2270
+ const DownArrow = 'DownArrow';
2271
+ const Insert = 'Insert';
2272
+ const Delete = 'Delete';
2273
+ const Digit0 = '0';
2274
+ const Digit1 = '1';
2275
+ const Digit2 = '2';
2276
+ const Digit3 = '3';
2277
+ const Digit4 = '4';
2278
+ const Digit5 = '5';
2279
+ const Digit6 = '6';
2280
+ const Digit7 = '7';
2281
+ const Digit8 = '8';
2282
+ const Digit9 = '9';
2283
+ const KeyA = 'a';
2284
+ const KeyB = 'b';
2285
+ const KeyC = 'c';
2286
+ const KeyD = 'd';
2287
+ const KeyE = 'e';
2288
+ const KeyF = 'f';
2289
+ const KeyG = 'g';
2290
+ const KeyH = 'h';
2291
+ const KeyI = 'i';
2292
+ const KeyJ = 'j';
2293
+ const KeyK = 'k';
2294
+ const KeyL = 'l';
2295
+ const KeyM = 'm';
2296
+ const KeyN = 'n';
2297
+ const KeyO = 'o';
2298
+ const KeyP = 'p';
2299
+ const KeyQ = 'q';
2300
+ const KeyR = 'r';
2301
+ const KeyS = 's';
2302
+ const KeyT = 't';
2303
+ const KeyU = 'u';
2304
+ const KeyV = 'v';
2305
+ const KeyW = 'w';
2306
+ const KeyX = 'x';
2307
+ const KeyY = 'y';
2308
+ const KeyZ = 'z';
2309
+ const F1 = 'F1';
2310
+ const F2 = 'F2';
2311
+ const F3 = 'F3';
2312
+ const F4 = 'F4';
2313
+ const F5 = 'F5';
2314
+ const F6 = 'F6';
2315
+ const Equal = '=';
2316
+ const Comma = ',';
2317
+ const Minus = 'Minus';
2318
+ const Backquote = 'Backquote';
2319
+ const Backslash = 'Backslash';
2320
+ const Star = '*';
2321
+ const Plus = '+';
2322
+
2323
+ const getKeyCodeString = keyCode => {
2324
+ switch (keyCode) {
2325
+ case Backspace$1:
2326
+ return Backspace;
2327
+ case Tab$1:
2328
+ return Tab;
2329
+ case Escape$1:
2330
+ return Escape;
2331
+ case Enter$1:
2332
+ return Enter;
2333
+ case Space$1:
2334
+ return Space;
2335
+ case PageUp$1:
2336
+ return PageUp;
2337
+ case PageDown$1:
2338
+ return PageDown;
2339
+ case End$1:
2340
+ return End;
2341
+ case Home$1:
2342
+ return Home;
2343
+ case LeftArrow$1:
2344
+ return LeftArrow;
2345
+ case UpArrow$1:
2346
+ return UpArrow;
2347
+ case RightArrow$1:
2348
+ return RightArrow;
2349
+ case DownArrow$1:
2350
+ return DownArrow;
2351
+ case Insert$1:
2352
+ return Insert;
2353
+ case Delete$1:
2354
+ return Delete;
2355
+ case Digit0$1:
2356
+ return Digit0;
2357
+ case Digit1$1:
2358
+ return Digit1;
2359
+ case Digit2$1:
2360
+ return Digit2;
2361
+ case Digit3$1:
2362
+ return Digit3;
2363
+ case Digit4$1:
2364
+ return Digit4;
2365
+ case Digit5$1:
2366
+ return Digit5;
2367
+ case Digit6$1:
2368
+ return Digit6;
2369
+ case Digit7$1:
2370
+ return Digit7;
2371
+ case Digit8$1:
2372
+ return Digit8;
2373
+ case Digit9$1:
2374
+ return Digit9;
2375
+ case KeyA$1:
2376
+ return KeyA;
2377
+ case KeyB$1:
2378
+ return KeyB;
2379
+ case KeyC$1:
2380
+ return KeyC;
2381
+ case KeyD$1:
2382
+ return KeyD;
2383
+ case KeyE$1:
2384
+ return KeyE;
2385
+ case KeyF$1:
2386
+ return KeyF;
2387
+ case KeyG$1:
2388
+ return KeyG;
2389
+ case KeyH$1:
2390
+ return KeyH;
2391
+ case KeyI$1:
2392
+ return KeyI;
2393
+ case KeyJ$1:
2394
+ return KeyJ;
2395
+ case KeyK$1:
2396
+ return KeyK;
2397
+ case KeyL$1:
2398
+ return KeyL;
2399
+ case KeyM$1:
2400
+ return KeyM;
2401
+ case KeyN$1:
2402
+ return KeyN;
2403
+ case KeyO$1:
2404
+ return KeyO;
2405
+ case KeyP$1:
2406
+ return KeyP;
2407
+ case KeyQ$1:
2408
+ return KeyQ;
2409
+ case KeyR$1:
2410
+ return KeyR;
2411
+ case KeyS$1:
2412
+ return KeyS;
2413
+ case KeyT$1:
2414
+ return KeyT;
2415
+ case KeyU$1:
2416
+ return KeyU;
2417
+ case KeyV$1:
2418
+ return KeyV;
2419
+ case KeyW$1:
2420
+ return KeyW;
2421
+ case KeyX$1:
2422
+ return KeyX;
2423
+ case KeyY$1:
2424
+ return KeyY;
2425
+ case KeyZ$1:
2426
+ return KeyZ;
2427
+ case F1$1:
2428
+ return F1;
2429
+ case F2$1:
2430
+ return F2;
2431
+ case F3$1:
2432
+ return F3;
2433
+ case F4$1:
2434
+ return F4;
2435
+ case F5$1:
2436
+ return F5;
2437
+ case F6$1:
2438
+ return F6;
2439
+ case Backslash$1:
2440
+ return Backslash;
2441
+ case Equal$1:
2442
+ return Equal;
2443
+ case Comma$1:
2444
+ return Comma;
2445
+ case Backquote$1:
2446
+ return Backquote;
2447
+ case Plus$1:
2448
+ return Plus;
2449
+ case Star$1:
2450
+ return Star;
2451
+ case Minus$1:
2452
+ return Minus;
2448
2453
  default:
2449
- return true;
2450
- }
2451
- };
2452
- const getIndexToFocusNext = menu => {
2453
- const startIndex = menu.focusedIndex + 1;
2454
- return getIndexToFocusNextStartingAt(menu.items, startIndex);
2455
- };
2456
-
2457
- // TODO handle printable letter and focus item that starts with that letter
2458
-
2459
- // TODO pageup / pagedown keys
2460
-
2461
- // TODO more tests
2462
-
2463
- const menus = [MenuEntriesEdit, MenuEntriesFile, MenuEntriesGo, MenuEntriesHelp, MenuEntriesRun, MenuEntriesSelection, MenuEntriesTerminal, MenuEntriesTitleBar, MenuEntriesView, MenuEntriesOpenRecent];
2464
- const getMenus = () => {
2465
- return menus;
2466
- };
2467
- const getModule = id => {
2468
- for (const module of menus) {
2469
- if (module.id === id) {
2470
- return module;
2471
- }
2472
- }
2473
- return undefined;
2474
- };
2475
- const getMenuEntries = async (id, ...args) => {
2476
- try {
2477
- const module = getModule(id);
2478
- // @ts-ignore
2479
- const inject = module.inject || [];
2480
- // @ts-ignore
2481
- return module.getMenuEntries(...args);
2482
- } catch (error) {
2483
- throw new VError(error, `Failed to load menu entries for id ${id}`);
2454
+ return Unknown;
2484
2455
  }
2485
2456
  };
2486
2457
 
2487
- const openMenuAtIndex = async (state, index, shouldBeFocused) => {
2488
- const {
2489
- titleBarEntries,
2490
- titleBarHeight,
2491
- x
2492
- } = state;
2493
- // TODO race conditions
2494
- // TODO send renderer process
2495
- // 1. open menu, items to show
2496
- // 2. focus menu
2497
- const titleBarEntry = titleBarEntries[index];
2498
- const {
2499
- id
2500
- } = titleBarEntry;
2501
- const items = await getMenuEntries(id);
2502
- const relevantEntries = titleBarEntries.slice(0, index);
2503
- const totalWidths = getTotalWidth(relevantEntries);
2504
- const offset = totalWidths;
2505
- // TODO race condition: another menu might already be open at this point
2458
+ const CtrlCmd = 1 << 11 >>> 0;
2459
+ const Shift = 1 << 10 >>> 0;
2506
2460
 
2507
- const menuX = x + offset;
2508
- const menuY = titleBarHeight;
2509
- const width = getMenuWidth();
2510
- const height = getMenuHeight(items);
2511
- const menuFocusedIndex = shouldBeFocused ? getIndexToFocusNextStartingAt(items, 0) : -1;
2512
- const menu = {
2513
- id,
2514
- items,
2515
- focusedIndex: menuFocusedIndex,
2516
- level: 0,
2517
- x: menuX,
2518
- y: menuY,
2519
- width,
2520
- height
2521
- };
2522
- const menus = [menu];
2461
+ const parseKey = rawKey => {
2462
+ number(rawKey);
2463
+ const isCtrl = Boolean(rawKey & CtrlCmd);
2464
+ const isShift = Boolean(rawKey & Shift);
2465
+ const keyCode = rawKey & 0x00_00_00_ff;
2466
+ const key = getKeyCodeString(keyCode);
2523
2467
  return {
2524
- ...state,
2525
- isMenuOpen: true,
2526
- focusedIndex: index,
2527
- menus
2468
+ key,
2469
+ isCtrl,
2470
+ isShift
2528
2471
  };
2529
2472
  };
2530
2473
 
2531
- const focusIndex = async (state, index) => {
2532
- object(state);
2533
- number(index);
2534
- const {
2535
- isMenuOpen,
2536
- focusedIndex
2537
- } = state;
2538
- if (index === focusedIndex) {
2539
- return state;
2540
- }
2541
- if (isMenuOpen) {
2542
- return openMenuAtIndex(state, index, /* focus */false);
2543
- }
2544
- return {
2545
- ...state,
2546
- focusedIndex: index
2547
- };
2474
+ const separator = {
2475
+ type: Div,
2476
+ className: MenuItemSeparator,
2477
+ role: Separator,
2478
+ childCount: 1
2548
2479
  };
2549
-
2550
- const handleMouseOutMenuClosed = state => {
2551
- return focusIndex(state, -1);
2480
+ const separatorLine = {
2481
+ type: Div,
2482
+ className: MenuItemSeparatorLine,
2483
+ childCount: 0
2552
2484
  };
2553
-
2554
- const handleMouseOutMenuOpen = state => {
2555
- return state;
2485
+ const checkboxUnchecked = {
2486
+ type: Div,
2487
+ className: MenuItem$1,
2488
+ role: MenuItemCheckBox,
2489
+ ariaChecked: false,
2490
+ tabIndex: -1,
2491
+ childCount: 1
2556
2492
  };
2557
-
2558
- const ifElse = (menuOpenFunction, menuClosedFunction) => {
2559
- const ifElseFunction = (state, ...args) => {
2560
- const {
2561
- isMenuOpen
2562
- } = state;
2563
- if (isMenuOpen) {
2564
- return menuOpenFunction(state, ...args);
2565
- }
2566
- return menuClosedFunction(state, ...args);
2567
- };
2568
- return ifElseFunction;
2493
+ const checkboxChecked = {
2494
+ type: Div,
2495
+ className: mergeClassNames(MenuItem$1, MenuItemCheckMark),
2496
+ role: MenuItemCheckBox,
2497
+ ariaChecked: true,
2498
+ tabIndex: -1,
2499
+ childCount: 2
2569
2500
  };
2570
-
2571
- const handleMouseOut = ifElse(handleMouseOutMenuOpen, handleMouseOutMenuClosed);
2572
-
2573
- const handlePointerOut = (state, clientX, clientY) => {
2574
- const index = getTitleBarIndexFromPosition(state.titleBarEntries, clientX - state.x);
2575
- if (index === -1) {
2576
- return state;
2577
- }
2578
- return handleMouseOut(state, index);
2501
+ const disabled = {
2502
+ type: Div,
2503
+ className: MenuItem$1,
2504
+ role: MenuItem,
2505
+ tabIndex: -1,
2506
+ disabled: true,
2507
+ childCount: 1
2579
2508
  };
2580
-
2581
- const handleMouseOverMenuClosed = (state, index) => {
2582
- return focusIndex(state, index);
2509
+ const arrowRight = {
2510
+ type: Div,
2511
+ className: MenuItemSubMenuArrowRight,
2512
+ childCount: 0
2583
2513
  };
2584
-
2585
- const handleMouseOverMenuOpen = async (state, index) => {
2586
- if (index === -1) {
2587
- return state;
2588
- }
2589
- return focusIndex(state, index);
2514
+ const getMenuItemSeparatorDom = menuItem => {
2515
+ return [separator, separatorLine];
2590
2516
  };
2591
-
2592
- const handleMouseOver = ifElse(handleMouseOverMenuOpen, handleMouseOverMenuClosed);
2593
-
2594
- const handlePointerOver = (state, clientX, clientY) => {
2595
- const index = getTitleBarIndexFromPosition(state.titleBarEntries, clientX - state.x);
2596
- if (index === -1) {
2597
- return state;
2598
- }
2599
- return handleMouseOver(state, index);
2517
+ const getMenuItemCheckedDom = menuItem => {
2518
+ const {
2519
+ label
2520
+ } = menuItem;
2521
+ return [checkboxChecked, {
2522
+ type: Div,
2523
+ className: mergeClassNames(MenuItemCheckmarkIcon, MaskIconCheck)
2524
+ }, text(label)];
2600
2525
  };
2601
-
2602
- const getFontString = (fontWeight, fontSize, fontFamily) => {
2603
- return `${fontWeight} ${fontSize}px ${fontFamily}`;
2526
+ const getMenuItemUncheckedDom = menuItem => {
2527
+ const {
2528
+ label
2529
+ } = menuItem;
2530
+ return [checkboxUnchecked, text(label)];
2604
2531
  };
2605
-
2606
- const state = {
2607
- ctx: undefined
2532
+ const getMenuItemDisabledDom = menuItem => {
2533
+ const {
2534
+ label
2535
+ } = menuItem;
2536
+ return [disabled, text(label)];
2608
2537
  };
2609
- const getOrCreate = createCtx => {
2610
- if (state.ctx) {
2611
- return state.ctx;
2538
+ const getMenuItemDefaultDom = menuItem => {
2539
+ const {
2540
+ label,
2541
+ isFocused,
2542
+ key
2543
+ } = menuItem;
2544
+ let className = MenuItem$1;
2545
+ if (isFocused) {
2546
+ className += ' ' + MenuItemFocused;
2612
2547
  }
2613
- state.ctx = createCtx();
2614
- return state.ctx;
2548
+ const dom = [];
2549
+ dom.push({
2550
+ type: Div,
2551
+ className,
2552
+ role: MenuItem,
2553
+ tabIndex: -1,
2554
+ childCount: 1
2555
+ }, text(label));
2556
+ if (key) {
2557
+ dom[0].childCount++;
2558
+ const parsedKey = parseKey(key);
2559
+ const keyBindingsString = getKeyBindingString(parsedKey.key, false, parsedKey.isCtrl, parsedKey.isShift);
2560
+ dom.push({
2561
+ type: Span,
2562
+ className: MenuItemKeyBinding,
2563
+ childCount: 1
2564
+ }, text(keyBindingsString));
2565
+ }
2566
+ return dom;
2615
2567
  };
2616
-
2617
- const createCtx = () => {
2618
- const canvas = new OffscreenCanvas(0, 0);
2619
- const ctx = /** @type {OffscreenCanvasRenderingContext2D} */canvas.getContext('2d');
2620
- if (!ctx) {
2621
- throw new Error('Failed to get canvas context 2d');
2568
+ const getMenuItemSubMenuDom = menuItem => {
2569
+ const {
2570
+ label,
2571
+ isFocused,
2572
+ isExpanded,
2573
+ level
2574
+ } = menuItem;
2575
+ let className = MenuItem$1;
2576
+ className += ' ' + MenuItemSubMenu;
2577
+ if (isFocused) {
2578
+ className += ' ' + MenuItemFocused;
2622
2579
  }
2623
- return ctx;
2580
+ return [{
2581
+ type: Div,
2582
+ className,
2583
+ role: MenuItem,
2584
+ tabIndex: -1,
2585
+ ariaHasPopup: true,
2586
+ ariaExpanded: isExpanded,
2587
+ ariaOwns: isExpanded ? `Menu-${level + 1}` : undefined,
2588
+ childCount: 2
2589
+ }, text(label), arrowRight];
2624
2590
  };
2625
- const getContext = () => {
2626
- const ctx = getOrCreate(createCtx);
2627
- return ctx;
2591
+ const getMenuItemVirtualDom = menuItem => {
2592
+ const {
2593
+ flags
2594
+ } = menuItem;
2595
+ switch (flags) {
2596
+ case None$1:
2597
+ case RestoreFocus:
2598
+ case Ignore:
2599
+ return getMenuItemDefaultDom(menuItem);
2600
+ case Separator$1:
2601
+ return getMenuItemSeparatorDom();
2602
+ case Checked:
2603
+ return getMenuItemCheckedDom(menuItem);
2604
+ case Unchecked:
2605
+ return getMenuItemUncheckedDom(menuItem);
2606
+ case Disabled:
2607
+ return getMenuItemDisabledDom(menuItem);
2608
+ case SubMenu:
2609
+ return getMenuItemSubMenuDom(menuItem);
2610
+ default:
2611
+ return [];
2612
+ }
2628
2613
  };
2629
-
2630
- const px = value => {
2631
- return `${value}px`;
2614
+ const getMenuVirtualDom = menuItems => {
2615
+ const dom = [];
2616
+ dom.push({
2617
+ type: Div,
2618
+ className: Menu$1,
2619
+ role: Menu,
2620
+ tabIndex: -1,
2621
+ childCount: menuItems.length
2622
+ });
2623
+ dom.push(...menuItems.flatMap(getMenuItemVirtualDom));
2624
+ return dom;
2632
2625
  };
2633
2626
 
2634
- const getLetterSpacingString = letterSpacing => {
2635
- return px(letterSpacing);
2636
- };
2637
- const measureTextWidth = (text, fontWeight, fontSize, fontFamily, letterSpacing, isMonoSpaceFont, charWidth) => {
2638
- string(text);
2639
- number(fontWeight);
2640
- number(fontSize);
2641
- string(fontFamily);
2642
- boolean(isMonoSpaceFont);
2643
- number(charWidth);
2644
- if (typeof letterSpacing !== 'number') {
2645
- throw new TypeError('letterSpacing must be of type number');
2646
- }
2647
- const letterSpacingString = getLetterSpacingString(letterSpacing);
2648
- const fontString = getFontString(fontWeight, fontSize, fontFamily);
2649
- const ctx = getContext();
2650
- // @ts-ignore
2651
- ctx.letterSpacing = letterSpacingString;
2652
- ctx.font = fontString;
2653
- const metrics = ctx.measureText(text);
2627
+ const getVisible = (items, focusedIndex, expanded, level) => {
2628
+ const visibleItems = [];
2654
2629
  const {
2655
- width
2656
- } = metrics;
2657
- return width;
2630
+ length
2631
+ } = items;
2632
+ for (let i = 0; i < length; i++) {
2633
+ const item = items[i];
2634
+ const {
2635
+ flags,
2636
+ label
2637
+ } = item;
2638
+ visibleItems.push({
2639
+ label,
2640
+ flags,
2641
+ isFocused: i === focusedIndex,
2642
+ isExpanded: i === focusedIndex && expanded,
2643
+ level,
2644
+ key: item.key
2645
+ });
2646
+ }
2647
+ return visibleItems;
2658
2648
  };
2659
2649
 
2660
- const measureTitleBarEntryWidth = (label, fontWeight, fontSize, fontFamily, letterSpacing) => {
2661
- const isMonospaceFont = false;
2662
- const charWidth = 0;
2663
- return measureTextWidth(label, fontWeight, fontSize, fontFamily, letterSpacing, isMonospaceFont, charWidth);
2650
+ const SetMenus = 'setMenus';
2651
+
2652
+ const renderMEnus = (oldState, newState) => {
2653
+ const oldMenus = oldState.menus;
2654
+ const newMenus = newState.menus;
2655
+ const oldLength = oldMenus.length;
2656
+ const newLength = newMenus.length;
2657
+ const commonLength = Math.min(oldLength, newLength);
2658
+ const changes = [];
2659
+ for (let i = 0; i < commonLength; i++) {
2660
+ const oldMenu = oldMenus[i];
2661
+ const newMenu = newMenus[i];
2662
+ if (oldMenu !== newMenu) {
2663
+ const visible = getVisible(newMenu.items, newMenu.focusedIndex, newMenu.expanded, newMenu.level);
2664
+ const dom = getMenuVirtualDom(visible).slice(1);
2665
+ changes.push([/* method */'updateMenu', newMenu, /* newLength */newLength, dom]);
2666
+ }
2667
+ }
2668
+ const difference = newLength - oldLength;
2669
+ if (difference > 0) {
2670
+ const newMenu = newMenus.at(-1);
2671
+ const visible = getVisible(newMenu.items, newMenu.focusedIndex, newMenu.expanded, newMenu.level);
2672
+ const dom = getMenuVirtualDom(visible).slice(1);
2673
+ changes.push(['addMenu', newMenu, dom]);
2674
+ } else if (difference < 0) {
2675
+ changes.push(['closeMenus', newLength]);
2676
+ }
2677
+ return ['Viewlet.send', newState.uid, /* method */SetMenus, /* changes */changes, newState.uid];
2664
2678
  };
2665
2679
 
2666
- const addWidths = (entries, labelPadding, fontWeight, fontSize, fontFamily, letterSpacing) => {
2667
- const withWidths = [];
2668
- for (const entry of entries) {
2669
- const textWidth = measureTitleBarEntryWidth(entry.label, fontWeight, fontSize, fontFamily, letterSpacing);
2670
- const width = textWidth + labelPadding * 2;
2671
- withWidths.push({
2672
- ...entry,
2673
- width
2674
- });
2680
+ const getRenderer = diffType => {
2681
+ switch (diffType) {
2682
+ case RenderEntries:
2683
+ return renderEntries;
2684
+ case RenderFocusedIndex:
2685
+ return renderFocusedIndex;
2686
+ case RenderMenus:
2687
+ return renderMEnus;
2688
+ default:
2689
+ throw new Error('unknown renderer');
2675
2690
  }
2676
- return withWidths;
2677
2691
  };
2678
2692
 
2679
- const loadContent = async (state, titleBarEntries) => {
2680
- const {
2681
- labelFontFamily,
2682
- labelFontSize,
2683
- labelFontWeight,
2684
- labelLetterSpacing,
2685
- labelPadding
2686
- } = state;
2687
- const withWidths = addWidths(titleBarEntries, labelPadding, labelFontWeight, labelFontSize, labelFontFamily, labelLetterSpacing);
2688
- return {
2689
- ...state,
2690
- titleBarEntries: withWidths
2691
- };
2693
+ const applyRender = (oldState, newState, diffResult) => {
2694
+ const commands = [];
2695
+ for (const item of diffResult) {
2696
+ const fn = getRenderer(item);
2697
+ commands.push(fn(oldState, newState));
2698
+ }
2699
+ return commands;
2692
2700
  };
2693
2701
 
2694
2702
  const render2 = async (uid, diffResult) => {
@@ -3138,7 +3146,7 @@ const handleMenuClick = (state, level, index) => {
3138
3146
  const menu = menus[level];
3139
3147
  const item = menu.items[index];
3140
3148
  switch (item.flags) {
3141
- case None:
3149
+ case None$1:
3142
3150
  return selectIndexNone(state, item);
3143
3151
  case SubMenu:
3144
3152
  return selectIndexSubMenu(state, menu, index);
@@ -3242,7 +3250,11 @@ const wrapCommand = fn => {
3242
3250
  newState
3243
3251
  } = get(uid);
3244
3252
  const newerState = await fn(newState, ...args);
3245
- set(uid, newState, newerState);
3253
+ if (newState === newerState) {
3254
+ return;
3255
+ }
3256
+ const latest = get(uid);
3257
+ set(uid, latest.oldState, newerState);
3246
3258
  };
3247
3259
  return wrapped;
3248
3260
  };
@@ -3292,10 +3304,7 @@ const commandMap = {
3292
3304
  'TitleBarMenuBar.saveState': saveState,
3293
3305
  'TitleBarMenuBar.terminate': terminate,
3294
3306
  'TitleBarMenuBar.toggleIndex': wrapCommand(toggleIndex),
3295
- 'TitleBarMenuBar.toggleMenu': wrapCommand(toggleMenu),
3296
- // deprecated
3297
- 'TitleBarMenuBar.diff': diff,
3298
- 'TitleBarMenuBar.render': doRender
3307
+ 'TitleBarMenuBar.toggleMenu': wrapCommand(toggleMenu)
3299
3308
  };
3300
3309
 
3301
3310
  const listen = async () => {