@lvce-editor/main-area-worker 9.5.0 → 9.7.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.
@@ -2155,7 +2155,7 @@ const getNextRequestId = () => {
2155
2155
  return ++requestIdCounter;
2156
2156
  };
2157
2157
 
2158
- const getViewletModuleId = async uri => {
2158
+ const getViewletModuleId$1 = async uri => {
2159
2159
  // Query RendererWorker for viewlet module ID (optional, may fail in tests)
2160
2160
  let viewletModuleId;
2161
2161
  try {
@@ -2175,7 +2175,7 @@ const getViewletModuleIdForEditorInput = async editorInput => {
2175
2175
  case 'diff-editor':
2176
2176
  return DiffEditor;
2177
2177
  case 'editor':
2178
- return getViewletModuleId(editorInput.uri);
2178
+ return getViewletModuleId$1(editorInput.uri);
2179
2179
  case 'extension-detail-view':
2180
2180
  return ExtensionDetail;
2181
2181
  }
@@ -2222,6 +2222,93 @@ const getActiveTabId$1 = state => {
2222
2222
  const activeGroup = groups.find(g => g.id === activeGroupId);
2223
2223
  return activeGroup?.activeTabId;
2224
2224
  };
2225
+ const getSelectedTabData = (state, groupIndex, index) => {
2226
+ const group = state.layout.groups[groupIndex];
2227
+ if (!group || index < 0 || index >= group.tabs.length) {
2228
+ return undefined;
2229
+ }
2230
+ const tab = group.tabs[index];
2231
+ return {
2232
+ group,
2233
+ groupId: group.id,
2234
+ tab,
2235
+ tabId: tab.id
2236
+ };
2237
+ };
2238
+ const getUpdatedGroups = (groups, groupIndex, needsLoading, tabId) => {
2239
+ return groups.map((group, index) => {
2240
+ if (index !== groupIndex) {
2241
+ return {
2242
+ ...group,
2243
+ focused: false
2244
+ };
2245
+ }
2246
+ const tabs = needsLoading ? group.tabs.map(tab => {
2247
+ if (tab.id !== tabId) {
2248
+ return tab;
2249
+ }
2250
+ return {
2251
+ ...tab,
2252
+ errorMessage: '',
2253
+ loadingState: 'loading'
2254
+ };
2255
+ }) : group.tabs;
2256
+ return {
2257
+ ...group,
2258
+ activeTabId: tabId,
2259
+ focused: true,
2260
+ tabs
2261
+ };
2262
+ });
2263
+ };
2264
+ const shouldCreateViewletForSelectedTab = tab => {
2265
+ return Boolean(tab.uri) && (tab.editorUid === -1 || !tab.loadingState || tab.loadingState === 'loading');
2266
+ };
2267
+ const getSelectedTabBounds = state => {
2268
+ return {
2269
+ height: state.height - state.tabHeight,
2270
+ width: state.width,
2271
+ x: state.x,
2272
+ y: state.y + state.tabHeight
2273
+ };
2274
+ };
2275
+ const getViewletModuleId = async tab => {
2276
+ return tab.editorInput ? getViewletModuleIdForEditorInput(tab.editorInput) : invoke('Layout.getModuleId', tab.uri);
2277
+ };
2278
+ const maybeStartLoading = async (state, newState, tabId, tab, needsLoading, requestId) => {
2279
+ if (needsLoading && tab.uri) {
2280
+ return startContentLoading(state, newState, tabId, tab.uri, requestId);
2281
+ }
2282
+ return newState;
2283
+ };
2284
+ const maybeCreateViewletForSelectedTab = async (state, newState, groupIndex, index, tabId, tab, uid, needsLoading, requestId, switchCommands) => {
2285
+ const selectedTab = newState.layout.groups[groupIndex].tabs[index];
2286
+ if (!shouldCreateViewletForSelectedTab(selectedTab)) {
2287
+ return undefined;
2288
+ }
2289
+ const viewletModuleId = await getViewletModuleId(selectedTab);
2290
+ if (!viewletModuleId) {
2291
+ return undefined;
2292
+ }
2293
+ const bounds = getSelectedTabBounds(newState);
2294
+ let stateWithViewlet = createViewletForTab(newState, tabId);
2295
+ set(uid, state, stateWithViewlet);
2296
+ if (switchCommands.length > 0) {
2297
+ await executeViewletCommands(switchCommands);
2298
+ }
2299
+ const tabWithViewlet = findTabById(stateWithViewlet, tabId);
2300
+ if (tabWithViewlet) {
2301
+ const {
2302
+ editorUid
2303
+ } = tabWithViewlet.tab;
2304
+ if (editorUid !== -1 && selectedTab.uri) {
2305
+ await createViewlet(viewletModuleId, editorUid, tabId, bounds, selectedTab.uri);
2306
+ stateWithViewlet = handleViewletReady(stateWithViewlet, editorUid);
2307
+ set(uid, state, stateWithViewlet);
2308
+ }
2309
+ }
2310
+ return maybeStartLoading(state, stateWithViewlet, tabId, tab, needsLoading, requestId);
2311
+ };
2225
2312
  const selectTab = async (state, groupIndex, index) => {
2226
2313
  const {
2227
2314
  layout,
@@ -2230,60 +2317,27 @@ const selectTab = async (state, groupIndex, index) => {
2230
2317
  const {
2231
2318
  groups
2232
2319
  } = layout;
2233
-
2234
- // Validate indexes
2235
2320
  if (groupIndex < 0 || groupIndex >= groups.length) {
2236
2321
  return state;
2237
2322
  }
2238
- const group = groups[groupIndex];
2239
- if (index < 0 || index >= group.tabs.length) {
2323
+ const selectedTabData = getSelectedTabData(state, groupIndex, index);
2324
+ if (!selectedTabData) {
2240
2325
  return state;
2241
2326
  }
2242
- const tab = group.tabs[index];
2243
- const groupId = group.id;
2244
- const tabId = tab.id;
2327
+ const {
2328
+ group,
2329
+ groupId,
2330
+ tab,
2331
+ tabId
2332
+ } = selectedTabData;
2245
2333
  const isAlreadyActive = layout.activeGroupId === groupId && group.activeTabId === tabId;
2246
-
2247
- // Allow restored tabs without a live editor to recover even if they are already selected.
2248
2334
  if (isAlreadyActive && !shouldLoadContentForTab(tab)) {
2249
2335
  return state;
2250
2336
  }
2251
-
2252
- // Get previous active tab ID for viewlet switching
2253
2337
  getActiveTabId$1(state);
2254
-
2255
- // Check if we need to load content for the newly selected tab
2256
2338
  const needsLoading = shouldLoadContentForTab(tab);
2257
2339
  const requestId = needsLoading ? getNextRequestId() : 0;
2258
-
2259
- // Update the groups array with the new active tab and active group
2260
- // Also set loading state if needed
2261
- const updatedGroups = groups.map((g, i) => {
2262
- if (i !== groupIndex) {
2263
- return {
2264
- ...g,
2265
- focused: false
2266
- };
2267
- }
2268
-
2269
- // This is the group being selected
2270
- const updatedTabs = needsLoading ? g.tabs.map(t => {
2271
- if (t.id === tabId) {
2272
- return {
2273
- ...t,
2274
- errorMessage: '',
2275
- loadingState: 'loading'
2276
- };
2277
- }
2278
- return t;
2279
- }) : g.tabs;
2280
- return {
2281
- ...g,
2282
- activeTabId: tabId,
2283
- focused: true,
2284
- tabs: updatedTabs
2285
- };
2286
- });
2340
+ const updatedGroups = getUpdatedGroups(groups, groupIndex, needsLoading, tabId);
2287
2341
  let newState = {
2288
2342
  ...state,
2289
2343
  layout: {
@@ -2292,75 +2346,20 @@ const selectTab = async (state, groupIndex, index) => {
2292
2346
  groups: updatedGroups
2293
2347
  }
2294
2348
  };
2295
-
2296
- // Switch viewlet: detach old, attach new (if ready)
2297
2349
  const {
2298
2350
  commands: switchCommands,
2299
2351
  newState: stateWithViewlet
2300
2352
  } = switchViewlet(newState);
2301
2353
  newState = stateWithViewlet;
2302
-
2303
- // If new tab's viewlet isn't ready yet, trigger creation (idempotent)
2304
- const newTab = newState.layout.groups[groupIndex].tabs[index];
2305
- if (newTab.uri && (newTab.editorUid === -1 || !newTab.loadingState || newTab.loadingState === 'loading')) {
2306
- const viewletModuleId = newTab.editorInput ? await getViewletModuleIdForEditorInput(newTab.editorInput) : await invoke('Layout.getModuleId', newTab.uri);
2307
- if (viewletModuleId) {
2308
- // Calculate bounds: use main area bounds minus 35px for tab height
2309
- const TAB_HEIGHT = 35;
2310
- const bounds = {
2311
- height: newState.height - TAB_HEIGHT,
2312
- width: newState.width,
2313
- x: newState.x,
2314
- y: newState.y + TAB_HEIGHT
2315
- };
2316
- const createdState = createViewletForTab(newState, tabId);
2317
- newState = createdState;
2318
-
2319
- // Store updated state before creating viewlet
2320
- set(uid, state, newState);
2321
-
2322
- // Execute viewlet commands if any
2323
- if (switchCommands.length > 0) {
2324
- await executeViewletCommands(switchCommands);
2325
- }
2326
-
2327
- // Get the tab to extract editorUid for viewlet creation
2328
- const tabWithViewlet = findTabById(newState, tabId);
2329
- if (tabWithViewlet) {
2330
- const {
2331
- editorUid
2332
- } = tabWithViewlet.tab;
2333
- if (editorUid !== -1 && newTab.uri) {
2334
- // Create the actual viewlet instance
2335
- await createViewlet(viewletModuleId, editorUid, tabId, bounds, newTab.uri);
2336
-
2337
- // Mark viewlet as ready
2338
- newState = handleViewletReady(newState, editorUid);
2339
- set(uid, state, newState);
2340
- }
2341
- }
2342
-
2343
- // Start loading content in the background if needed
2344
- if (needsLoading && tab.uri) {
2345
- const latestState = await startContentLoading(state, newState, tabId, tab.uri, requestId);
2346
- return latestState;
2347
- }
2348
- return newState;
2349
- }
2354
+ const maybeCreatedState = await maybeCreateViewletForSelectedTab(state, newState, groupIndex, index, tabId, tab, uid, needsLoading, requestId, switchCommands);
2355
+ if (maybeCreatedState) {
2356
+ return maybeCreatedState;
2350
2357
  }
2351
2358
  set(uid, state, newState);
2352
-
2353
- // Execute viewlet commands if any
2354
2359
  if (switchCommands.length > 0) {
2355
2360
  await executeViewletCommands(switchCommands);
2356
2361
  }
2357
-
2358
- // Start loading content in the background if needed
2359
- if (needsLoading && tab.uri) {
2360
- const latestState = await startContentLoading(state, newState, tabId, tab.uri, requestId);
2361
- return latestState;
2362
- }
2363
- return newState;
2362
+ return maybeStartLoading(state, newState, tabId, tab, needsLoading, requestId);
2364
2363
  };
2365
2364
 
2366
2365
  const focusNextTab = async state => {
@@ -2875,6 +2874,78 @@ const switchTab = (state, groupId, tabId) => {
2875
2874
  };
2876
2875
  };
2877
2876
 
2877
+ const getTabCount = state => {
2878
+ return state.layout.groups.reduce((sum, group) => sum + group.tabs.length, 0);
2879
+ };
2880
+ const getCurrentState = state => {
2881
+ const {
2882
+ uid
2883
+ } = state;
2884
+ const stateFromStore = get(uid);
2885
+ if (!stateFromStore) {
2886
+ set(uid, state, state);
2887
+ return state;
2888
+ }
2889
+ const storedState = stateFromStore.newState;
2890
+ if (getTabCount(storedState) > getTabCount(state)) {
2891
+ return storedState;
2892
+ }
2893
+ set(uid, state, state);
2894
+ return state;
2895
+ };
2896
+ const getStateWithTab = (currentState, editorInput, existingTab, shouldRetryExistingTab, uri, preview, title, editorType) => {
2897
+ if (shouldRetryExistingTab && existingTab) {
2898
+ const focusedState = focusEditorGroup(currentState, existingTab.groupId);
2899
+ return {
2900
+ stateWithTab: updateTab(focusedState, existingTab.tab.id, {
2901
+ editorInput,
2902
+ errorMessage: '',
2903
+ loadingState: 'loading',
2904
+ title,
2905
+ uri
2906
+ }),
2907
+ tabId: existingTab.tab.id
2908
+ };
2909
+ }
2910
+ const stateWithTab = ensureActiveGroup(currentState, uri, preview, title, editorType, editorInput);
2911
+ return {
2912
+ stateWithTab,
2913
+ tabId: getActiveTabId(stateWithTab)
2914
+ };
2915
+ };
2916
+ const updateTabIcon = async (uid, state, readyState, tabId) => {
2917
+ const newTab = findTabById(readyState, tabId);
2918
+ if (!newTab || !newTab.tab.uri) {
2919
+ return undefined;
2920
+ }
2921
+ try {
2922
+ const {
2923
+ newFileIconCache
2924
+ } = await getFileIconsForTabs([newTab.tab], readyState.fileIconCache);
2925
+ const {
2926
+ newState: stateBeforeIconUpdate
2927
+ } = get(uid);
2928
+ const icon = newFileIconCache[newTab.tab.uri] || '';
2929
+ const stateWithIcon = {
2930
+ ...stateBeforeIconUpdate,
2931
+ fileIconCache: newFileIconCache,
2932
+ layout: {
2933
+ ...stateBeforeIconUpdate.layout,
2934
+ groups: stateBeforeIconUpdate.layout.groups.map(group => ({
2935
+ ...group,
2936
+ tabs: group.tabs.map(tab => tab.id === tabId ? {
2937
+ ...tab,
2938
+ icon
2939
+ } : tab)
2940
+ }))
2941
+ }
2942
+ };
2943
+ set(uid, state, stateWithIcon);
2944
+ return stateWithIcon;
2945
+ } catch {
2946
+ return undefined;
2947
+ }
2948
+ };
2878
2949
  const openInput = async (state, options) => {
2879
2950
  object(state);
2880
2951
  object(options);
@@ -2889,44 +2960,17 @@ const openInput = async (state, options) => {
2889
2960
  const title = getEditorInputTitle(editorInput);
2890
2961
  const editorType = getEditorInputEditorType(editorInput);
2891
2962
  const existingTab = findTabByUri(state, uri);
2892
- const shouldRetryExistingTab = existingTab && existingTab.tab.loadingState === 'error';
2963
+ const shouldRetryExistingTab = !!existingTab && existingTab.tab.loadingState === 'error';
2893
2964
  if (existingTab && !shouldRetryExistingTab) {
2894
2965
  const focusedState = focusEditorGroup(state, existingTab.groupId);
2895
2966
  return switchTab(focusedState, existingTab.groupId, existingTab.tab.id);
2896
2967
  }
2897
2968
  const previousTabId = getActiveTabId(state);
2898
- const stateFromStore = get(uid);
2899
- let currentState;
2900
- if (stateFromStore) {
2901
- const storedState = stateFromStore.newState;
2902
- const storedTabCount = storedState.layout.groups.reduce((sum, group) => sum + group.tabs.length, 0);
2903
- const passedTabCount = state.layout.groups.reduce((sum, group) => sum + group.tabs.length, 0);
2904
- if (storedTabCount > passedTabCount) {
2905
- currentState = storedState;
2906
- } else {
2907
- currentState = state;
2908
- set(uid, state, state);
2909
- }
2910
- } else {
2911
- currentState = state;
2912
- set(uid, state, state);
2913
- }
2914
- let tabId;
2915
- let stateWithTab;
2916
- if (shouldRetryExistingTab && existingTab) {
2917
- const focusedState = focusEditorGroup(currentState, existingTab.groupId);
2918
- stateWithTab = updateTab(focusedState, existingTab.tab.id, {
2919
- editorInput,
2920
- errorMessage: '',
2921
- loadingState: 'loading',
2922
- title,
2923
- uri
2924
- });
2925
- tabId = existingTab.tab.id;
2926
- } else {
2927
- stateWithTab = ensureActiveGroup(currentState, uri, preview, title, editorType, editorInput);
2928
- tabId = getActiveTabId(stateWithTab);
2929
- }
2969
+ const currentState = getCurrentState(state);
2970
+ const {
2971
+ stateWithTab,
2972
+ tabId
2973
+ } = getStateWithTab(currentState, editorInput, existingTab, shouldRetryExistingTab, uri, preview, title, editorType);
2930
2974
  set(uid, state, stateWithTab);
2931
2975
  try {
2932
2976
  const viewletModuleId = await getViewletModuleIdForEditorInput(editorInput);
@@ -2968,35 +3012,9 @@ const openInput = async (state, options) => {
2968
3012
  } = get(uid);
2969
3013
  const readyState = handleViewletReady(latestState, editorUid);
2970
3014
  set(uid, state, readyState);
2971
- try {
2972
- const newTab = findTabById(readyState, tabId);
2973
- if (newTab && newTab.tab.uri) {
2974
- const {
2975
- newFileIconCache
2976
- } = await getFileIconsForTabs([newTab.tab], readyState.fileIconCache);
2977
- const {
2978
- newState: stateBeforeIconUpdate
2979
- } = get(uid);
2980
- const icon = newFileIconCache[newTab.tab.uri] || '';
2981
- const stateWithIcon = {
2982
- ...stateBeforeIconUpdate,
2983
- fileIconCache: newFileIconCache,
2984
- layout: {
2985
- ...stateBeforeIconUpdate.layout,
2986
- groups: stateBeforeIconUpdate.layout.groups.map(group => ({
2987
- ...group,
2988
- tabs: group.tabs.map(tab => tab.id === tabId ? {
2989
- ...tab,
2990
- icon
2991
- } : tab)
2992
- }))
2993
- }
2994
- };
2995
- set(uid, state, stateWithIcon);
2996
- return stateWithIcon;
2997
- }
2998
- } catch {
2999
- // ignore
3015
+ const stateWithIcon = await updateTabIcon(uid, state, readyState, tabId);
3016
+ if (stateWithIcon) {
3017
+ return stateWithIcon;
3000
3018
  }
3001
3019
  const {
3002
3020
  newState: finalState
@@ -3023,14 +3041,67 @@ const getOptionUriOptions = options => {
3023
3041
  return options.uri;
3024
3042
  };
3025
3043
 
3044
+ const getEditorInputFromUri = uri => {
3045
+ if (uri.startsWith('diff://?')) {
3046
+ try {
3047
+ const parsed = new URL(uri);
3048
+ const uriLeft = parsed.searchParams.get('left');
3049
+ const uriRight = parsed.searchParams.get('right');
3050
+ if (uriLeft && uriRight) {
3051
+ return {
3052
+ type: 'diff-editor',
3053
+ uriLeft,
3054
+ uriRight
3055
+ };
3056
+ }
3057
+ } catch {
3058
+ // Ignore malformed legacy URIs and fall back to a text editor input.
3059
+ }
3060
+ }
3061
+ if (uri.startsWith('extension-detail://')) {
3062
+ const extensionIdWithPath = uri.slice('extension-detail://'.length);
3063
+ const extensionId = extensionIdWithPath.split('/')[0];
3064
+ if (extensionId) {
3065
+ return {
3066
+ extensionId,
3067
+ type: 'extension-detail-view'
3068
+ };
3069
+ }
3070
+ }
3071
+ return {
3072
+ type: 'editor',
3073
+ uri
3074
+ };
3075
+ };
3076
+ const getNormalizedEditorInput = tab => {
3077
+ if (typeof tab?.uri === 'string') {
3078
+ const inferredEditorInput = getEditorInputFromUri(tab.uri);
3079
+ if (inferredEditorInput.type !== 'editor') {
3080
+ return inferredEditorInput;
3081
+ }
3082
+ }
3083
+ return tab?.editorInput;
3084
+ };
3085
+ const normalizeTabEditorInput = tab => {
3086
+ const editorInput = getNormalizedEditorInput(tab);
3087
+ if (!editorInput) {
3088
+ return tab;
3089
+ }
3090
+ return {
3091
+ ...tab,
3092
+ editorInput,
3093
+ editorType: getEditorInputEditorType(editorInput)
3094
+ };
3095
+ };
3096
+ const getNormalizedOpenEditorInput = uri => {
3097
+ return getEditorInputFromUri(uri);
3098
+ };
3099
+
3026
3100
  const openUri = async (state, options) => {
3027
3101
  const uri = getOptionUriOptions(options);
3028
3102
  const preview = typeof options === 'string' ? false : options.preview ?? false;
3029
3103
  return openInput(state, {
3030
- editorInput: {
3031
- type: 'editor',
3032
- uri
3033
- },
3104
+ editorInput: getNormalizedOpenEditorInput(uri),
3034
3105
  focu: typeof options === 'string' ? false : options.focu,
3035
3106
  preview
3036
3107
  });
@@ -3751,8 +3822,9 @@ const normalizeRestoredTab = tab => {
3751
3822
  loadingState: _loadingState,
3752
3823
  ...rest
3753
3824
  } = tab ?? {};
3825
+ const normalizedTab = normalizeTabEditorInput(rest);
3754
3826
  return {
3755
- ...rest,
3827
+ ...normalizedTab,
3756
3828
  editorUid: -1,
3757
3829
  isDirty: false,
3758
3830
  isPreview: typeof tab?.isPreview === 'boolean' ? tab.isPreview : false
@@ -3823,7 +3895,7 @@ const getViewletModuleIds = async layout => {
3823
3895
  } = group;
3824
3896
  const activeTab = tabs.find(tab => tab.id === group.activeTabId);
3825
3897
  if (activeTab && (activeTab.editorInput || activeTab.uri)) {
3826
- const viewletModuleId = activeTab.editorInput ? await getViewletModuleIdForEditorInput(activeTab.editorInput) : await getViewletModuleId(activeTab.uri);
3898
+ const viewletModuleId = activeTab.editorInput ? await getViewletModuleIdForEditorInput(activeTab.editorInput) : await getViewletModuleId$1(activeTab.uri);
3827
3899
  if (viewletModuleId) {
3828
3900
  viewletModuleIds[activeTab.id] = viewletModuleId;
3829
3901
  }
@@ -5004,6 +5076,12 @@ const renderSingleEditorGroup = (layout, splitButtonEnabled, sizeProperty = 'wid
5004
5076
  }, ...renderEditorGroup(layout.groups[0], 0, splitButtonEnabled, sizeProperty)];
5005
5077
  };
5006
5078
 
5079
+ const getDirectionClassName = (direction, isSplit) => {
5080
+ if (!isSplit) {
5081
+ return '';
5082
+ }
5083
+ return direction === Horizontal ? EditorGroupsVertical : EditorGroupsHorizontal;
5084
+ };
5007
5085
  const getMainAreaVirtualDom = (layout, splitButtonEnabled = false, width = 0) => {
5008
5086
  const {
5009
5087
  direction,
@@ -5015,7 +5093,7 @@ const getMainAreaVirtualDom = (layout, splitButtonEnabled = false, width = 0) =>
5015
5093
  }
5016
5094
  const children = [];
5017
5095
  const isSplit = groups.length > 1;
5018
- const directionClassName = isSplit ? direction === Horizontal ? EditorGroupsVertical : EditorGroupsHorizontal : '';
5096
+ const directionClassName = getDirectionClassName(direction, isSplit);
5019
5097
  const editorGroupsContainerClassName = directionClassName ? `${EDITOR_GROUPS_CONTAINER} ${directionClassName}` : EDITOR_GROUPS_CONTAINER;
5020
5098
  if (groups.length === 0) {
5021
5099
  return [{
@@ -5181,7 +5259,7 @@ const save = async state => {
5181
5259
  const getFilteredGroups = groups => {
5182
5260
  return groups.map(group => ({
5183
5261
  ...group,
5184
- tabs: group.tabs.filter(tab => !tab.uri?.startsWith('untitled://'))
5262
+ tabs: group.tabs.filter(tab => !tab.uri?.startsWith('untitled://')).map(normalizeTabEditorInput)
5185
5263
  })).filter(group => group.tabs.length > 0);
5186
5264
  };
5187
5265
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/main-area-worker",
3
- "version": "9.5.0",
3
+ "version": "9.7.0",
4
4
  "description": "Main Area Worker",
5
5
  "repository": {
6
6
  "type": "git",