@lvce-editor/main-area-worker 1.2.0 → 1.4.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.
@@ -90,12 +90,24 @@ const getType = value => {
90
90
  return Unknown;
91
91
  }
92
92
  };
93
+ const object = value => {
94
+ const type = getType(value);
95
+ if (type !== Object$1) {
96
+ throw new AssertionError('expected value to be of type object');
97
+ }
98
+ };
93
99
  const number = value => {
94
100
  const type = getType(value);
95
101
  if (type !== Number$1) {
96
102
  throw new AssertionError('expected value to be of type number');
97
103
  }
98
104
  };
105
+ const string = value => {
106
+ const type = getType(value);
107
+ if (type !== String) {
108
+ throw new AssertionError('expected value to be of type string');
109
+ }
110
+ };
99
111
 
100
112
  const isMessagePort = value => {
101
113
  return value && value instanceof MessagePort;
@@ -450,7 +462,7 @@ const getFirstEvent = (eventEmitter, eventMap) => {
450
462
  return promise;
451
463
  };
452
464
  const Message$1 = 3;
453
- const create$5 = async ({
465
+ const create$5$1 = async ({
454
466
  isMessagePortOpen,
455
467
  messagePort
456
468
  }) => {
@@ -501,7 +513,7 @@ const wrap$5 = messagePort => {
501
513
  };
502
514
  const IpcParentWithMessagePort$1 = {
503
515
  __proto__: null,
504
- create: create$5,
516
+ create: create$5$1,
505
517
  signal: signal$1,
506
518
  wrap: wrap$5
507
519
  };
@@ -720,7 +732,7 @@ const getErrorResponse = (id, error, preparePrettyError, logError) => {
720
732
  const errorProperty = getErrorProperty(error, prettyError);
721
733
  return create$1$2(id, errorProperty);
722
734
  };
723
- const create$3 = (message, result) => {
735
+ const create$5 = (message, result) => {
724
736
  return {
725
737
  jsonrpc: Two$1,
726
738
  id: message.id,
@@ -729,7 +741,7 @@ const create$3 = (message, result) => {
729
741
  };
730
742
  const getSuccessResponse = (message, result) => {
731
743
  const resultProperty = result ?? null;
732
- return create$3(message, resultProperty);
744
+ return create$5(message, resultProperty);
733
745
  };
734
746
  const getErrorResponseSimple = (id, error) => {
735
747
  return {
@@ -1031,6 +1043,7 @@ const createMockRpc = ({
1031
1043
  const Button = 1;
1032
1044
  const Div = 4;
1033
1045
  const Span = 8;
1046
+ const Text = 12;
1034
1047
  const Pre = 51;
1035
1048
 
1036
1049
  const TargetName = 'event.target.name';
@@ -1052,7 +1065,7 @@ const remove = id => {
1052
1065
  };
1053
1066
 
1054
1067
  /* eslint-disable @typescript-eslint/explicit-function-return-type */
1055
- const create$2 = rpcId => {
1068
+ const create$3 = rpcId => {
1056
1069
  return {
1057
1070
  async dispose() {
1058
1071
  const rpc = get$1(rpcId);
@@ -1090,12 +1103,12 @@ const create$2 = rpcId => {
1090
1103
 
1091
1104
  const {
1092
1105
  set: set$2
1093
- } = create$2(ExtensionHostWorker);
1106
+ } = create$3(ExtensionHostWorker);
1094
1107
 
1095
1108
  const {
1096
1109
  invokeAndTransfer,
1097
1110
  set: set$1
1098
- } = create$2(RendererWorker);
1111
+ } = create$3(RendererWorker);
1099
1112
  const sendMessagePortToExtensionHostWorker$1 = async (port, rpcId = 0) => {
1100
1113
  const command = 'HandleMessagePort.handleMessagePort2';
1101
1114
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToExtensionHostWorker', port, command, rpcId);
@@ -1105,7 +1118,7 @@ const toCommandId = key => {
1105
1118
  const dotIndex = key.indexOf('.');
1106
1119
  return key.slice(dotIndex + 1);
1107
1120
  };
1108
- const create$1 = () => {
1121
+ const create$2 = () => {
1109
1122
  const states = Object.create(null);
1110
1123
  const commandMapRef = {};
1111
1124
  return {
@@ -1191,9 +1204,9 @@ const {
1191
1204
  getCommandIds,
1192
1205
  registerCommands,
1193
1206
  set,
1194
- wrapCommand} = create$1();
1207
+ wrapCommand} = create$2();
1195
1208
 
1196
- const create = (uid, uri, x, y, width, height, platform, assetDir) => {
1209
+ const create$1 = (uid, uri, x, y, width, height, platform, assetDir) => {
1197
1210
  const state = {
1198
1211
  assetDir,
1199
1212
  platform,
@@ -1263,13 +1276,381 @@ const initialize = async () => {
1263
1276
  };
1264
1277
 
1265
1278
  const loadContent = async state => {
1279
+ const tabs = [{
1280
+ content: '',
1281
+ editorType: 'text',
1282
+ id: '1',
1283
+ isDirty: false,
1284
+ title: 'tab 1'
1285
+ }, {
1286
+ content: '',
1287
+ editorType: 'text',
1288
+ id: '2',
1289
+ isDirty: false,
1290
+ title: 'tab 2'
1291
+ }];
1292
+ return {
1293
+ ...state,
1294
+ layout: {
1295
+ activeGroupId: '0',
1296
+ direction: 'horizontal',
1297
+ groups: [{
1298
+ activeTabId: '',
1299
+ direction: 'horizontal',
1300
+ focused: false,
1301
+ id: '0',
1302
+ size: 300,
1303
+ tabs
1304
+ }]
1305
+ }
1306
+ };
1307
+ };
1308
+
1309
+ let idCounter = 0;
1310
+ const create = () => {
1311
+ idCounter++;
1312
+ return idCounter;
1313
+ };
1314
+
1315
+ const getTitle = (uri, homeDir) => {
1316
+ if (!uri) {
1317
+ return '';
1318
+ }
1319
+ return uri;
1320
+ };
1321
+ const getLabel = uri => {
1322
+ if (uri.startsWith('settings://')) {
1323
+ return 'Settings';
1324
+ }
1325
+ if (uri.startsWith('simple-browser://')) {
1326
+ return 'Simple Browser';
1327
+ }
1328
+ return uri;
1329
+ // return Workspace.pathBaseName(uri)
1330
+ };
1331
+
1332
+ /**
1333
+ *
1334
+ * @param {string} uri
1335
+ * @returns
1336
+ */
1337
+ const getFileIcon = uri => {
1338
+ if (uri === 'app://keybindings') {
1339
+ return `MaskIconRecordKey`;
1340
+ }
1341
+ if (uri.startsWith('extension-detail://')) {
1342
+ return `MaskIconExtensions`;
1343
+ }
1344
+ return '';
1345
+ };
1346
+
1347
+ const Preview = 1 << 4;
1348
+
1349
+ // @ts-nocheck
1350
+
1351
+ const focusIndex = async (state, index) => {
1352
+ const {
1353
+ activeGroupIndex,
1354
+ groups,
1355
+ tabHeight,
1356
+ uid
1357
+ } = state;
1358
+ const group = groups[activeGroupIndex];
1359
+ const {
1360
+ editors
1361
+ } = group;
1362
+ const oldActiveIndex = group.activeIndex;
1363
+ if (index === oldActiveIndex) {
1364
+ return {
1365
+ commands: [],
1366
+ newState: state
1367
+ };
1368
+ }
1369
+ const newGroup = {
1370
+ ...group,
1371
+ activeIndex: index
1372
+ };
1373
+ const newGroups = [...groups.slice(0, activeGroupIndex), newGroup, ...groups.slice(activeGroupIndex + 1)];
1374
+ const newState = {
1375
+ ...state,
1376
+ groups: newGroups
1377
+ };
1378
+ const editor = editors[index];
1379
+ const {
1380
+ x
1381
+ } = group;
1382
+ const y = group.y + tabHeight;
1383
+ const {
1384
+ width
1385
+ } = group;
1386
+ const contentHeight = group.height - tabHeight;
1387
+ const id = await ViewletMap.getModuleId(editor.uri);
1388
+ const oldEditor = editors[oldActiveIndex];
1389
+ const oldId = await ViewletMap.getModuleId(oldEditor.uri);
1390
+ // @ts-ignore
1391
+ ViewletStates.getInstance(oldId);
1392
+ const previousUid = oldEditor.uid;
1393
+ number(previousUid);
1394
+ const disposeCommands = Viewlet.disposeFunctional(previousUid);
1395
+ const maybeHiddenEditorInstance = ViewletStates.getInstance(editor.uid);
1396
+ if (maybeHiddenEditorInstance) {
1397
+ const commands = Viewlet.showFunctional(editor.uid);
1398
+ const allCommands = [...disposeCommands, ...commands];
1399
+ return {
1400
+ commands: allCommands,
1401
+ newState
1402
+ };
1403
+ }
1404
+ const instanceUid = create();
1405
+ const instance = ViewletManager.create(ViewletModule.load, id, uid, editor.uri, x, y, width, contentHeight);
1406
+ instance.show = false;
1407
+ instance.setBounds = false;
1408
+ instance.uid = instanceUid;
1409
+ editor.uid = instanceUid;
1410
+ const resizeCommands = ['Viewlet.setBounds', instanceUid, x, tabHeight, width, contentHeight];
1411
+
1412
+ // @ts-ignore
1413
+ const commands = await ViewletManager.load(instance);
1414
+ // @ts-ignore
1415
+ commands.unshift(...disposeCommands);
1416
+ // @ts-ignore
1417
+ commands.push(resizeCommands);
1418
+ // @ts-ignore
1419
+ commands.push(['Viewlet.append', uid, instanceUid]);
1420
+ return {
1421
+ commands,
1422
+ newState
1423
+ };
1424
+ };
1425
+
1426
+ // @ts-nocheck
1427
+ const openUri = async (state, uri, focus = true, {
1428
+ preview = false,
1429
+ ...context
1430
+ } = {}) => {
1431
+ object(state);
1432
+ string(uri);
1433
+ const {
1434
+ activeGroupIndex,
1435
+ groups,
1436
+ tabFontFamily,
1437
+ tabFontSize,
1438
+ tabFontWeight,
1439
+ tabHeight,
1440
+ tabLetterSpacing
1441
+ } = state;
1442
+ const {
1443
+ x
1444
+ } = state;
1445
+ const y = state.y + tabHeight;
1446
+ const {
1447
+ width
1448
+ } = state;
1449
+ const contentHeight = state.height - tabHeight;
1450
+ // @ts-ignore
1451
+ const moduleId = await ViewletMap.getModuleId(uri, context.opener);
1452
+ let activeGroup = groups[activeGroupIndex];
1453
+ activeGroup ||= {
1454
+ activeIndex: -1,
1455
+ editors: [],
1456
+ focusedIndex: -1,
1457
+ height: state.height,
1458
+ tabsUid: create(),
1459
+ uid: create(),
1460
+ width,
1461
+ x,
1462
+ y: 0
1463
+ };
1464
+ const {
1465
+ activeIndex,
1466
+ editors
1467
+ } = activeGroup;
1468
+ const previousEditor = editors[activeIndex];
1469
+ let disposeCommands;
1470
+ // @ts-ignore
1471
+ if (previousEditor && previousEditor.uri === uri && previousEditor.opener === context.opener) {
1472
+ return {
1473
+ commands: [],
1474
+ newState: state
1475
+ };
1476
+ }
1477
+ for (let i = 0; i < editors.length; i++) {
1478
+ const editor = editors[i];
1479
+ if (editor.uri === uri &&
1480
+ // @ts-ignore
1481
+ editor.opener === context.opener) {
1482
+ return focusIndex(state, i);
1483
+ }
1484
+ }
1485
+ // TODO editor needs to be disposed when closing
1486
+ // other tabs and closing all tabs
1487
+ if (previousEditor) {
1488
+ const previousUid = previousEditor.uid;
1489
+ disposeCommands = Viewlet.hideFunctional(previousUid);
1490
+ }
1491
+ const instanceUid = create();
1492
+ const instance = ViewletManager.create(ViewletModule.load, moduleId, state.uid, uri, activeGroup.x, y, activeGroup.width, contentHeight);
1493
+ instance.uid = instanceUid;
1494
+ // const oldActiveIndex = state.activeIndex
1495
+ const tabLabel = getLabel(uri);
1496
+ const tabWidth = MeasureTabWidth.measureTabWidth(tabLabel, tabFontWeight, tabFontSize, tabFontFamily, tabLetterSpacing);
1497
+ const tabTitle = getTitle(uri);
1498
+ const icon = getFileIcon(uri);
1499
+ const newEditor = {
1500
+ flags: Preview,
1501
+ icon,
1502
+ label: tabLabel,
1503
+ moduleId,
1504
+ tabWidth,
1505
+ title: tabTitle,
1506
+ uid: instanceUid,
1507
+ uri
1508
+ };
1509
+ const newEditors = [...activeGroup.editors, newEditor];
1510
+ const newActiveIndex = newEditors.length - 1;
1511
+ const newGroup = {
1512
+ ...activeGroup,
1513
+ activeIndex: newActiveIndex,
1514
+ editors: newEditors
1515
+ };
1516
+ const newGroups = [...groups.slice(0, activeGroupIndex), newGroup, ...groups.slice(activeGroupIndex + 1)];
1517
+ // @ts-ignore
1518
+ instance.show = false;
1519
+ instance.setBounds = false;
1520
+ ViewletStates.setState(state.uid, {
1521
+ ...state,
1522
+ activeGroupIndex: 0,
1523
+ groups: newGroups,
1524
+ pendingUid: instanceUid
1525
+ });
1526
+ if (context) {
1527
+ instance.args = [context];
1528
+ }
1529
+ // @ts-ignore
1530
+ const commands = await ViewletManager.load(instance, focus);
1531
+ commands.push(['Viewlet.setBounds', instanceUid, activeGroup.x, tabHeight, activeGroup.width, contentHeight]);
1532
+ let {
1533
+ tabsUid
1534
+ } = state;
1535
+ if (tabsUid === -1) {
1536
+ tabsUid = create();
1537
+ }
1538
+ if (disposeCommands) {
1539
+ commands.push(...disposeCommands);
1540
+ }
1541
+ commands.push(['Viewlet.append', state.uid, instanceUid]);
1542
+ if (focus) {
1543
+ commands.push(['Viewlet.focus', instanceUid]);
1544
+ }
1545
+ const latestState = ViewletStates.getState(state.uid);
1546
+ const latestPendingUid = latestState.pendingUid;
1547
+ if (latestPendingUid !== instanceUid) {
1548
+ return {
1549
+ commands: [],
1550
+ newState: state
1551
+ };
1552
+ }
1553
+ if (!ViewletStates.hasInstance(instanceUid)) {
1554
+ return {
1555
+ commands,
1556
+ newState: state
1557
+ };
1558
+ }
1559
+ return {
1560
+ commands,
1561
+ newState: {
1562
+ ...state,
1563
+ groups: newGroups,
1564
+ tabsUid
1565
+ }
1566
+ };
1567
+ };
1568
+
1569
+ const text = data => {
1266
1570
  return {
1267
- ...state
1571
+ childCount: 0,
1572
+ text: data,
1573
+ type: Text
1268
1574
  };
1269
1575
  };
1270
1576
 
1577
+ const CSS_CLASSES = {
1578
+ EDITOR_GROUPS_CONTAINER: 'editor-groups-container'};
1579
+
1580
+ const renderTab = (tab, isActive) => {
1581
+ return [{
1582
+ childCount: 2,
1583
+ className: 'MainTab',
1584
+ type: Div
1585
+ }, {
1586
+ childCount: 1,
1587
+ className: 'TabTitle',
1588
+ type: Span
1589
+ }, text(tab.isDirty ? `*${tab.title}` : tab.title), {
1590
+ childCount: 1,
1591
+ className: 'TabCloseButton',
1592
+ type: Button
1593
+ }, text('×')];
1594
+ };
1595
+ const renderTabBar = group => {
1596
+ return [{
1597
+ childCount: group.tabs.length,
1598
+ className: 'MainTabs',
1599
+ type: Div
1600
+ }, ...group.tabs.flatMap(tab => renderTab(tab))];
1601
+ };
1602
+ const renderEditor = tab => {
1603
+ if (!tab) {
1604
+ return [text('Tab not found')];
1605
+ }
1606
+ if (tab.editorType === 'custom') {
1607
+ return [{
1608
+ childCount: 1,
1609
+ className: 'CustomEditor',
1610
+ type: Div
1611
+ }, text(`Custom Editor: ${tab.customEditorId}`)];
1612
+ }
1613
+ return [{
1614
+ childCount: 1,
1615
+ className: 'TextEditor',
1616
+ type: Div
1617
+ }, {
1618
+ childCount: 1,
1619
+ className: 'EditorContent',
1620
+ type: Pre
1621
+ }, text(tab.content || '')];
1622
+ };
1623
+ const renderEditorGroup = group => {
1624
+ const activeTab = group.tabs.find(tab => tab.id === group.activeTabId);
1625
+ return [{
1626
+ childCount: 2,
1627
+ className: 'EditorGroup',
1628
+ type: Div
1629
+ }, ...renderTabBar(group), {
1630
+ childCount: activeTab ? 1 : 1,
1631
+ className: 'EditorContainer',
1632
+ type: Div
1633
+ }, ...renderEditor(activeTab)];
1634
+ };
1635
+ const getMainAreaVirtualDom = layout => {
1636
+ return [{
1637
+ childCount: 1,
1638
+ className: 'Main',
1639
+ type: Div
1640
+ }, {
1641
+ childCount: layout.groups.length,
1642
+ className: CSS_CLASSES.EDITOR_GROUPS_CONTAINER,
1643
+ type: Div
1644
+ }, ...layout.groups.flatMap(renderEditorGroup)];
1645
+ };
1646
+
1271
1647
  const renderItems = (oldState, newState) => {
1272
- return [SetDom2, newState.uid, []];
1648
+ const {
1649
+ layout,
1650
+ uid
1651
+ } = newState;
1652
+ const dom = getMainAreaVirtualDom(layout);
1653
+ return [SetDom2, uid, dom];
1273
1654
  };
1274
1655
 
1275
1656
  const getRenderer = diffType => {
@@ -1336,12 +1717,13 @@ const saveState = uid => {
1336
1717
  };
1337
1718
 
1338
1719
  const commandMap = {
1339
- 'MainArea.create': create,
1720
+ 'MainArea.create': create$1,
1340
1721
  'MainArea.diff2': diff2,
1341
1722
  'MainArea.getCommandIds': getCommandIds,
1342
1723
  'MainArea.handleClick': wrapCommand(handleClick),
1343
1724
  'MainArea.initialize': initialize,
1344
1725
  'MainArea.loadContent': wrapCommand(loadContent),
1726
+ 'MainArea.openUri': wrapCommand(openUri),
1345
1727
  'MainArea.render2': render2,
1346
1728
  'MainArea.renderEventListeners': renderEventListeners,
1347
1729
  'MainArea.resize': wrapCommand(resize),
@@ -1361,560 +1743,10 @@ const main$2 = async () => {
1361
1743
  await listen();
1362
1744
  };
1363
1745
 
1364
- const closeEditorGroup = (state, groupId) => {
1365
- const groupIndex = state.layout.groups.findIndex(group => group.id === groupId);
1366
- if (groupIndex === -1 || state.layout.groups.length <= 1) {
1367
- return state;
1368
- }
1369
- const remainingGroups = state.layout.groups.filter(group => group.id !== groupId);
1370
- const redistributedGroups = remainingGroups.map((group, index) => ({
1371
- ...group,
1372
- size: Math.round(100 / remainingGroups.length)
1373
- }));
1374
- const newActiveGroupId = state.layout.activeGroupId === groupId ? remainingGroups[0].id : state.layout.activeGroupId;
1375
- return {
1376
- ...state,
1377
- layout: {
1378
- ...state.layout,
1379
- activeGroupId: newActiveGroupId,
1380
- groups: redistributedGroups
1381
- }
1382
- };
1383
- };
1384
-
1385
- const closeTab = (state, groupId, tabId) => {
1386
- const groups = state.layout.groups.map(group => {
1387
- if (group.id === groupId) {
1388
- const newTabs = group.tabs.filter(tab => tab.id !== tabId);
1389
- let newActiveTabId = group.activeTabId;
1390
- if (group.activeTabId === tabId) {
1391
- const tabIndex = group.tabs.findIndex(tab => tab.id === tabId);
1392
- if (newTabs.length > 0) {
1393
- newActiveTabId = newTabs[Math.min(tabIndex, newTabs.length - 1)].id;
1394
- } else {
1395
- newActiveTabId = undefined;
1396
- }
1397
- }
1398
- return {
1399
- ...group,
1400
- activeTabId: newActiveTabId,
1401
- tabs: newTabs
1402
- };
1403
- }
1404
- return group;
1405
- });
1406
- return {
1407
- ...state,
1408
- layout: {
1409
- ...state.layout,
1410
- groups
1411
- }
1412
- };
1413
- };
1414
-
1415
- const focusEditorGroup = (state, groupId) => {
1416
- const groups = state.layout.groups.map(group => ({
1417
- ...group,
1418
- focused: group.id === groupId
1419
- }));
1420
- return {
1421
- ...state,
1422
- layout: {
1423
- ...state.layout,
1424
- activeGroupId: groupId,
1425
- groups
1426
- }
1427
- };
1428
- };
1429
-
1430
- const CSS_CLASSES = {
1431
- CUSTOM_EDITOR: 'custom-editor',
1432
- EDITOR_CONTAINER: 'editor-container',
1433
- EDITOR_CONTENT: 'editor-content',
1434
- EDITOR_GROUP: 'editor-group',
1435
- EDITOR_GROUP_FOCUSED: 'focused',
1436
- EDITOR_GROUPS_CONTAINER: 'editor-groups-container',
1437
- EMPTY_EDITOR: 'empty-editor',
1438
- MAIN_AREA: 'main-area',
1439
- TAB: 'tab',
1440
- TAB_ACTIVE: 'active',
1441
- TAB_BAR: 'tab-bar',
1442
- TAB_CLOSE: 'tab-close',
1443
- TAB_TITLE: 'tab-title',
1444
- TEXT_EDITOR: 'text-editor'
1445
- };
1446
- const CSS_ATTRIBUTES = {
1447
- DATA_ACTION: 'data-action',
1448
- DATA_CUSTOM_EDITOR_ID: 'data-custom-editor-id',
1449
- DATA_DIRECTION: 'data-direction',
1450
- DATA_GROUP_ID: 'data-group-id',
1451
- DATA_LANGUAGE: 'data-language',
1452
- DATA_TAB_ID: 'data-tab-id'
1453
- };
1454
- const CSS_STYLES = {
1455
- CUSTOM_EDITOR_STYLE: 'flex: 1; overflow: auto;',
1456
- EDITOR_GROUP_BASE: 'display: flex; flex-direction: column; border-right: 1px solid var(--border-color);',
1457
- EDITOR_GROUP_FOCUSED_STYLE: 'box-shadow: 0 0 0 1px var(--focus-border-color);',
1458
- EMPTY_EDITOR_STYLE: 'flex: 1; display: flex; align-items: center; justify-content: center; color: var(--dimmed-color);',
1459
- FLEX_COLUMN: 'display: flex; flex-direction: column; height: 100%;',
1460
- FLEX_ROW: 'display: flex; flex-direction: row; height: 100%;',
1461
- TAB_ACTIVE_STYLE: 'background: var(--tab-active-background); color: var(--tab-active-color);',
1462
- TAB_BAR_BASE: 'display: flex; align-items: center; background: var(--tab-bar-background); border-bottom: 1px solid var(--border-color);',
1463
- TAB_BASE: 'padding: 4px 8px; cursor: pointer; border-right: 1px solid var(--border-color); display: flex; align-items: center; gap: 4px;',
1464
- TAB_CLOSE_STYLE: 'background: none; border: none; cursor: pointer; padding: 2px; border-radius: 2px; opacity: 0.7;',
1465
- TEXT_EDITOR_STYLE: 'flex: 1; overflow: auto; font-family: var(--editor-font-family); font-size: var(--editor-font-size);'
1466
- };
1467
- const THEMES = {
1468
- DARK: {
1469
- '--border-color': '#3e3e42',
1470
- '--dimmed-color': '#858585',
1471
- '--editor-font-family': 'Consolas, Monaco, "Courier New", monospace',
1472
- '--editor-font-size': '14px',
1473
- '--focus-border-color': '#0078d4',
1474
- '--tab-active-background': '#1e1e1e',
1475
- '--tab-active-color': '#ffffff',
1476
- '--tab-bar-background': '#252526',
1477
- '--tab-close-hover-background': '#3e3e42'
1478
- },
1479
- LIGHT: {
1480
- '--border-color': '#e1e1e1',
1481
- '--dimmed-color': '#999999',
1482
- '--editor-font-family': 'Consolas, Monaco, "Courier New", monospace',
1483
- '--editor-font-size': '14px',
1484
- '--focus-border-color': '#0078d4',
1485
- '--tab-active-background': '#ffffff',
1486
- '--tab-active-color': '#333333',
1487
- '--tab-bar-background': '#f3f3f3',
1488
- '--tab-close-hover-background': '#e1e1e1'
1489
- }
1490
- };
1491
- const getThemeStyles = (theme = 'DARK') => {
1492
- const themeVars = THEMES[theme];
1493
- return Object.entries(themeVars).map(([key, value]) => `${key}: ${value};`).join(' ');
1494
- };
1495
-
1496
- const renderTab = (tab, isActive) => {
1497
- return {
1498
- attributes: {
1499
- [CSS_ATTRIBUTES.DATA_TAB_ID]: tab.id,
1500
- style: `${CSS_STYLES.TAB_BASE} ${isActive ? CSS_STYLES.TAB_ACTIVE_STYLE : ''}`
1501
- },
1502
- childCount: 2,
1503
- children: [{
1504
- childCount: 1,
1505
- children: [tab.isDirty ? `*${tab.title}` : tab.title],
1506
- className: CSS_CLASSES.TAB_TITLE,
1507
- type: Span
1508
- }, {
1509
- attributes: {
1510
- [CSS_ATTRIBUTES.DATA_ACTION]: 'close-tab',
1511
- [CSS_ATTRIBUTES.DATA_TAB_ID]: tab.id,
1512
- style: CSS_STYLES.TAB_CLOSE_STYLE
1513
- },
1514
- childCount: 1,
1515
- children: ['×'],
1516
- className: CSS_CLASSES.TAB_CLOSE,
1517
- type: Button
1518
- }],
1519
- className: `${CSS_CLASSES.TAB} ${isActive ? CSS_CLASSES.TAB_ACTIVE : ''}`,
1520
- type: Div
1521
- };
1522
- };
1523
- const renderTabBar = group => {
1524
- return {
1525
- attributes: {
1526
- [CSS_ATTRIBUTES.DATA_GROUP_ID]: group.id,
1527
- style: CSS_STYLES.TAB_BAR_BASE
1528
- },
1529
- childCount: group.tabs.length,
1530
- children: group.tabs.map(tab => renderTab(tab, tab.id === group.activeTabId)),
1531
- className: CSS_CLASSES.TAB_BAR,
1532
- type: Div
1533
- };
1534
- };
1535
- const renderEditor = tab => {
1536
- if (tab.editorType === 'custom') {
1537
- return {
1538
- attributes: {
1539
- [CSS_ATTRIBUTES.DATA_CUSTOM_EDITOR_ID]: tab.customEditorId,
1540
- [CSS_ATTRIBUTES.DATA_TAB_ID]: tab.id,
1541
- style: CSS_STYLES.CUSTOM_EDITOR_STYLE
1542
- },
1543
- childCount: 1,
1544
- children: [`Custom Editor: ${tab.customEditorId}`],
1545
- className: CSS_CLASSES.CUSTOM_EDITOR,
1546
- type: Div
1547
- };
1548
- }
1549
- return {
1550
- attributes: {
1551
- [CSS_ATTRIBUTES.DATA_LANGUAGE]: tab.language || 'plaintext',
1552
- [CSS_ATTRIBUTES.DATA_TAB_ID]: tab.id,
1553
- style: CSS_STYLES.TEXT_EDITOR_STYLE
1554
- },
1555
- childCount: 1,
1556
- children: [{
1557
- childCount: 1,
1558
- children: [tab.content || ''],
1559
- className: CSS_CLASSES.EDITOR_CONTENT,
1560
- type: Pre
1561
- }],
1562
- className: CSS_CLASSES.TEXT_EDITOR,
1563
- type: Div
1564
- };
1565
- };
1566
- const renderEditorGroup = group => {
1567
- const activeTab = group.tabs.find(tab => tab.id === group.activeTabId);
1568
- return {
1569
- attributes: {
1570
- [CSS_ATTRIBUTES.DATA_GROUP_ID]: group.id,
1571
- style: `${CSS_STYLES.EDITOR_GROUP_BASE} flex: ${group.size}; ${group.focused ? CSS_STYLES.EDITOR_GROUP_FOCUSED_STYLE : ''}`
1572
- },
1573
- childCount: 2,
1574
- children: [renderTabBar(group), {
1575
- childCount: activeTab ? 1 : 1,
1576
- children: activeTab ? [renderEditor(activeTab)] : [{
1577
- attributes: {
1578
- style: CSS_STYLES.EMPTY_EDITOR_STYLE
1579
- },
1580
- childCount: 1,
1581
- children: ['No open tabs'],
1582
- className: CSS_CLASSES.EMPTY_EDITOR,
1583
- type: Div
1584
- }],
1585
- className: CSS_CLASSES.EDITOR_CONTAINER,
1586
- type: Div
1587
- }],
1588
- className: `${CSS_CLASSES.EDITOR_GROUP} ${group.focused ? CSS_CLASSES.EDITOR_GROUP_FOCUSED : ''}`,
1589
- type: Div
1590
- };
1591
- };
1592
- const getMainAreaVirtualDom = state => {
1593
- return [{
1594
- attributes: {
1595
- [CSS_ATTRIBUTES.DATA_DIRECTION]: state.layout.direction,
1596
- style: getThemeStyles('DARK')
1597
- },
1598
- childCount: 1,
1599
- children: [{
1600
- attributes: {
1601
- style: state.layout.direction === 'horizontal' ? CSS_STYLES.FLEX_ROW : CSS_STYLES.FLEX_COLUMN
1602
- },
1603
- childCount: state.layout.groups.length,
1604
- children: state.layout.groups.map(renderEditorGroup),
1605
- className: CSS_CLASSES.EDITOR_GROUPS_CONTAINER,
1606
- type: Div
1607
- }],
1608
- className: CSS_CLASSES.MAIN_AREA,
1609
- type: Div
1610
- }];
1611
- };
1612
-
1613
- const splitEditorGroup = (state, groupId, direction) => {
1614
- const sourceGroup = state.layout.groups.find(group => group.id === groupId);
1615
- if (!sourceGroup) {
1616
- return state;
1617
- }
1618
- const newGroupId = `group-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
1619
- const isHorizontalSplit = direction === 'left' || direction === 'right';
1620
- const newLayoutDirection = isHorizontalSplit ? 'horizontal' : 'vertical';
1621
- const updatedGroups = state.layout.groups.map(group => {
1622
- if (group.id === groupId) {
1623
- return {
1624
- ...group,
1625
- focused: false,
1626
- size: 50
1627
- };
1628
- }
1629
- return group;
1630
- });
1631
- const newGroup = {
1632
- activeTabId: undefined,
1633
- focused: true,
1634
- id: newGroupId,
1635
- size: 50,
1636
- tabs: []
1637
- };
1638
- let reorderedGroups;
1639
- if (direction === 'right' || direction === 'down') {
1640
- reorderedGroups = [...updatedGroups, newGroup];
1641
- } else {
1642
- const sourceIndex = updatedGroups.findIndex(group => group.id === groupId);
1643
- reorderedGroups = [...updatedGroups.slice(0, sourceIndex), newGroup, ...updatedGroups.slice(sourceIndex)];
1644
- }
1645
- return {
1646
- ...state,
1647
- layout: {
1648
- activeGroupId: newGroupId,
1649
- direction: newLayoutDirection,
1650
- groups: reorderedGroups
1651
- }
1652
- };
1653
- };
1654
-
1655
- const switchTab = (state, groupId, tabId) => {
1656
- const groups = state.layout.groups.map(group => {
1657
- if (group.id === groupId) {
1658
- const tabExists = group.tabs.some(tab => tab.id === tabId);
1659
- if (tabExists) {
1660
- return {
1661
- ...group,
1662
- activeTabId: tabId
1663
- };
1664
- }
1665
- }
1666
- return group;
1667
- });
1668
- return {
1669
- ...state,
1670
- layout: {
1671
- ...state.layout,
1672
- groups
1673
- }
1674
- };
1675
- };
1676
-
1677
- const handleMainAreaClick = (state, event) => {
1678
- const {
1679
- target
1680
- } = event;
1681
- if (!target.dataset) {
1682
- return state;
1683
- }
1684
- const {
1685
- dataset
1686
- } = target;
1687
-
1688
- // Handle tab click
1689
- if (dataset.tabId) {
1690
- const {
1691
- groupId
1692
- } = dataset;
1693
- if (groupId) {
1694
- return switchTab(state, groupId, dataset.tabId);
1695
- }
1696
- }
1697
-
1698
- // Handle tab close button
1699
- if (dataset.action === 'close-tab' && dataset.tabId) {
1700
- const {
1701
- groupId
1702
- } = dataset;
1703
- if (groupId) {
1704
- return closeTab(state, groupId, dataset.tabId);
1705
- }
1706
- }
1707
-
1708
- // Handle editor group focus
1709
- if (dataset.groupId && !dataset.tabId) {
1710
- return focusEditorGroup(state, dataset.groupId);
1711
- }
1712
-
1713
- // Handle split actions
1714
- if (dataset.action?.startsWith('split-')) {
1715
- const {
1716
- groupId
1717
- } = dataset;
1718
- if (groupId) {
1719
- const direction = dataset.action.replace('split-', '');
1720
- return splitEditorGroup(state, groupId, direction);
1721
- }
1722
- }
1723
- return state;
1724
- };
1725
-
1726
- const handleMainAreaKeyboard = (state, event) => {
1727
- const {
1728
- ctrlKey = false,
1729
- key,
1730
- metaKey = false,
1731
- shiftKey = false
1732
- } = event;
1733
- const isCtrl = ctrlKey || metaKey;
1734
- const activeGroup = state.layout.groups.find(group => group.focused);
1735
- if (!activeGroup) {
1736
- return state;
1737
- }
1738
-
1739
- // Tab navigation
1740
- if (key === 'Tab' && isCtrl) {
1741
- const groupIndex = state.layout.groups.findIndex(group => group.id === activeGroup.id);
1742
- const nextGroupIndex = shiftKey ? (groupIndex - 1 + state.layout.groups.length) % state.layout.groups.length : (groupIndex + 1) % state.layout.groups.length;
1743
- const nextGroup = state.layout.groups[nextGroupIndex];
1744
- return focusEditorGroup(state, nextGroup.id);
1745
- }
1746
-
1747
- // Switch between tabs within group
1748
- if (key === 'ArrowLeft' && isCtrl && !shiftKey) {
1749
- const activeTabIndex = activeGroup.tabs.findIndex(tab => tab.id === activeGroup.activeTabId);
1750
- if (activeTabIndex > 0) {
1751
- const prevTab = activeGroup.tabs[activeTabIndex - 1];
1752
- return switchTab(state, activeGroup.id, prevTab.id);
1753
- }
1754
- }
1755
- if (key === 'ArrowRight' && isCtrl && !shiftKey) {
1756
- const activeTabIndex = activeGroup.tabs.findIndex(tab => tab.id === activeGroup.activeTabId);
1757
- if (activeTabIndex < activeGroup.tabs.length - 1) {
1758
- const nextTab = activeGroup.tabs[activeTabIndex + 1];
1759
- return switchTab(state, activeGroup.id, nextTab.id);
1760
- }
1761
- }
1762
-
1763
- // Close current tab
1764
- if (key === 'w' && isCtrl && activeGroup.activeTabId) {
1765
- return closeTab(state, activeGroup.id, activeGroup.activeTabId);
1766
- }
1767
-
1768
- // Split editor
1769
- if (key === '\\' && isCtrl) {
1770
- const direction = shiftKey ? 'down' : 'right';
1771
- return splitEditorGroup(state, activeGroup.id, direction);
1772
- }
1773
- return state;
1774
- };
1775
-
1776
- const moveTabToGroup = (state, sourceGroupId, targetGroupId, tabId, targetIndex) => {
1777
- const sourceGroup = state.layout.groups.find(group => group.id === sourceGroupId);
1778
- const targetGroup = state.layout.groups.find(group => group.id === targetGroupId);
1779
- if (!sourceGroup || !targetGroup || sourceGroupId === targetGroupId) {
1780
- return state;
1781
- }
1782
- const tabToMove = sourceGroup.tabs.find(tab => tab.id === tabId);
1783
- if (!tabToMove) {
1784
- return state;
1785
- }
1786
- const updatedGroups = state.layout.groups.map(group => {
1787
- if (group.id === sourceGroupId) {
1788
- const newTabs = group.tabs.filter(tab => tab.id !== tabId);
1789
- let newActiveTabId = group.activeTabId;
1790
- if (group.activeTabId === tabId) {
1791
- if (newTabs.length > 0) {
1792
- const removedIndex = group.tabs.findIndex(tab => tab.id === tabId);
1793
- newActiveTabId = newTabs[Math.min(removedIndex, newTabs.length - 1)].id;
1794
- } else {
1795
- newActiveTabId = undefined;
1796
- }
1797
- }
1798
- return {
1799
- ...group,
1800
- activeTabId: newActiveTabId,
1801
- tabs: newTabs
1802
- };
1803
- }
1804
- if (group.id === targetGroupId) {
1805
- const insertIndex = targetIndex === undefined ? group.tabs.length : targetIndex;
1806
- const newTabs = [...group.tabs];
1807
- newTabs.splice(insertIndex, 0, tabToMove);
1808
- return {
1809
- ...group,
1810
- activeTabId: tabId,
1811
- tabs: newTabs
1812
- };
1813
- }
1814
- return group;
1815
- });
1816
- return {
1817
- ...state,
1818
- layout: {
1819
- ...state.layout,
1820
- activeGroupId: targetGroupId,
1821
- groups: updatedGroups
1822
- }
1823
- };
1824
- };
1825
-
1826
- const startTabDrag = (state, tabId, groupId) => {
1827
- return {
1828
- dragState: {
1829
- draggedTabId: tabId,
1830
- sourceGroupId: groupId
1831
- },
1832
- state
1833
- };
1834
- };
1835
- const updateTabDrag = (state, dragState, targetGroupId, targetIndex) => {
1836
- return {
1837
- ...dragState,
1838
- targetGroupId,
1839
- targetIndex
1840
- };
1841
- };
1842
- const endTabDrag = (state, dragState) => {
1843
- if (dragState.targetGroupId && dragState.targetGroupId !== dragState.sourceGroupId) {
1844
- return moveTabToGroup(state, dragState.sourceGroupId, dragState.targetGroupId, dragState.draggedTabId, dragState.targetIndex);
1845
- }
1846
- return state;
1847
- };
1848
-
1849
- const openTab = (state, groupId, tab) => {
1850
- const newTab = {
1851
- ...tab,
1852
- id: `tab-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`
1853
- };
1854
- const groups = state.layout.groups.map(group => {
1855
- if (group.id === groupId) {
1856
- return {
1857
- ...group,
1858
- activeTabId: newTab.id,
1859
- tabs: [...group.tabs, newTab]
1860
- };
1861
- }
1862
- return group;
1863
- });
1864
- return {
1865
- ...state,
1866
- layout: {
1867
- ...state.layout,
1868
- groups
1869
- }
1870
- };
1871
- };
1872
-
1873
- const restoreMainAreaState = (savedState, currentState) => {
1874
- try {
1875
- const parsed = JSON.parse(savedState);
1876
- return {
1877
- ...currentState,
1878
- layout: parsed.layout
1879
- };
1880
- } catch (error) {
1881
- console.error('Failed to restore main area state:', error);
1882
- return currentState;
1883
- }
1884
- };
1885
-
1886
- const saveMainAreaState = state => {
1887
- return JSON.stringify({
1888
- layout: state.layout,
1889
- version: '1.0.0'
1890
- });
1891
- };
1892
-
1893
- const mainAreaCommandMap = {
1894
- 'MainArea.closeEditorGroup': wrapCommand((state, groupId) => closeEditorGroup(state, groupId)),
1895
- 'MainArea.closeTab': wrapCommand((state, groupId, tabId) => closeTab(state, groupId, tabId)),
1896
- 'MainArea.create': () => {},
1897
- 'MainArea.endTabDrag': wrapCommand((state, dragState) => endTabDrag(state, dragState)),
1898
- 'MainArea.focusEditorGroup': wrapCommand((state, groupId) => focusEditorGroup(state, groupId)),
1899
- 'MainArea.getCommandIds': getCommandIds,
1900
- 'MainArea.getVirtualDom': state => getMainAreaVirtualDom(state),
1901
- 'MainArea.handleClick': wrapCommand((state, event) => handleMainAreaClick(state, event)),
1902
- 'MainArea.handleKeyboard': wrapCommand((state, event) => handleMainAreaKeyboard(state, event)),
1903
- 'MainArea.moveTabToGroup': wrapCommand((state, sourceGroupId, targetGroupId, tabId, targetIndex) => moveTabToGroup(state, sourceGroupId, targetGroupId, tabId, targetIndex)),
1904
- 'MainArea.openTab': wrapCommand((state, groupId, tab) => openTab(state, groupId, tab)),
1905
- 'MainArea.restoreState': wrapCommand((state, savedState) => restoreMainAreaState(savedState, state)),
1906
- 'MainArea.saveState': state => saveMainAreaState(state),
1907
- 'MainArea.splitEditorGroup': wrapCommand((state, groupId, direction) => splitEditorGroup(state, groupId, direction)),
1908
- 'MainArea.startTabDrag': (state, tabId, groupId) => startTabDrag(state, tabId, groupId),
1909
- 'MainArea.switchTab': wrapCommand((state, groupId, tabId) => switchTab(state, groupId, tabId)),
1910
- 'MainArea.terminate': terminate,
1911
- 'MainArea.updateTabDrag': (state, dragState, targetGroupId, targetIndex) => updateTabDrag(state, dragState, targetGroupId, targetIndex)
1912
- };
1913
-
1914
1746
  const main$1 = async () => {
1915
- registerCommands(mainAreaCommandMap);
1747
+ registerCommands(commandMap);
1916
1748
  const rpc = await WebWorkerRpcClient.create({
1917
- commandMap: mainAreaCommandMap
1749
+ commandMap: commandMap
1918
1750
  });
1919
1751
  set$1(rpc);
1920
1752
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/main-area-worker",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Main Area Worker",
5
5
  "repository": {
6
6
  "type": "git",