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