@lvce-editor/main-area-worker 9.74.0 → 9.74.2

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.
@@ -628,9 +628,12 @@ const applyDataProperties = (restoredError, error) => {
628
628
  writable: true
629
629
  });
630
630
  }
631
- if (error.data.type) {
632
- // @ts-ignore
633
- restoredError.name = error.data.type;
631
+ const name = error.data.name || error.data.type;
632
+ if (name) {
633
+ Object.defineProperty(restoredError, 'name', {
634
+ configurable: true,
635
+ value: name
636
+ });
634
637
  }
635
638
  };
636
639
  const applyDirectProperties = (restoredError, error) => {
@@ -647,7 +650,7 @@ const applyDirectProperties = (restoredError, error) => {
647
650
  }
648
651
  };
649
652
  const restoreMessageError = (error, _currentStack) => {
650
- const restoredError = constructError(error.message, error.type || error.data?.type, error.name);
653
+ const restoredError = constructError(error.message, error.type || error.data?.type, error.name || error.data?.name);
651
654
  if (typeof error.code === 'string' || typeof error.code === 'number') {
652
655
  Object.defineProperty(restoredError, 'code', {
653
656
  configurable: true,
@@ -2877,7 +2880,7 @@ const create$1 = (uid, uri, x, y, width, height, platform, assetDir, tabHeight =
2877
2880
  };
2878
2881
 
2879
2882
  const isEqual$3 = (oldState, newState) => {
2880
- return oldState.layout === newState.layout && oldState.width === newState.width;
2883
+ return oldState.layout === newState.layout && oldState.width === newState.width && oldState.activeTabVisibilityVersion === newState.activeTabVisibilityVersion;
2881
2884
  };
2882
2885
 
2883
2886
  const isEqual$2 = (oldState, newState) => {
@@ -4442,6 +4445,12 @@ const tabIconNode = {
4442
4445
  role: None$2,
4443
4446
  type: Div
4444
4447
  };
4448
+ const emptyTabIconNode = {
4449
+ childCount: 0,
4450
+ className: TabIcon,
4451
+ role: None$2,
4452
+ type: Div
4453
+ };
4445
4454
  const tabTitleNode = {
4446
4455
  childCount: 1,
4447
4456
  className: TabTitle,
@@ -4477,7 +4486,7 @@ const renderTabIcon = tab => {
4477
4486
  return renderMaskIcon(MaskIconDebugAlt2);
4478
4487
  }
4479
4488
  if (!tab.icon) {
4480
- return [];
4489
+ return [emptyTabIconNode];
4481
4490
  }
4482
4491
  return [{
4483
4492
  childCount: 0,
@@ -4511,7 +4520,7 @@ const renderTab = (tab, isActive, tabIndex, groupIndex, dropIndicatorPosition) =
4511
4520
  }
4512
4521
  return [{
4513
4522
  'aria-selected': isActive,
4514
- childCount: 2 + (tabIconNodes.length > 0 ? 1 : 0),
4523
+ childCount: 3,
4515
4524
  className,
4516
4525
  'data-groupIndex': groupIndex,
4517
4526
  'data-index': tabIndex,
@@ -5433,68 +5442,6 @@ const isDirectoryEditorInput = async (editorInput, applicationId) => {
5433
5442
  }
5434
5443
  };
5435
5444
 
5436
- const switchTab = (state, groupId, tabId) => {
5437
- const {
5438
- layout
5439
- } = state;
5440
- const {
5441
- groups
5442
- } = layout;
5443
- const updatedGroups = groups.map(group => {
5444
- if (group.id === groupId) {
5445
- const tabExists = group.tabs.some(tab => tab.id === tabId);
5446
- if (tabExists) {
5447
- return {
5448
- ...group,
5449
- activeTabId: tabId
5450
- };
5451
- }
5452
- }
5453
- return group;
5454
- });
5455
- return {
5456
- ...state,
5457
- layout: {
5458
- ...layout,
5459
- groups: updatedGroups
5460
- }
5461
- };
5462
- };
5463
-
5464
- const getIconsCached = (dirents, fileIconCache) => {
5465
- return dirents.map(dirent => fileIconCache[dirent]);
5466
- };
5467
-
5468
- const getBasename = uri => {
5469
- const lastSlashIndex = uri.lastIndexOf('/');
5470
- if (lastSlashIndex === -1) {
5471
- return uri;
5472
- }
5473
- return uri.slice(lastSlashIndex + 1);
5474
- };
5475
- const getMissingTabs = (tabs, fileIconCache) => {
5476
- const missingTabs = [];
5477
- for (const tab of tabs) {
5478
- if (tab.uri && !getFileIcon(tab.uri) && !(tab.uri in fileIconCache)) {
5479
- missingTabs.push(tab);
5480
- }
5481
- }
5482
- return missingTabs;
5483
- };
5484
- const tabToIconRequest = tab => {
5485
- const uri = tab.uri || '';
5486
- return {
5487
- name: getBasename(uri),
5488
- path: uri,
5489
- type: File
5490
- };
5491
- };
5492
- const getMissingIconRequestsForTabs = (tabs, fileIconCache) => {
5493
- const missingRequests = getMissingTabs(tabs, fileIconCache);
5494
- const iconRequests = missingRequests.map(tabToIconRequest);
5495
- return iconRequests;
5496
- };
5497
-
5498
5445
  const getSimpleIconRequestType = direntType => {
5499
5446
  if (direntType === Directory || direntType === DirectoryExpanded) {
5500
5447
  return 2;
@@ -5518,75 +5465,58 @@ const requestFileIcons = async requests => {
5518
5465
  return icons;
5519
5466
  };
5520
5467
 
5521
- const updateIconCache = (iconCache, missingRequests, newIcons) => {
5522
- if (missingRequests.length === 0) {
5523
- return iconCache;
5468
+ const prepareTabIcon = async (context, uri) => {
5469
+ const state = context.getState();
5470
+ let icon = getFileIcon(uri) || state.fileIconCache[uri] || '';
5471
+ if (!icon && uri && !(uri in state.fileIconCache)) {
5472
+ try {
5473
+ const icons = await requestFileIcons([{
5474
+ name: uri.slice(uri.lastIndexOf('/') + 1),
5475
+ path: uri,
5476
+ type: File
5477
+ }]);
5478
+ icon = icons[0] || '';
5479
+ } catch {
5480
+ // Icon failures must not prevent opening the file. The reserved slot keeps its title stable.
5481
+ }
5524
5482
  }
5525
- const newFileIconCache = {
5526
- ...iconCache
5527
- };
5528
- for (let i = 0; i < missingRequests.length; i++) {
5529
- const request = missingRequests[i];
5530
- const icon = newIcons[i];
5531
- newFileIconCache[request.path] = icon;
5483
+ const latestState = context.getState();
5484
+ if (latestState.iconThemeVersion !== state.iconThemeVersion) {
5485
+ return prepareTabIcon(context, uri);
5532
5486
  }
5533
- return newFileIconCache;
5534
- };
5535
-
5536
- const getUri = tab => {
5537
- return tab.uri || '';
5538
- };
5539
- const getFileIconsForTabs = async (tabs, fileIconCache) => {
5540
- const missingRequests = getMissingIconRequestsForTabs(tabs, fileIconCache);
5541
- const newIcons = await requestFileIcons(missingRequests);
5542
- const newFileIconCache = updateIconCache(fileIconCache, missingRequests, newIcons);
5543
- const tabUris = tabs.map(getUri);
5544
- const icons = getIconsCached(tabUris, newFileIconCache);
5545
5487
  return {
5546
- icons,
5547
- newFileIconCache
5488
+ icon,
5489
+ state: latestState
5548
5490
  };
5549
5491
  };
5550
5492
 
5551
- const updateIcon = (state, tabId, icon) => {
5493
+ const switchTab = (state, groupId, tabId) => {
5494
+ const {
5495
+ layout
5496
+ } = state;
5497
+ const {
5498
+ groups
5499
+ } = layout;
5500
+ const updatedGroups = groups.map(group => {
5501
+ if (group.id === groupId) {
5502
+ const tabExists = group.tabs.some(tab => tab.id === tabId);
5503
+ if (tabExists) {
5504
+ return {
5505
+ ...group,
5506
+ activeTabId: tabId
5507
+ };
5508
+ }
5509
+ }
5510
+ return group;
5511
+ });
5552
5512
  return {
5553
5513
  ...state,
5554
5514
  layout: {
5555
- ...state.layout,
5556
- groups: state.layout.groups.map(group => ({
5557
- ...group,
5558
- tabs: group.tabs.map(tab => tab.id === tabId ? {
5559
- ...tab,
5560
- icon
5561
- } : tab)
5562
- }))
5515
+ ...layout,
5516
+ groups: updatedGroups
5563
5517
  }
5564
5518
  };
5565
5519
  };
5566
- const updateTabIcon = async (context, readyState, tabId) => {
5567
- const newTab = findTabById(readyState, tabId);
5568
- if (!newTab || !newTab.tab.uri) {
5569
- return undefined;
5570
- }
5571
- const builtInIcon = getFileIcon(newTab.tab.uri);
5572
- if (builtInIcon) {
5573
- return context.updateState(state => updateIcon(state, tabId, builtInIcon));
5574
- }
5575
- try {
5576
- const {
5577
- newFileIconCache
5578
- } = await getFileIconsForTabs([newTab.tab], readyState.fileIconCache);
5579
- const stateBeforeIconUpdate = context.getState();
5580
- const icon = newFileIconCache[newTab.tab.uri] || '';
5581
- const stateWithIcon = updateIcon({
5582
- ...stateBeforeIconUpdate,
5583
- fileIconCache: newFileIconCache
5584
- }, tabId, icon);
5585
- return context.updateState(() => stateWithIcon);
5586
- } catch {
5587
- return undefined;
5588
- }
5589
- };
5590
5520
 
5591
5521
  const renderMainAreaPending = async (uid, applicationId) => {
5592
5522
  try {
@@ -5642,19 +5572,96 @@ const getViewletArgs = options => {
5642
5572
  })
5643
5573
  }, ...rest];
5644
5574
  };
5575
+ const handleOpenError = async (context, tabId, editorInput, uri, error) => {
5576
+ const latestState = context.getState();
5577
+ if (editorInput.type === 'editor' && error instanceof Error && error.name === 'BinaryFileError') {
5578
+ const editorUid = findTabById(latestState, tabId)?.tab.editorUid ?? -1;
5579
+ const binaryState = updateTab(latestState, tabId, {
5580
+ editorInput: {
5581
+ type: 'binary',
5582
+ uri
5583
+ },
5584
+ editorUid: -1,
5585
+ errorMessage: undefined,
5586
+ loadingState: 'binary'
5587
+ });
5588
+ await context.updateState(() => binaryState);
5589
+ if (editorUid !== -1) {
5590
+ await disposeEditors([editorUid]);
5591
+ }
5592
+ return;
5593
+ }
5594
+ const errorMessage = error instanceof Error ? error.message : 'Failed to open URI';
5595
+ const errorState = updateTab(latestState, tabId, {
5596
+ errorMessage,
5597
+ loadingState: 'error'
5598
+ });
5599
+ await context.updateState(() => errorState);
5600
+ };
5601
+ const findReusableTab = (state, options, uri) => {
5602
+ return options.reuseExisting === false ? undefined : findTabByUri(state, uri);
5603
+ };
5604
+ const getStateWithIcon = (state, tabId, uri, icon) => {
5605
+ const stateWithIcon = updateTab(state, tabId, {
5606
+ icon
5607
+ });
5608
+ return {
5609
+ ...stateWithIcon,
5610
+ fileIconCache: icon && !getFileIcon(uri) ? {
5611
+ ...stateWithIcon.fileIconCache,
5612
+ [uri]: icon
5613
+ } : stateWithIcon.fileIconCache
5614
+ };
5615
+ };
5616
+ const disposePreviewEditor = async editorUid => {
5617
+ if (editorUid !== -1) {
5618
+ await disposeEditors([editorUid]);
5619
+ }
5620
+ };
5621
+ const isSupersededPreview = (state, initialState, requestId, preview) => {
5622
+ return preview && (state.openRequestId !== requestId || getActiveTabId(state) !== getActiveTabId(initialState));
5623
+ };
5645
5624
  const openInputWithContext = async (context, options) => {
5646
- const state = context.getState();
5647
- object(state);
5625
+ object(context.getState());
5648
5626
  object(options);
5627
+ const requestId = getNextRequestId();
5628
+ await context.updateState(state => ({
5629
+ ...state,
5630
+ openRequestId: requestId
5631
+ }));
5632
+ const state = context.getState();
5649
5633
  const {
5650
5634
  editorInput
5651
5635
  } = options;
5652
5636
  const preview = options.preview ?? false;
5653
5637
  const uri = getEditorInputUri(editorInput);
5654
5638
  const title = getEditorInputTitle(editorInput);
5655
- const currentState = state;
5656
- const existingTab = options.reuseExisting === false ? undefined : findTabByUri(currentState, uri);
5657
- const shouldRetry = shouldRetryExistingTab(existingTab, editorInput, options.forceOpen === true);
5639
+ let currentState = state;
5640
+ let existingTab = findReusableTab(currentState, options, uri);
5641
+ let shouldRetry = shouldRetryExistingTab(existingTab, editorInput, options.forceOpen === true);
5642
+ let icon = '';
5643
+ let {
5644
+ iconThemeVersion
5645
+ } = state;
5646
+ if (!existingTab || shouldRetry) {
5647
+ const {
5648
+ icon: resolvedIcon,
5649
+ state: {
5650
+ iconThemeVersion: resolvedIconThemeVersion
5651
+ }
5652
+ } = await prepareTabIcon(context, uri);
5653
+ icon = resolvedIcon;
5654
+ iconThemeVersion = resolvedIconThemeVersion;
5655
+ currentState = context.getState();
5656
+ existingTab = findReusableTab(currentState, options, uri);
5657
+ shouldRetry = shouldRetryExistingTab(existingTab, editorInput, options.forceOpen === true);
5658
+ }
5659
+ if (isSupersededPreview(currentState, state, requestId, preview)) {
5660
+ return;
5661
+ }
5662
+ if (currentState.iconThemeVersion !== iconThemeVersion) {
5663
+ return openInputWithContext(context, options);
5664
+ }
5658
5665
  if (existingTab && !shouldRetry) {
5659
5666
  const switchedState = getExistingTabState(currentState, existingTab, preview);
5660
5667
  await context.updateState(() => switchedState);
@@ -5668,10 +5675,8 @@ const openInputWithContext = async (context, options) => {
5668
5675
  stateWithTab,
5669
5676
  tabId
5670
5677
  } = getStateWithTab(currentState, editorInput, existingTab, shouldRetry, uri, preview, title);
5671
- await context.updateState(() => stateWithTab);
5672
- if (replacedEditorUid !== -1) {
5673
- await disposeEditors([replacedEditorUid]);
5674
- }
5678
+ await context.updateState(() => getStateWithIcon(stateWithTab, tabId, uri, icon));
5679
+ await disposePreviewEditor(replacedEditorUid);
5675
5680
  if (await isDirectoryEditorInput(editorInput, state.applicationId)) {
5676
5681
  const latestState = context.getState();
5677
5682
  const errorState = updateTab(latestState, tabId, {
@@ -5744,18 +5749,14 @@ const openInputWithContext = async (context, options) => {
5744
5749
  readyState = handleViewletReady(context.getState(), editorUid, renderedTitle);
5745
5750
  await context.updateState(() => readyState);
5746
5751
  }
5747
- const stateWithIcon = await updateTabIcon(context, readyState, tabId);
5748
- if (stateWithIcon) {
5749
- return;
5750
- }
5752
+ // Earlier visibility commands can run before the final editor DOM is committed.
5753
+ // Request a final scroll without changing the tab DOM or fetching its icon again.
5754
+ await context.updateState(state => ({
5755
+ ...state,
5756
+ activeTabVisibilityVersion: (state.activeTabVisibilityVersion ?? 0) + 1
5757
+ }));
5751
5758
  } catch (error) {
5752
- const latestState = context.getState();
5753
- const errorMessage = error instanceof Error ? error.message : 'Failed to open URI';
5754
- const errorState = updateTab(latestState, tabId, {
5755
- errorMessage,
5756
- loadingState: 'error'
5757
- });
5758
- await context.updateState(() => errorState);
5759
+ await handleOpenError(context, tabId, editorInput, uri, error);
5759
5760
  }
5760
5761
  };
5761
5762
  const openInput = async (state, options) => {
@@ -7162,6 +7163,70 @@ const handleHeaderDoubleClick = async (state, className, groupIndexRaw) => {
7162
7163
  return newFile(state);
7163
7164
  };
7164
7165
 
7166
+ const getIconsCached = (dirents, fileIconCache) => {
7167
+ return dirents.map(dirent => fileIconCache[dirent]);
7168
+ };
7169
+
7170
+ const getBasename = uri => {
7171
+ const lastSlashIndex = uri.lastIndexOf('/');
7172
+ if (lastSlashIndex === -1) {
7173
+ return uri;
7174
+ }
7175
+ return uri.slice(lastSlashIndex + 1);
7176
+ };
7177
+ const getMissingTabs = (tabs, fileIconCache) => {
7178
+ const missingTabs = [];
7179
+ for (const tab of tabs) {
7180
+ if (tab.uri && !getFileIcon(tab.uri) && !(tab.uri in fileIconCache)) {
7181
+ missingTabs.push(tab);
7182
+ }
7183
+ }
7184
+ return missingTabs;
7185
+ };
7186
+ const tabToIconRequest = tab => {
7187
+ const uri = tab.uri || '';
7188
+ return {
7189
+ name: getBasename(uri),
7190
+ path: uri,
7191
+ type: File
7192
+ };
7193
+ };
7194
+ const getMissingIconRequestsForTabs = (tabs, fileIconCache) => {
7195
+ const missingRequests = getMissingTabs(tabs, fileIconCache);
7196
+ const iconRequests = missingRequests.map(tabToIconRequest);
7197
+ return iconRequests;
7198
+ };
7199
+
7200
+ const updateIconCache = (iconCache, missingRequests, newIcons) => {
7201
+ if (missingRequests.length === 0) {
7202
+ return iconCache;
7203
+ }
7204
+ const newFileIconCache = {
7205
+ ...iconCache
7206
+ };
7207
+ for (let i = 0; i < missingRequests.length; i++) {
7208
+ const request = missingRequests[i];
7209
+ const icon = newIcons[i];
7210
+ newFileIconCache[request.path] = icon;
7211
+ }
7212
+ return newFileIconCache;
7213
+ };
7214
+
7215
+ const getUri = tab => {
7216
+ return tab.uri || '';
7217
+ };
7218
+ const getFileIconsForTabs = async (tabs, fileIconCache) => {
7219
+ const missingRequests = getMissingIconRequestsForTabs(tabs, fileIconCache);
7220
+ const newIcons = await requestFileIcons(missingRequests);
7221
+ const newFileIconCache = updateIconCache(fileIconCache, missingRequests, newIcons);
7222
+ const tabUris = tabs.map(getUri);
7223
+ const icons = getIconsCached(tabUris, newFileIconCache);
7224
+ return {
7225
+ icons,
7226
+ newFileIconCache
7227
+ };
7228
+ };
7229
+
7165
7230
  const getAllTabs$1 = layout => {
7166
7231
  const {
7167
7232
  groups
@@ -7236,6 +7301,7 @@ const handleIconThemeChange = async state => {
7236
7301
  return {
7237
7302
  ...state,
7238
7303
  fileIconCache,
7304
+ iconThemeVersion: (state.iconThemeVersion ?? 0) + 1,
7239
7305
  layout: updatedLayout
7240
7306
  };
7241
7307
  };
@@ -8843,11 +8909,14 @@ const applyRender = (oldState, newState, diffResult) => {
8843
8909
  return commands;
8844
8910
  };
8845
8911
 
8846
- const render2 = (uid, diffResult) => {
8912
+ const render2 = (uid, _diffResult) => {
8847
8913
  const {
8848
8914
  newState,
8849
8915
  oldState
8850
8916
  } = get$1(uid);
8917
+ // State can change between the caller's diff2 RPC and this render2 RPC.
8918
+ // Diff and render the same snapshot before marking it as rendered.
8919
+ const diffResult = diff(oldState, newState);
8851
8920
  set$1(uid, newState, newState);
8852
8921
  const commands = applyRender(oldState, newState, diffResult);
8853
8922
  if (!isConnected()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/main-area-worker",
3
- "version": "9.74.0",
3
+ "version": "9.74.2",
4
4
  "description": "Main Area Worker",
5
5
  "repository": {
6
6
  "type": "git",