@lvce-editor/main-area-worker 1.11.0 → 1.12.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.
@@ -1038,6 +1038,7 @@ const Button$1 = 1;
1038
1038
  const Div = 4;
1039
1039
  const Span = 8;
1040
1040
  const Text = 12;
1041
+ const Img = 17;
1041
1042
  const Pre = 51;
1042
1043
 
1043
1044
  const Button = 'event.button';
@@ -1210,6 +1211,17 @@ const terminate = () => {
1210
1211
  globalThis.close();
1211
1212
  };
1212
1213
 
1214
+ const closeAll$1 = state => {
1215
+ return {
1216
+ ...state,
1217
+ layout: {
1218
+ ...state.layout,
1219
+ activeGroupId: undefined,
1220
+ groups: []
1221
+ }
1222
+ };
1223
+ };
1224
+
1213
1225
  const {
1214
1226
  get,
1215
1227
  getCommandIds,
@@ -1356,13 +1368,144 @@ const handleClickCloseTab = (state, rawGroupIndex, rawIndex) => {
1356
1368
  return closeTab(state, groupId, tabId);
1357
1369
  };
1358
1370
 
1359
- const selectTab = async (state, groupIndex, index) => {
1371
+ const FileSystemReadFile = 'ExtensionHostFileSystem.readFile';
1372
+
1373
+ // Counter for request IDs to handle race conditions
1374
+ let requestIdCounter = 0;
1375
+ const getNextRequestId = () => {
1376
+ return ++requestIdCounter;
1377
+ };
1378
+ const updateTab = (state, tabId, updates) => {
1379
+ const {
1380
+ layout
1381
+ } = state;
1382
+ const {
1383
+ groups
1384
+ } = layout;
1385
+ const updatedGroups = groups.map(group => {
1386
+ const tabIndex = group.tabs.findIndex(t => t.id === tabId);
1387
+ if (tabIndex === -1) {
1388
+ return group;
1389
+ }
1390
+ const updatedTabs = group.tabs.map((tab, index) => {
1391
+ if (index === tabIndex) {
1392
+ return {
1393
+ ...tab,
1394
+ ...updates
1395
+ };
1396
+ }
1397
+ return tab;
1398
+ });
1399
+ return {
1400
+ ...group,
1401
+ tabs: updatedTabs
1402
+ };
1403
+ });
1404
+ return {
1405
+ ...state,
1406
+ layout: {
1407
+ ...layout,
1408
+ groups: updatedGroups
1409
+ }
1410
+ };
1411
+ };
1412
+ const findTab = (state, tabId) => {
1360
1413
  const {
1361
1414
  layout
1362
1415
  } = state;
1363
1416
  const {
1364
1417
  groups
1365
1418
  } = layout;
1419
+ for (const group of groups) {
1420
+ const tab = group.tabs.find(t => t.id === tabId);
1421
+ if (tab) {
1422
+ return tab;
1423
+ }
1424
+ }
1425
+ return undefined;
1426
+ };
1427
+ const loadFileContent = async path => {
1428
+ // @ts-ignore
1429
+ const content = await invoke(FileSystemReadFile, path);
1430
+ return content;
1431
+ };
1432
+ const loadTabContentAsync = async (tabId, path, requestId, getLatestState) => {
1433
+ try {
1434
+ const content = await loadFileContent(path);
1435
+
1436
+ // Check for race condition: get the latest state and verify the request ID
1437
+ const latestState = getLatestState();
1438
+ const latestTab = findTab(latestState, tabId);
1439
+
1440
+ // If the tab no longer exists or a newer request was started, discard this result
1441
+ if (!latestTab || latestTab.loadRequestId !== requestId) {
1442
+ return latestState;
1443
+ }
1444
+ return updateTab(latestState, tabId, {
1445
+ content,
1446
+ errorMessage: undefined,
1447
+ loadingState: 'loaded'
1448
+ });
1449
+ } catch (error) {
1450
+ // Check for race condition before updating with error
1451
+ const latestState = getLatestState();
1452
+ const latestTab = findTab(latestState, tabId);
1453
+ if (!latestTab || latestTab.loadRequestId !== requestId) {
1454
+ return latestState;
1455
+ }
1456
+ const errorMessage = error instanceof Error ? error.message : 'Failed to load file content';
1457
+ return updateTab(latestState, tabId, {
1458
+ content: '',
1459
+ errorMessage,
1460
+ loadingState: 'error'
1461
+ });
1462
+ }
1463
+ };
1464
+
1465
+ const startContentLoading = (uid, tabId, path, requestId) => {
1466
+ const loadContent = async () => {
1467
+ try {
1468
+ const currentState = get(uid);
1469
+ if (!currentState) {
1470
+ return;
1471
+ }
1472
+ const getLatestState = () => get(uid) ?? currentState;
1473
+ const newState = await loadTabContentAsync(tabId, path, requestId, getLatestState);
1474
+ const oldState = get(uid);
1475
+ if (oldState) {
1476
+ set(uid, oldState, newState);
1477
+ }
1478
+ await invoke('Main.refresh');
1479
+ } catch {
1480
+ // Silently ignore errors - the tab may have been closed or the component unmounted
1481
+ }
1482
+ };
1483
+ void loadContent();
1484
+ };
1485
+
1486
+ const shouldLoadContent = tab => {
1487
+ // Load if:
1488
+ // - Has a path (file-based tab)
1489
+ // - Not already loaded or currently loading
1490
+ if (!tab.path) {
1491
+ return false;
1492
+ }
1493
+ if (tab.loadingState === 'loading') {
1494
+ return false;
1495
+ }
1496
+ if (tab.loadingState === 'loaded' && tab.content) {
1497
+ return false;
1498
+ }
1499
+ return true;
1500
+ };
1501
+ const selectTab = async (state, groupIndex, index) => {
1502
+ const {
1503
+ layout,
1504
+ uid
1505
+ } = state;
1506
+ const {
1507
+ groups
1508
+ } = layout;
1366
1509
 
1367
1510
  // Validate indexes
1368
1511
  if (groupIndex < 0 || groupIndex >= groups.length) {
@@ -1381,13 +1524,40 @@ const selectTab = async (state, groupIndex, index) => {
1381
1524
  return state;
1382
1525
  }
1383
1526
 
1527
+ // Check if we need to load content for the newly selected tab
1528
+ const needsLoading = shouldLoadContent(tab);
1529
+ const requestId = needsLoading ? getNextRequestId() : 0;
1530
+
1384
1531
  // Update the groups array with the new active tab and active group
1385
- const updatedGroups = groups.map((g, i) => ({
1386
- ...g,
1387
- activeTabId: i === groupIndex ? tabId : g.activeTabId,
1388
- focused: i === groupIndex
1389
- }));
1390
- return {
1532
+ // Also set loading state if needed
1533
+ const updatedGroups = groups.map((g, i) => {
1534
+ if (i !== groupIndex) {
1535
+ return {
1536
+ ...g,
1537
+ focused: false
1538
+ };
1539
+ }
1540
+
1541
+ // This is the group being selected
1542
+ const updatedTabs = needsLoading ? g.tabs.map(t => {
1543
+ if (t.id === tabId) {
1544
+ return {
1545
+ ...t,
1546
+ errorMessage: undefined,
1547
+ loadingState: 'loading',
1548
+ loadRequestId: requestId
1549
+ };
1550
+ }
1551
+ return t;
1552
+ }) : g.tabs;
1553
+ return {
1554
+ ...g,
1555
+ activeTabId: tabId,
1556
+ focused: true,
1557
+ tabs: updatedTabs
1558
+ };
1559
+ });
1560
+ const newState = {
1391
1561
  ...state,
1392
1562
  layout: {
1393
1563
  ...layout,
@@ -1395,6 +1565,12 @@ const selectTab = async (state, groupIndex, index) => {
1395
1565
  groups: updatedGroups
1396
1566
  }
1397
1567
  };
1568
+
1569
+ // Start loading content in the background if needed
1570
+ if (needsLoading && tab.path) {
1571
+ startContentLoading(uid, tabId, tab.path, requestId);
1572
+ }
1573
+ return newState;
1398
1574
  };
1399
1575
 
1400
1576
  const handleClickTab = async (state, groupIndexRaw, indexRaw) => {
@@ -1444,41 +1620,47 @@ const initialize = async () => {
1444
1620
  set$2(rpc);
1445
1621
  };
1446
1622
 
1623
+ let idCounter = 0;
1624
+ const create = () => {
1625
+ idCounter++;
1626
+ return idCounter;
1627
+ };
1628
+
1447
1629
  const getTabs = async () => {
1448
1630
  const tabs = [{
1449
1631
  content: '',
1450
1632
  editorType: 'text',
1451
- id: 1,
1633
+ id: create(),
1452
1634
  isDirty: false,
1453
1635
  title: 'tab 1'
1454
1636
  }, {
1455
1637
  content: '',
1456
1638
  editorType: 'text',
1457
- id: 2,
1639
+ id: create(),
1458
1640
  isDirty: false,
1459
1641
  title: 'tab 2'
1460
1642
  }, {
1461
1643
  content: '',
1462
1644
  editorType: 'text',
1463
- id: 3,
1645
+ id: create(),
1464
1646
  isDirty: false,
1465
1647
  title: 'tab 3'
1466
1648
  }, {
1467
1649
  content: '',
1468
1650
  editorType: 'text',
1469
- id: 4,
1651
+ id: create(),
1470
1652
  isDirty: false,
1471
1653
  title: 'tab 4'
1472
1654
  }, {
1473
1655
  content: '',
1474
1656
  editorType: 'text',
1475
- id: 5,
1657
+ id: create(),
1476
1658
  isDirty: false,
1477
1659
  title: 'tab 5'
1478
1660
  }, {
1479
1661
  content: '',
1480
1662
  editorType: 'text',
1481
- id: 6,
1663
+ id: create(),
1482
1664
  isDirty: false,
1483
1665
  title: 'tab 6'
1484
1666
  }];
@@ -1655,14 +1837,8 @@ const focusEditorGroup = (state, groupId) => {
1655
1837
  };
1656
1838
  };
1657
1839
 
1658
- let idCounter = 0;
1659
- const create = () => {
1660
- idCounter++;
1661
- return idCounter;
1662
- };
1663
-
1664
1840
  const openTab = (state, groupId, tab) => {
1665
- const newTab = {
1841
+ const newTab = 'id' in tab && tab.id !== undefined ? tab : {
1666
1842
  ...tab,
1667
1843
  id: create()
1668
1844
  };
@@ -1757,7 +1933,8 @@ const openUri = async (state, options) => {
1757
1933
 
1758
1934
  // Find the active group (by activeGroupId or focused flag)
1759
1935
  const {
1760
- layout
1936
+ layout,
1937
+ uid
1761
1938
  } = state;
1762
1939
  const {
1763
1940
  activeGroupId,
@@ -1765,15 +1942,21 @@ const openUri = async (state, options) => {
1765
1942
  } = layout;
1766
1943
  const activeGroup = activeGroupId === undefined ? groups.find(group => group.focused) : groups.find(group => group.id === activeGroupId);
1767
1944
 
1945
+ // Generate a request ID for content loading
1946
+ const requestId = getNextRequestId();
1947
+
1768
1948
  // If no active group exists, create one
1769
1949
  if (!activeGroup) {
1770
1950
  const groupId = create();
1771
1951
  const title = getLabel(uri);
1952
+ const tabId = create();
1772
1953
  const newTab = {
1773
1954
  content: '',
1774
1955
  editorType: 'text',
1775
- id: create(),
1956
+ id: tabId,
1776
1957
  isDirty: false,
1958
+ loadingState: 'loading',
1959
+ loadRequestId: requestId,
1777
1960
  path: uri,
1778
1961
  title
1779
1962
  };
@@ -1784,7 +1967,7 @@ const openUri = async (state, options) => {
1784
1967
  size: 100,
1785
1968
  tabs: [newTab]
1786
1969
  };
1787
- return {
1970
+ const newState = {
1788
1971
  ...state,
1789
1972
  layout: {
1790
1973
  ...layout,
@@ -1792,18 +1975,36 @@ const openUri = async (state, options) => {
1792
1975
  groups: [...groups, newGroup]
1793
1976
  }
1794
1977
  };
1978
+
1979
+ // Start loading content
1980
+ startContentLoading(uid, tabId, uri, requestId);
1981
+ return newState;
1795
1982
  }
1796
1983
 
1797
1984
  // Create a new tab with the URI in the active group
1798
1985
  const title = getLabel(uri);
1986
+ const tabId = create();
1799
1987
  const newTab = {
1800
1988
  content: '',
1801
1989
  editorType: 'text',
1990
+ id: tabId,
1802
1991
  isDirty: false,
1992
+ loadingState: 'loading',
1993
+ loadRequestId: requestId,
1803
1994
  path: uri,
1804
1995
  title
1805
1996
  };
1806
- return openTab(state, activeGroup.id, newTab);
1997
+ const newState = openTab(state, activeGroup.id, newTab);
1998
+
1999
+ // Start loading content
2000
+ startContentLoading(uid, tabId, uri, requestId);
2001
+ return newState;
2002
+ };
2003
+
2004
+ const refresh = state => {
2005
+ return {
2006
+ ...state
2007
+ };
1807
2008
  };
1808
2009
 
1809
2010
  const text = data => {
@@ -1817,17 +2018,42 @@ const text = data => {
1817
2018
  const CSS_CLASSES = {
1818
2019
  EDITOR_GROUPS_CONTAINER: 'editor-groups-container'};
1819
2020
 
2021
+ const renderLoading = () => {
2022
+ return [{
2023
+ childCount: 1,
2024
+ className: 'TextEditor TextEditor--loading',
2025
+ type: Div
2026
+ }, {
2027
+ childCount: 1,
2028
+ className: 'EditorContent EditorContent--loading',
2029
+ type: Div
2030
+ }, text('Loading...')];
2031
+ };
2032
+ const renderError = errorMessage => {
2033
+ return [{
2034
+ childCount: 1,
2035
+ className: 'TextEditor TextEditor--error',
2036
+ type: Div
2037
+ }, {
2038
+ childCount: 1,
2039
+ className: 'EditorContent EditorContent--error',
2040
+ type: Div
2041
+ }, text(`Error: ${errorMessage}`)];
2042
+ };
2043
+ const renderContent = content => {
2044
+ return [{
2045
+ childCount: 1,
2046
+ className: 'TextEditor',
2047
+ type: Div
2048
+ }, {
2049
+ childCount: 1,
2050
+ className: 'EditorContent',
2051
+ type: Pre
2052
+ }, text(content)];
2053
+ };
1820
2054
  const renderEditor = tab => {
1821
2055
  if (!tab) {
1822
- return [{
1823
- childCount: 1,
1824
- className: 'TextEditor',
1825
- type: Div
1826
- }, {
1827
- childCount: 1,
1828
- className: 'EditorContent',
1829
- type: Pre
1830
- }, text('')];
2056
+ return renderContent('');
1831
2057
  }
1832
2058
  if (tab.editorType === 'custom') {
1833
2059
  return [{
@@ -1836,15 +2062,19 @@ const renderEditor = tab => {
1836
2062
  type: Div
1837
2063
  }, text(`Custom Editor: ${tab.customEditorId}`)];
1838
2064
  }
1839
- return [{
1840
- childCount: 1,
1841
- className: 'TextEditor',
1842
- type: Div
1843
- }, {
1844
- childCount: 1,
1845
- className: 'EditorContent',
1846
- type: Pre
1847
- }, text(tab.content || '')];
2065
+
2066
+ // Handle loading state
2067
+ if (tab.loadingState === 'loading') {
2068
+ return renderLoading();
2069
+ }
2070
+
2071
+ // Handle error state
2072
+ if (tab.loadingState === 'error' && tab.errorMessage) {
2073
+ return renderError(tab.errorMessage);
2074
+ }
2075
+
2076
+ // Default: render content
2077
+ return renderContent(tab.content || '');
1848
2078
  };
1849
2079
 
1850
2080
  const HandleClick = 11;
@@ -1854,7 +2084,7 @@ const HandleTabContextMenu = 14;
1854
2084
 
1855
2085
  const renderTab = (tab, isActive, tabIndex, groupIndex) => {
1856
2086
  return [{
1857
- childCount: 2,
2087
+ childCount: 3,
1858
2088
  className: isActive ? 'MainTab MainTabSelected' : 'MainTab',
1859
2089
  'data-groupIndex': groupIndex,
1860
2090
  'data-index': tabIndex,
@@ -1863,6 +2093,11 @@ const renderTab = (tab, isActive, tabIndex, groupIndex) => {
1863
2093
  role: 'tab',
1864
2094
  title: tab.path || tab.title,
1865
2095
  type: Div
2096
+ }, {
2097
+ childCount: 0,
2098
+ className: 'TabIcon',
2099
+ src: 'icons/refresh.svg',
2100
+ type: Img
1866
2101
  }, {
1867
2102
  childCount: 1,
1868
2103
  className: 'TabTitle',
@@ -1984,6 +2219,7 @@ const saveState = state => {
1984
2219
  };
1985
2220
 
1986
2221
  const commandMap = {
2222
+ 'MainArea.closeAll': wrapCommand(closeAll$1),
1987
2223
  'MainArea.create': create$1,
1988
2224
  'MainArea.diff2': diff2,
1989
2225
  'MainArea.getCommandIds': getCommandIds,
@@ -1996,6 +2232,7 @@ const commandMap = {
1996
2232
  'MainArea.initialize': initialize,
1997
2233
  'MainArea.loadContent': wrapCommand(loadContent),
1998
2234
  'MainArea.openUri': wrapCommand(openUri),
2235
+ 'MainArea.refresh': wrapCommand(refresh),
1999
2236
  'MainArea.render2': render2,
2000
2237
  'MainArea.renderEventListeners': renderEventListeners,
2001
2238
  'MainArea.resize': wrapCommand(resize),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/main-area-worker",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "description": "Main Area Worker",
5
5
  "repository": {
6
6
  "type": "git",