@lvce-editor/main-area-worker 8.22.0 → 9.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.
@@ -2153,35 +2153,62 @@ const getNextRequestId = () => {
2153
2153
  return ++requestIdCounter;
2154
2154
  };
2155
2155
 
2156
- const startContentLoading = async (oldState, state, tabId, path, requestId) => {
2156
+ const getViewletModuleId = async uri => {
2157
+ // Query RendererWorker for viewlet module ID (optional, may fail in tests)
2158
+ let viewletModuleId;
2157
2159
  try {
2158
- const getLatestState = () => {
2159
- return get(state.uid).newState;
2160
- };
2161
- set(state.uid, oldState, state);
2162
- const newState = await loadTabContentAsync(tabId, path, requestId, getLatestState);
2163
- return newState;
2160
+ viewletModuleId = await invoke('Layout.getModuleId', uri);
2164
2161
  } catch {
2165
- // Silently ignore errors - the tab may have been closed or the component unmounted
2162
+ // Viewlet creation is optional - silently ignore if RendererWorker isn't available
2166
2163
  }
2167
- return state;
2164
+ return viewletModuleId;
2168
2165
  };
2169
2166
 
2170
- const shouldLoadContent = tab => {
2171
- // Load if:
2172
- // - Has a path (file-based tab)
2173
- // - Not already loaded or currently loading
2167
+ const DiffEditor = 'DiffEditor';
2168
+ const ExtensionDetail = 'ExtensionDetail';
2169
+ const QuickPick = 'QuickPick';
2170
+
2171
+ const getViewletModuleIdForEditorInput = async editorInput => {
2172
+ switch (editorInput.type) {
2173
+ case 'diff-editor':
2174
+ return DiffEditor;
2175
+ case 'editor':
2176
+ return getViewletModuleId(editorInput.uri);
2177
+ case 'extension-detail-view':
2178
+ return ExtensionDetail;
2179
+ }
2180
+ };
2181
+
2182
+ const shouldLoadContentForTab = tab => {
2183
+ if (tab.editorInput && tab.editorInput.type !== 'editor') {
2184
+ return false;
2185
+ }
2174
2186
  if (!tab.uri) {
2175
2187
  return false;
2176
2188
  }
2177
2189
  if (tab.loadingState === 'loading') {
2178
2190
  return false;
2179
2191
  }
2180
- if (tab.loadingState === 'loaded') {
2192
+ if (tab.loadingState === 'loaded' && tab.editorUid !== -1) {
2181
2193
  return false;
2182
2194
  }
2183
2195
  return true;
2184
2196
  };
2197
+
2198
+ const startContentLoading = async (oldState, state, tabId, path, requestId) => {
2199
+ try {
2200
+ const getLatestState = () => {
2201
+ return get(state.uid).newState;
2202
+ };
2203
+ set(state.uid, oldState, state);
2204
+ const newState = await loadTabContentAsync(tabId, path, requestId, getLatestState);
2205
+ return newState;
2206
+ } catch {
2207
+ // Silently ignore errors - the tab may have been closed or the component unmounted
2208
+ }
2209
+ return state;
2210
+ };
2211
+
2185
2212
  const getActiveTabId$1 = state => {
2186
2213
  const {
2187
2214
  layout
@@ -2223,7 +2250,7 @@ const selectTab = async (state, groupIndex, index) => {
2223
2250
  getActiveTabId$1(state);
2224
2251
 
2225
2252
  // Check if we need to load content for the newly selected tab
2226
- const needsLoading = shouldLoadContent(tab);
2253
+ const needsLoading = shouldLoadContentForTab(tab);
2227
2254
  const requestId = needsLoading ? getNextRequestId() : 0;
2228
2255
 
2229
2256
  // Update the groups array with the new active tab and active group
@@ -2272,10 +2299,8 @@ const selectTab = async (state, groupIndex, index) => {
2272
2299
 
2273
2300
  // If new tab's viewlet isn't ready yet, trigger creation (idempotent)
2274
2301
  const newTab = newState.layout.groups[groupIndex].tabs[index];
2275
- if (newTab.uri && (!newTab.loadingState || newTab.loadingState === 'loading')) {
2276
- // Query RendererWorker for viewlet module ID
2277
-
2278
- const viewletModuleId = await invoke('Layout.getModuleId', newTab.uri);
2302
+ if (newTab.uri && (newTab.editorUid === -1 || !newTab.loadingState || newTab.loadingState === 'loading')) {
2303
+ const viewletModuleId = newTab.editorInput ? await getViewletModuleIdForEditorInput(newTab.editorInput) : await invoke('Layout.getModuleId', newTab.uri);
2279
2304
  if (viewletModuleId) {
2280
2305
  // Calculate bounds: use main area bounds minus 35px for tab height
2281
2306
  const TAB_HEIGHT = 35;
@@ -2475,7 +2500,7 @@ const getLabel = uri => {
2475
2500
  return getBasename$1(uri);
2476
2501
  };
2477
2502
 
2478
- const createEmptyGroup = (state, uri, requestId, preview = false) => {
2503
+ const createEmptyGroup = (state, uri, requestId, preview = false, title = getLabel(uri), editorType = 'text', editorInput) => {
2479
2504
  const {
2480
2505
  layout
2481
2506
  } = state;
@@ -2483,11 +2508,11 @@ const createEmptyGroup = (state, uri, requestId, preview = false) => {
2483
2508
  groups
2484
2509
  } = layout;
2485
2510
  const groupId = create$1();
2486
- const title = getLabel(uri);
2487
2511
  const tabId = create$1();
2488
2512
  const editorUid = create$1();
2489
2513
  const newTab = {
2490
- editorType: 'text',
2514
+ editorInput,
2515
+ editorType,
2491
2516
  editorUid,
2492
2517
  errorMessage: '',
2493
2518
  icon: '',
@@ -2553,7 +2578,7 @@ const openTab = (state, groupId, tab) => {
2553
2578
  };
2554
2579
  };
2555
2580
 
2556
- const ensureActiveGroup = (state, uri, preview = false) => {
2581
+ const ensureActiveGroup = (state, uri, preview = false, title = getLabel(uri), editorType = 'text', editorInput) => {
2557
2582
  // Find the active group (by activeGroupId or focused flag)
2558
2583
  const {
2559
2584
  layout
@@ -2572,7 +2597,6 @@ const ensureActiveGroup = (state, uri, preview = false) => {
2572
2597
  if (activeGroup) {
2573
2598
  const activeTab = activeGroup.tabs.find(tab => tab.id === activeGroup.activeTabId);
2574
2599
  if (activeTab?.isPreview) {
2575
- const title = getLabel(uri);
2576
2600
  const updatedGroups = groups.map(group => {
2577
2601
  if (group.id !== activeGroup.id) {
2578
2602
  return group;
@@ -2583,6 +2607,8 @@ const ensureActiveGroup = (state, uri, preview = false) => {
2583
2607
  }
2584
2608
  return {
2585
2609
  ...tab,
2610
+ editorInput,
2611
+ editorType,
2586
2612
  errorMessage: '',
2587
2613
  icon: '',
2588
2614
  isDirty: false,
@@ -2609,11 +2635,11 @@ const ensureActiveGroup = (state, uri, preview = false) => {
2609
2635
  }
2610
2636
 
2611
2637
  // Create a new tab with the URI in the active group
2612
- const title = getLabel(uri);
2613
2638
  const tabId = create$1();
2614
2639
  const editorUid = create$1();
2615
2640
  const newTab = {
2616
- editorType: 'text',
2641
+ editorInput,
2642
+ editorType,
2617
2643
  editorUid,
2618
2644
  errorMessage: '',
2619
2645
  icon: '',
@@ -2627,7 +2653,7 @@ const ensureActiveGroup = (state, uri, preview = false) => {
2627
2653
  };
2628
2654
  newState = openTab(state, activeGroup.id, newTab);
2629
2655
  } else {
2630
- newState = createEmptyGroup(state, uri, requestId, preview);
2656
+ newState = createEmptyGroup(state, uri, requestId, preview, title, editorType, editorInput);
2631
2657
  }
2632
2658
  return newState;
2633
2659
  };
@@ -2684,6 +2710,42 @@ const getActiveTabId = state => {
2684
2710
  return activeGroup?.activeTabId;
2685
2711
  };
2686
2712
 
2713
+ const getEditorInputEditorType = editorInput => {
2714
+ switch (editorInput.type) {
2715
+ case 'diff-editor':
2716
+ case 'extension-detail-view':
2717
+ return 'custom';
2718
+ case 'editor':
2719
+ return 'text';
2720
+ }
2721
+ };
2722
+
2723
+ const getEditorInputTitle = editorInput => {
2724
+ switch (editorInput.type) {
2725
+ case 'diff-editor':
2726
+ {
2727
+ const leftTitle = getLabel(editorInput.uriLeft);
2728
+ const rightTitle = getLabel(editorInput.uriRight);
2729
+ return `${leftTitle} - ${rightTitle}`;
2730
+ }
2731
+ case 'editor':
2732
+ return getLabel(editorInput.uri);
2733
+ case 'extension-detail-view':
2734
+ return editorInput.extensionId;
2735
+ }
2736
+ };
2737
+
2738
+ const getEditorInputUri = editorInput => {
2739
+ switch (editorInput.type) {
2740
+ case 'diff-editor':
2741
+ return `diff://?left=${encodeURIComponent(editorInput.uriLeft)}&right=${encodeURIComponent(editorInput.uriRight)}`;
2742
+ case 'editor':
2743
+ return editorInput.uri;
2744
+ case 'extension-detail-view':
2745
+ return `extension-detail://${editorInput.extensionId}`;
2746
+ }
2747
+ };
2748
+
2687
2749
  const getIconsCached = (dirents, fileIconCache) => {
2688
2750
  return dirents.map(dirent => fileIconCache[dirent]);
2689
2751
  };
@@ -2771,24 +2833,6 @@ const getFileIconsForTabs = async (tabs, fileIconCache) => {
2771
2833
  };
2772
2834
  };
2773
2835
 
2774
- const getOptionUriOptions = options => {
2775
- if (typeof options === 'string') {
2776
- return options;
2777
- }
2778
- return options.uri;
2779
- };
2780
-
2781
- const getViewletModuleId = async uri => {
2782
- // Query RendererWorker for viewlet module ID (optional, may fail in tests)
2783
- let viewletModuleId;
2784
- try {
2785
- viewletModuleId = await invoke('Layout.getModuleId', uri);
2786
- } catch {
2787
- // Viewlet creation is optional - silently ignore if RendererWorker isn't available
2788
- }
2789
- return viewletModuleId;
2790
- };
2791
-
2792
2836
  const switchTab = (state, groupId, tabId) => {
2793
2837
  const {
2794
2838
  layout
@@ -2817,46 +2861,39 @@ const switchTab = (state, groupId, tabId) => {
2817
2861
  };
2818
2862
  };
2819
2863
 
2820
- const openUri = async (state, options) => {
2864
+ const openInput = async (state, options) => {
2821
2865
  object(state);
2866
+ object(options);
2822
2867
  const {
2823
2868
  uid
2824
2869
  } = state;
2825
- const uri = getOptionUriOptions(options);
2826
- const preview = typeof options === 'string' ? false : options.preview ?? false;
2827
-
2828
- // Check if a tab with this URI already exists in the passed-in state
2870
+ const {
2871
+ editorInput
2872
+ } = options;
2873
+ const preview = options.preview ?? false;
2874
+ const uri = getEditorInputUri(editorInput);
2875
+ const title = getEditorInputTitle(editorInput);
2876
+ const editorType = getEditorInputEditorType(editorInput);
2829
2877
  const existingTab = findTabByUri(state, uri);
2830
2878
  const shouldRetryExistingTab = existingTab && existingTab.tab.loadingState === 'error';
2831
2879
  if (existingTab && !shouldRetryExistingTab) {
2832
- // Tab exists, switch to it and focus its group
2833
2880
  const focusedState = focusEditorGroup(state, existingTab.groupId);
2834
2881
  return switchTab(focusedState, existingTab.groupId, existingTab.tab.id);
2835
2882
  }
2836
-
2837
- // Get previous active tab ID for viewlet switching
2838
2883
  const previousTabId = getActiveTabId(state);
2839
-
2840
- // Check if there's existing state in the global store
2841
2884
  const stateFromStore = get(uid);
2842
2885
  let currentState;
2843
2886
  if (stateFromStore) {
2844
2887
  const storedState = stateFromStore.newState;
2845
- // Use the stored state if it has more tabs than the passed-in state
2846
- // (indicating concurrent calls have already added tabs)
2847
- // Otherwise use the passed-in state (test setup with initial data)
2848
- const storedTabCount = storedState.layout.groups.reduce((sum, g) => sum + g.tabs.length, 0);
2849
- const passedTabCount = state.layout.groups.reduce((sum, g) => sum + g.tabs.length, 0);
2888
+ const storedTabCount = storedState.layout.groups.reduce((sum, group) => sum + group.tabs.length, 0);
2889
+ const passedTabCount = state.layout.groups.reduce((sum, group) => sum + group.tabs.length, 0);
2850
2890
  if (storedTabCount > passedTabCount) {
2851
- // Stored state has more tabs - concurrent calls have added tabs
2852
2891
  currentState = storedState;
2853
2892
  } else {
2854
- // Passed-in state has same or more tabs, use it (likely fresh test setup)
2855
2893
  currentState = state;
2856
2894
  set(uid, state, state);
2857
2895
  }
2858
2896
  } else {
2859
- // No state in store yet, register the passed-in state
2860
2897
  currentState = state;
2861
2898
  set(uid, state, state);
2862
2899
  }
@@ -2865,22 +2902,20 @@ const openUri = async (state, options) => {
2865
2902
  if (shouldRetryExistingTab && existingTab) {
2866
2903
  const focusedState = focusEditorGroup(currentState, existingTab.groupId);
2867
2904
  stateWithTab = updateTab(focusedState, existingTab.tab.id, {
2905
+ editorInput,
2868
2906
  errorMessage: '',
2869
- loadingState: 'loading'
2907
+ loadingState: 'loading',
2908
+ title,
2909
+ uri
2870
2910
  });
2871
2911
  tabId = existingTab.tab.id;
2872
2912
  } else {
2873
- // Add tab to state BEFORE any async calls to prevent race conditions
2874
- stateWithTab = ensureActiveGroup(currentState, uri, preview);
2913
+ stateWithTab = ensureActiveGroup(currentState, uri, preview, title, editorType, editorInput);
2875
2914
  tabId = getActiveTabId(stateWithTab);
2876
2915
  }
2877
-
2878
- // Save state immediately after adding tab or retrying
2879
2916
  set(uid, state, stateWithTab);
2880
2917
  try {
2881
- const viewletModuleId = await getViewletModuleId(uri);
2882
-
2883
- // After async call, get the latest state to account for any concurrent changes
2918
+ const viewletModuleId = await getViewletModuleIdForEditorInput(editorInput);
2884
2919
  const {
2885
2920
  newState: stateAfterModuleId
2886
2921
  } = get(uid);
@@ -2890,8 +2925,6 @@ const openUri = async (state, options) => {
2890
2925
  loadingState: 'error'
2891
2926
  });
2892
2927
  }
2893
-
2894
- // Calculate bounds: use main area bounds minus tab height
2895
2928
  const bounds = {
2896
2929
  height: stateAfterModuleId.height - stateAfterModuleId.tabHeight,
2897
2930
  width: stateAfterModuleId.width,
@@ -2899,55 +2932,38 @@ const openUri = async (state, options) => {
2899
2932
  y: stateAfterModuleId.y + stateAfterModuleId.tabHeight
2900
2933
  };
2901
2934
  const stateWithViewlet = createViewletForTab(stateAfterModuleId, tabId, viewletModuleId, bounds);
2902
- let intermediateState1 = stateWithViewlet;
2903
-
2904
- // Switch viewlet (detach old, attach new if ready)
2935
+ let intermediateState = stateWithViewlet;
2905
2936
  const {
2906
2937
  newState: switchedState
2907
- } = switchViewlet(intermediateState1, previousTabId, tabId);
2908
- intermediateState1 = switchedState;
2909
- set(uid, state, intermediateState1);
2910
-
2911
- // Get the tab to extract editorUid
2912
- const tabWithViewlet = findTabById(intermediateState1, tabId);
2938
+ } = switchViewlet(intermediateState, previousTabId, tabId);
2939
+ intermediateState = switchedState;
2940
+ set(uid, state, intermediateState);
2941
+ const tabWithViewlet = findTabById(intermediateState, tabId);
2913
2942
  if (!tabWithViewlet) {
2914
- return intermediateState1;
2943
+ return intermediateState;
2915
2944
  }
2916
2945
  const {
2917
2946
  editorUid
2918
2947
  } = tabWithViewlet.tab;
2919
2948
  if (editorUid === -1) {
2920
- throw new Error(`invalid editorUid`);
2949
+ throw new Error('invalid editorUid');
2921
2950
  }
2922
2951
  await createViewlet(viewletModuleId, editorUid, tabId, bounds, uri);
2923
-
2924
- // After viewlet is created, get the latest state and mark it as ready
2925
- // This ensures we have any state updates that occurred during viewlet creation
2926
2952
  const {
2927
2953
  newState: latestState
2928
2954
  } = get(uid);
2929
-
2930
- // Attachment is handled automatically by virtual DOM reference nodes
2931
2955
  const readyState = handleViewletReady(latestState, editorUid);
2932
-
2933
- // Save state before async icon request
2934
2956
  set(uid, state, readyState);
2935
-
2936
- // Request file icon for the newly opened tab
2937
2957
  try {
2938
2958
  const newTab = findTabById(readyState, tabId);
2939
2959
  if (newTab && newTab.tab.uri) {
2940
2960
  const {
2941
2961
  newFileIconCache
2942
2962
  } = await getFileIconsForTabs([newTab.tab], readyState.fileIconCache);
2943
-
2944
- // After async call, get the latest state again
2945
2963
  const {
2946
2964
  newState: stateBeforeIconUpdate
2947
2965
  } = get(uid);
2948
2966
  const icon = newFileIconCache[newTab.tab.uri] || '';
2949
-
2950
- // Update the tab with the icon in the latest state
2951
2967
  const stateWithIcon = {
2952
2968
  ...stateBeforeIconUpdate,
2953
2969
  fileIconCache: newFileIconCache,
@@ -2962,16 +2978,12 @@ const openUri = async (state, options) => {
2962
2978
  }))
2963
2979
  }
2964
2980
  };
2965
-
2966
- // Save the state with icon update so concurrent calls can see it
2967
2981
  set(uid, state, stateWithIcon);
2968
2982
  return stateWithIcon;
2969
2983
  }
2970
2984
  } catch {
2971
- // If icon request fails, continue without icon
2985
+ // ignore
2972
2986
  }
2973
-
2974
- // Get final latest state
2975
2987
  const {
2976
2988
  newState: finalState
2977
2989
  } = get(uid);
@@ -2990,6 +3002,26 @@ const openUri = async (state, options) => {
2990
3002
  }
2991
3003
  };
2992
3004
 
3005
+ const getOptionUriOptions = options => {
3006
+ if (typeof options === 'string') {
3007
+ return options;
3008
+ }
3009
+ return options.uri;
3010
+ };
3011
+
3012
+ const openUri = async (state, options) => {
3013
+ const uri = getOptionUriOptions(options);
3014
+ const preview = typeof options === 'string' ? false : options.preview ?? false;
3015
+ return openInput(state, {
3016
+ editorInput: {
3017
+ type: 'editor',
3018
+ uri
3019
+ },
3020
+ focu: typeof options === 'string' ? false : options.focu,
3021
+ preview
3022
+ });
3023
+ };
3024
+
2993
3025
  const retryOpen = async state => {
2994
3026
  const activeTabData = getActiveTab(state);
2995
3027
  if (!activeTabData) {
@@ -2998,6 +3030,13 @@ const retryOpen = async state => {
2998
3030
  const {
2999
3031
  tab
3000
3032
  } = activeTabData;
3033
+ if (tab.editorInput) {
3034
+ return openInput(state, {
3035
+ editorInput: tab.editorInput,
3036
+ focu: false,
3037
+ preview: tab.isPreview
3038
+ });
3039
+ }
3001
3040
  if (!tab.uri) {
3002
3041
  return state;
3003
3042
  }
@@ -3693,6 +3732,19 @@ const isValidMainAreaLayout = layout => {
3693
3732
  return true;
3694
3733
  };
3695
3734
 
3735
+ const normalizeRestoredTab = tab => {
3736
+ const {
3737
+ errorMessage: _errorMessage,
3738
+ loadingState: _loadingState,
3739
+ ...rest
3740
+ } = tab ?? {};
3741
+ return {
3742
+ ...rest,
3743
+ editorUid: -1,
3744
+ isDirty: false,
3745
+ isPreview: typeof tab?.isPreview === 'boolean' ? tab.isPreview : false
3746
+ };
3747
+ };
3696
3748
  const tryRestoreLayout = savedState => {
3697
3749
  if (savedState === undefined || savedState === null) {
3698
3750
  return undefined;
@@ -3727,12 +3779,7 @@ const tryRestoreLayout = savedState => {
3727
3779
  ...(groupDirection === undefined ? {} : {
3728
3780
  direction: groupDirection
3729
3781
  }),
3730
- tabs: Array.isArray(group?.tabs) ? group.tabs.map(tab => ({
3731
- ...tab,
3732
- editorUid: -1,
3733
- isDirty: false,
3734
- isPreview: typeof tab?.isPreview === 'boolean' ? tab.isPreview : false
3735
- })) : []
3782
+ tabs: Array.isArray(group?.tabs) ? group.tabs.map(normalizeRestoredTab) : []
3736
3783
  };
3737
3784
  })
3738
3785
  };
@@ -3762,8 +3809,8 @@ const getViewletModuleIds = async layout => {
3762
3809
  tabs
3763
3810
  } = group;
3764
3811
  const activeTab = tabs.find(tab => tab.id === group.activeTabId);
3765
- if (activeTab && activeTab.uri) {
3766
- const viewletModuleId = await getViewletModuleId(activeTab.uri);
3812
+ if (activeTab && (activeTab.editorInput || activeTab.uri)) {
3813
+ const viewletModuleId = activeTab.editorInput ? await getViewletModuleIdForEditorInput(activeTab.editorInput) : await getViewletModuleId(activeTab.uri);
3767
3814
  if (viewletModuleId) {
3768
3815
  viewletModuleIds[activeTab.id] = viewletModuleId;
3769
3816
  }
@@ -4006,8 +4053,6 @@ const menuEntrySeparator = {
4006
4053
  label: ''
4007
4054
  };
4008
4055
 
4009
- const QuickPick = 'QuickPick';
4010
-
4011
4056
  const getArgs = groupId => {
4012
4057
  if (groupId === undefined) {
4013
4058
  return undefined;
@@ -5258,6 +5303,7 @@ const commandMap = {
5258
5303
  'MainArea.initialize': initialize,
5259
5304
  'MainArea.loadContent': wrapCommand(loadContent),
5260
5305
  'MainArea.newFile': wrapCommand(newFile),
5306
+ 'MainArea.openInput': wrapCommand(openInput),
5261
5307
  'MainArea.openUri': wrapCommand(openUri),
5262
5308
  'MainArea.openUris': wrapCommand(openUris),
5263
5309
  'MainArea.refresh': wrapCommand(refresh),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/main-area-worker",
3
- "version": "8.22.0",
3
+ "version": "9.0.0",
4
4
  "description": "Main Area Worker",
5
5
  "repository": {
6
6
  "type": "git",