@lvce-editor/main-area-worker 9.15.0 → 9.17.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.
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
1
  # Main Area Worker
2
2
 
3
3
  WebWorker for the main area functionality in Lvce Editor.
4
+
5
+ <!-- -->
@@ -13,13 +13,13 @@ const create$f = () => {
13
13
  },
14
14
  diff(uid, modules, numbers) {
15
15
  const {
16
- newState,
17
- oldState
16
+ oldState,
17
+ scheduledState
18
18
  } = states[uid];
19
19
  const diffResult = [];
20
20
  for (let i = 0; i < modules.length; i++) {
21
21
  const fn = modules[i];
22
- if (!fn(oldState, newState)) {
22
+ if (!fn(oldState, scheduledState)) {
23
23
  diffResult.push(numbers[i]);
24
24
  }
25
25
  }
@@ -37,17 +37,16 @@ const create$f = () => {
37
37
  return ids;
38
38
  },
39
39
  getKeys() {
40
- return Object.keys(states).map(key => {
41
- return Number.parseFloat(key);
42
- });
40
+ return Object.keys(states).map(Number);
43
41
  },
44
42
  registerCommands(commandMap) {
45
43
  Object.assign(commandMapRef, commandMap);
46
44
  },
47
- set(uid, oldState, newState) {
45
+ set(uid, oldState, newState, scheduledState) {
48
46
  states[uid] = {
49
47
  newState,
50
- oldState
48
+ oldState,
49
+ scheduledState: scheduledState ?? newState
51
50
  };
52
51
  },
53
52
  wrapCommand(fn) {
@@ -67,7 +66,8 @@ const create$f = () => {
67
66
  };
68
67
  states[uid] = {
69
68
  newState: latestNew,
70
- oldState: latestOld.oldState
69
+ oldState: latestOld.oldState,
70
+ scheduledState: latestNew
71
71
  };
72
72
  };
73
73
  return wrapped;
@@ -104,7 +104,8 @@ const create$f = () => {
104
104
  };
105
105
  states[uid] = {
106
106
  newState: latestNew,
107
- oldState: latestOld.oldState
107
+ oldState: latestOld.oldState,
108
+ scheduledState: latestNew
108
109
  };
109
110
  return {
110
111
  error
@@ -820,8 +821,10 @@ const getCurrentStack = () => {
820
821
  const currentStack = joinLines(splitLines(new Error().stack || '').slice(stackLinesToSkip));
821
822
  return currentStack;
822
823
  };
823
- const getNewLineIndex = (string, startIndex = undefined) => {
824
- return string.indexOf(NewLine, startIndex);
824
+ const getNewLineIndex = (string, startIndex) => {
825
+ {
826
+ return string.indexOf(NewLine);
827
+ }
825
828
  };
826
829
  const getParentStack = error => {
827
830
  let parentStack = error.stack || error.data || error.message || '';
@@ -832,55 +835,77 @@ const getParentStack = error => {
832
835
  };
833
836
  const MethodNotFound = -32601;
834
837
  const Custom = -32001;
838
+ const restoreExistingError = (error, currentStack) => {
839
+ if (typeof error.stack === 'string') {
840
+ error.stack = error.stack + NewLine + currentStack;
841
+ }
842
+ return error;
843
+ };
844
+ const restoreMethodNotFoundError = (error, currentStack) => {
845
+ const restoredError = new JsonRpcError(error.message);
846
+ const parentStack = getParentStack(error);
847
+ restoredError.stack = parentStack + NewLine + currentStack;
848
+ return restoredError;
849
+ };
850
+ const restoreStackFromData = (restoredError, error, currentStack) => {
851
+ if (error.data.stack && error.data.type && error.message) {
852
+ restoredError.stack = error.data.type + ': ' + error.message + NewLine + error.data.stack + NewLine + currentStack;
853
+ return;
854
+ }
855
+ if (error.data.stack) {
856
+ restoredError.stack = error.data.stack;
857
+ }
858
+ };
859
+ const applyDataProperties = (restoredError, error) => {
860
+ if (!error.data) {
861
+ return;
862
+ }
863
+ restoreStackFromData(restoredError, error, getCurrentStack());
864
+ if (error.data.codeFrame) {
865
+ // @ts-ignore
866
+ restoredError.codeFrame = error.data.codeFrame;
867
+ }
868
+ if (error.data.code) {
869
+ // @ts-ignore
870
+ restoredError.code = error.data.code;
871
+ }
872
+ if (error.data.type) {
873
+ // @ts-ignore
874
+ restoredError.name = error.data.type;
875
+ }
876
+ };
877
+ const applyDirectProperties = (restoredError, error) => {
878
+ if (error.stack) {
879
+ const lowerStack = restoredError.stack || '';
880
+ const indexNewLine = getNewLineIndex(lowerStack);
881
+ const parentStack = getParentStack(error);
882
+ // @ts-ignore
883
+ restoredError.stack = parentStack + lowerStack.slice(indexNewLine);
884
+ }
885
+ if (error.codeFrame) {
886
+ // @ts-ignore
887
+ restoredError.codeFrame = error.codeFrame;
888
+ }
889
+ };
890
+ const restoreMessageError = (error, _currentStack) => {
891
+ const restoredError = constructError(error.message, error.type, error.name);
892
+ if (error.data) {
893
+ applyDataProperties(restoredError, error);
894
+ } else {
895
+ applyDirectProperties(restoredError, error);
896
+ }
897
+ return restoredError;
898
+ };
835
899
  const restoreJsonRpcError = error => {
836
900
  const currentStack = getCurrentStack();
837
901
  if (error && error instanceof Error) {
838
- if (typeof error.stack === 'string') {
839
- error.stack = error.stack + NewLine + currentStack;
840
- }
841
- return error;
902
+ return restoreExistingError(error, currentStack);
842
903
  }
843
904
  if (error && error.code && error.code === MethodNotFound) {
844
- const restoredError = new JsonRpcError(error.message);
845
- const parentStack = getParentStack(error);
846
- restoredError.stack = parentStack + NewLine + currentStack;
847
- return restoredError;
905
+ return restoreMethodNotFoundError(error, currentStack);
848
906
  }
849
907
  if (error && error.message) {
850
- const restoredError = constructError(error.message, error.type, error.name);
851
- if (error.data) {
852
- if (error.data.stack && error.data.type && error.message) {
853
- restoredError.stack = error.data.type + ': ' + error.message + NewLine + error.data.stack + NewLine + currentStack;
854
- } else if (error.data.stack) {
855
- restoredError.stack = error.data.stack;
856
- }
857
- if (error.data.codeFrame) {
858
- // @ts-ignore
859
- restoredError.codeFrame = error.data.codeFrame;
860
- }
861
- if (error.data.code) {
862
- // @ts-ignore
863
- restoredError.code = error.data.code;
864
- }
865
- if (error.data.type) {
866
- // @ts-ignore
867
- restoredError.name = error.data.type;
868
- }
869
- } else {
870
- if (error.stack) {
871
- const lowerStack = restoredError.stack || '';
872
- // @ts-ignore
873
- const indexNewLine = getNewLineIndex(lowerStack);
874
- const parentStack = getParentStack(error);
875
- // @ts-ignore
876
- restoredError.stack = parentStack + lowerStack.slice(indexNewLine);
877
- }
878
- if (error.codeFrame) {
879
- // @ts-ignore
880
- restoredError.codeFrame = error.codeFrame;
881
- }
882
- }
883
- return restoredError;
908
+ return restoreMessageError(error);
884
909
  }
885
910
  if (typeof error === 'string') {
886
911
  return new Error(`JsonRpc Error: ${error}`);
@@ -1606,7 +1631,7 @@ const closeTabAndSave = async (state, groupId, tabId) => {
1606
1631
  if (!tab) {
1607
1632
  return state;
1608
1633
  }
1609
- if (tab.editorUid !== -1) {
1634
+ if (tab.editorUid !== -1 && tab.isDirty) {
1610
1635
  const editorState = await saveEditor(tab.editorUid);
1611
1636
  if (!editorState?.modified && tab.uri) {
1612
1637
  await handleModifiedStatusChange$1(tab.uri, false);
@@ -1799,21 +1824,23 @@ const closeSaved$1 = state => {
1799
1824
  return withGroups(addClosedTabs(state, closedTabs), newGroups);
1800
1825
  };
1801
1826
 
1827
+ const closeFirstTabByUri = (state, uri) => {
1828
+ const matchingGroup = state.layout.groups.find(group => {
1829
+ return group.tabs.some(tab => tab.uri === uri);
1830
+ });
1831
+ const matchingTab = matchingGroup?.tabs.find(tab => tab.uri === uri);
1832
+ if (!matchingGroup || !matchingTab) {
1833
+ return undefined;
1834
+ }
1835
+ return closeTab(state, matchingGroup.id, matchingTab.id);
1836
+ };
1802
1837
  const closeTabsByUris = (state, uris) => {
1803
1838
  let currentState = state;
1804
1839
  for (const uri of uris) {
1805
- while (true) {
1806
- const matchingGroup = currentState.layout.groups.find(group => {
1807
- return group.tabs.some(tab => tab.uri === uri);
1808
- });
1809
- if (!matchingGroup) {
1810
- break;
1811
- }
1812
- const matchingTab = matchingGroup.tabs.find(tab => tab.uri === uri);
1813
- if (!matchingTab) {
1814
- break;
1815
- }
1816
- currentState = closeTab(currentState, matchingGroup.id, matchingTab.id);
1840
+ let nextState = closeFirstTabByUri(currentState, uri);
1841
+ while (nextState) {
1842
+ currentState = nextState;
1843
+ nextState = closeFirstTabByUri(currentState, uri);
1817
1844
  }
1818
1845
  }
1819
1846
  return currentState;
@@ -1924,8 +1951,8 @@ const create$2 = (uid, uri, x, y, width, height, platform, assetDir, tabHeight =
1924
1951
  direction: Horizontal,
1925
1952
  groups: []
1926
1953
  },
1927
- maxOpenEditorGroups: Number.POSITIVE_INFINITY,
1928
- maxOpenEditors: Number.POSITIVE_INFINITY,
1954
+ maxOpenEditorGroups: Infinity,
1955
+ maxOpenEditors: Infinity,
1929
1956
  minGroupWidthPx: 250,
1930
1957
  platform,
1931
1958
  splitButtonEnabled: false,
@@ -1974,196 +2001,6 @@ const diff2 = uid => {
1974
2001
  return result;
1975
2002
  };
1976
2003
 
1977
- const getActiveTabId$1 = state => {
1978
- const {
1979
- layout
1980
- } = state;
1981
- const {
1982
- activeGroupId,
1983
- groups
1984
- } = layout;
1985
- const activeGroup = groups.find(group => group.id === activeGroupId);
1986
- return activeGroup?.activeTabId;
1987
- };
1988
-
1989
- const getSelectedTabBounds = state => {
1990
- return {
1991
- height: state.height - state.tabHeight,
1992
- width: state.width,
1993
- x: state.x,
1994
- y: state.y + state.tabHeight
1995
- };
1996
- };
1997
-
1998
- const getSelectedTabData = (state, groupIndex, index) => {
1999
- const group = state.layout.groups[groupIndex];
2000
- if (!group || index < 0 || index >= group.tabs.length) {
2001
- return undefined;
2002
- }
2003
- const tab = group.tabs[index];
2004
- return {
2005
- group,
2006
- groupId: group.id,
2007
- tab,
2008
- tabId: tab.id
2009
- };
2010
- };
2011
-
2012
- const getUpdatedGroups = (groups, groupIndex, needsLoading, tabId) => {
2013
- return groups.map((group, index) => {
2014
- if (index !== groupIndex) {
2015
- return {
2016
- ...group,
2017
- focused: false
2018
- };
2019
- }
2020
- const tabs = needsLoading ? group.tabs.map(tab => {
2021
- if (tab.id !== tabId) {
2022
- return tab;
2023
- }
2024
- return {
2025
- ...tab,
2026
- errorMessage: '',
2027
- loadingState: 'loading'
2028
- };
2029
- }) : group.tabs;
2030
- return {
2031
- ...group,
2032
- activeTabId: tabId,
2033
- focused: true,
2034
- tabs
2035
- };
2036
- });
2037
- };
2038
-
2039
- const getViewletModuleId$1 = async uri => {
2040
- // Query RendererWorker for viewlet module ID (optional, may fail in tests)
2041
- let viewletModuleId;
2042
- try {
2043
- viewletModuleId = await invoke('Layout.getModuleId', uri);
2044
- } catch {
2045
- // Viewlet creation is optional - silently ignore if RendererWorker isn't available
2046
- }
2047
- return viewletModuleId;
2048
- };
2049
-
2050
- const DiffEditor = 'DiffEditor';
2051
- const EditorText = 'Editor';
2052
- const ExtensionDetail = 'ExtensionDetail';
2053
- const QuickPick = 'QuickPick';
2054
-
2055
- const getViewletModuleIdForEditorInput = async editorInput => {
2056
- switch (editorInput.type) {
2057
- case 'diff-editor':
2058
- return DiffEditor;
2059
- case 'editor':
2060
- return getViewletModuleId$1(editorInput.uri);
2061
- case 'extension-detail-view':
2062
- return ExtensionDetail;
2063
- }
2064
- };
2065
-
2066
- const getEditorInputEditorType = editorInput => {
2067
- switch (editorInput.type) {
2068
- case 'diff-editor':
2069
- case 'extension-detail-view':
2070
- return 'custom';
2071
- case 'editor':
2072
- return 'text';
2073
- }
2074
- };
2075
-
2076
- const getEditorInputUri = editorInput => {
2077
- switch (editorInput.type) {
2078
- case 'diff-editor':
2079
- return `diff://?left=${encodeURIComponent(editorInput.uriLeft)}&right=${encodeURIComponent(editorInput.uriRight)}`;
2080
- case 'editor':
2081
- return editorInput.uri;
2082
- case 'extension-detail-view':
2083
- return `extension-detail://${editorInput.extensionId}`;
2084
- }
2085
- };
2086
-
2087
- const getEditorInputFromUri = uri => {
2088
- if (uri.startsWith('diff://?')) {
2089
- try {
2090
- const parsed = new URL(uri);
2091
- const uriLeft = parsed.searchParams.get('left');
2092
- const uriRight = parsed.searchParams.get('right');
2093
- if (uriLeft && uriRight) {
2094
- return {
2095
- type: 'diff-editor',
2096
- uriLeft,
2097
- uriRight
2098
- };
2099
- }
2100
- } catch {
2101
- // Ignore malformed legacy URIs and fall back to a text editor input.
2102
- }
2103
- }
2104
- if (uri.startsWith('extension-detail://')) {
2105
- const extensionIdWithPath = uri.slice('extension-detail://'.length);
2106
- const extensionId = extensionIdWithPath.split('/')[0];
2107
- if (extensionId) {
2108
- return {
2109
- extensionId,
2110
- type: 'extension-detail-view'
2111
- };
2112
- }
2113
- }
2114
- return {
2115
- type: 'editor',
2116
- uri
2117
- };
2118
- };
2119
- const getNormalizedEditorInput = tab => {
2120
- const {
2121
- editorInput: tabEditorInput,
2122
- uri: tabUri
2123
- } = tab ?? {};
2124
- let uri;
2125
- if (typeof tabUri === 'string') {
2126
- uri = tabUri;
2127
- } else if (typeof tabEditorInput?.uri === 'string') {
2128
- const {
2129
- uri: tabEditorInputUri
2130
- } = tabEditorInput;
2131
- uri = tabEditorInputUri;
2132
- }
2133
- if (uri) {
2134
- const inferredEditorInput = getEditorInputFromUri(uri);
2135
- if (inferredEditorInput.type !== 'editor') {
2136
- return inferredEditorInput;
2137
- }
2138
- }
2139
- return tab?.editorInput;
2140
- };
2141
- const normalizeTabEditorInput = tab => {
2142
- const editorInput = getNormalizedEditorInput(tab);
2143
- if (!editorInput) {
2144
- return tab;
2145
- }
2146
- const uri = typeof tab?.uri === 'string' ? tab.uri : getEditorInputUri(editorInput);
2147
- return {
2148
- ...tab,
2149
- editorInput,
2150
- editorType: getEditorInputEditorType(editorInput),
2151
- uri
2152
- };
2153
- };
2154
- const getNormalizedOpenEditorInput = uri => {
2155
- return getEditorInputFromUri(uri);
2156
- };
2157
-
2158
- const getViewletModuleId = async tab => {
2159
- const normalizedTab = normalizeTabEditorInput(tab);
2160
- return normalizedTab.editorInput ? getViewletModuleIdForEditorInput(normalizedTab.editorInput) : invoke('Layout.getModuleId', normalizedTab.uri);
2161
- };
2162
-
2163
- const createViewlet = async (viewletModuleId, editorUid, tabId, bounds, uri) => {
2164
- await invoke('Layout.createViewlet', viewletModuleId, editorUid, tabId, bounds, uri);
2165
- };
2166
-
2167
2004
  const handleAttach = async command => {
2168
2005
  // TODO find a better way to append editors
2169
2006
  const parentNodeSelector = '.editor-groups-container, .EditorGroup';
@@ -2321,103 +2158,326 @@ const switchViewlet = (state, fromTabId, toTabId) => {
2321
2158
  };
2322
2159
  };
2323
2160
 
2324
- const findTabByEditorUid = (state, editorUid) => {
2161
+ const findTabByEditorUid = (state, editorUid) => {
2162
+ const {
2163
+ layout
2164
+ } = state;
2165
+ const {
2166
+ groups
2167
+ } = layout;
2168
+ for (const group of groups) {
2169
+ const tab = group.tabs.find(t => t.editorUid === editorUid);
2170
+ if (tab) {
2171
+ return tab;
2172
+ }
2173
+ }
2174
+ return undefined;
2175
+ };
2176
+
2177
+ /**
2178
+ * Called when renderer reports viewlet finished creating.
2179
+ * With reference nodes, attachment is handled automatically by virtual DOM rendering.
2180
+ * No commands needed - state update is sufficient.
2181
+ */
2182
+ const handleViewletReady = (state, editorUid) => {
2183
+ if (editorUid === -1) {
2184
+ throw new Error('Invalid editorUid -1');
2185
+ }
2186
+ // Find the tab by viewletRequestId
2187
+ const tab = findTabByEditorUid(state, editorUid);
2188
+ if (!tab) {
2189
+ // Tab was closed, dispose viewlet
2190
+ return state;
2191
+ }
2192
+
2193
+ // Mark viewlet as ready and set instance id
2194
+ // Reference nodes will handle rendering at the correct position automatically
2195
+ const newState = updateTab(state, tab.id, {
2196
+ editorUid,
2197
+ loadingState: 'loaded'
2198
+ });
2199
+
2200
+ // No attach commands needed - virtual DOM reference nodes handle positioning
2201
+ return newState;
2202
+ };
2203
+
2204
+ const handleCreate = async command => {
2205
+ // Safe to call - no visible side effects
2206
+
2207
+ const instanceId = Math.random(); // TODO try to find a better way to get consistent integer ids (thread safe)
2208
+
2209
+ await invoke('Layout.createViewlet', command.viewletModuleId, command.tabId, command.bounds, command.uri, instanceId);
2210
+
2211
+ // After viewlet is created, mark it as ready
2212
+ // Attachment is handled automatically by virtual DOM reference nodes
2213
+ const {
2214
+ newState: state,
2215
+ oldState
2216
+ } = get(command.uid);
2217
+ const readyState = handleViewletReady(state, command.editorUid);
2218
+ set(command.uid, oldState, readyState);
2219
+ return readyState;
2220
+ };
2221
+
2222
+ const handleDetach = async command => {
2223
+ // Hides viewlet but keeps it alive
2224
+
2225
+ await invoke('Viewlet.detach', command.instanceId);
2226
+ };
2227
+
2228
+ const handleDispose = async command => {
2229
+ // Fully destroys viewlet
2230
+
2231
+ await invoke('Viewlet.dispose', command.instanceId);
2232
+ };
2233
+
2234
+ const handleSetBounds = async command => {
2235
+ await invoke('Viewlet.setBounds', command.instanceId, command.bounds);
2236
+ };
2237
+
2238
+ const executeViewletCommand = async command => {
2239
+ switch (command.type) {
2240
+ case 'attach':
2241
+ await handleAttach(command);
2242
+ return;
2243
+ case 'create':
2244
+ await handleCreate(command);
2245
+ return;
2246
+ case 'detach':
2247
+ await handleDetach(command);
2248
+ return;
2249
+ case 'dispose':
2250
+ await handleDispose(command);
2251
+ return;
2252
+ case 'setBounds':
2253
+ await handleSetBounds(command);
2254
+ return;
2255
+ }
2256
+ };
2257
+ const executeViewletCommands = async commands => {
2258
+ for (const command of commands) {
2259
+ await executeViewletCommand(command);
2260
+ }
2261
+ };
2262
+
2263
+ const requestIdState = {
2264
+ counter: 0
2265
+ };
2266
+ const getNextRequestId = () => {
2267
+ return ++requestIdState.counter;
2268
+ };
2269
+
2270
+ const getEditorInputEditorType = editorInput => {
2271
+ switch (editorInput.type) {
2272
+ case 'diff-editor':
2273
+ case 'extension-detail-view':
2274
+ case 'image':
2275
+ case 'video':
2276
+ return 'custom';
2277
+ case 'editor':
2278
+ return 'text';
2279
+ }
2280
+ };
2281
+
2282
+ const getEditorInputUri = editorInput => {
2283
+ switch (editorInput.type) {
2284
+ case 'diff-editor':
2285
+ return `diff://?left=${encodeURIComponent(editorInput.uriLeft)}&right=${encodeURIComponent(editorInput.uriRight)}`;
2286
+ case 'editor':
2287
+ case 'image':
2288
+ case 'video':
2289
+ return editorInput.uri;
2290
+ case 'extension-detail-view':
2291
+ return `extension-detail://${editorInput.extensionId}`;
2292
+ }
2293
+ };
2294
+
2295
+ const imageExtensions = new Set(['.avif', '.bmp', '.gif', '.ico', '.jpeg', '.jpg', '.png', '.svg', '.tif', '.tiff', '.webp']);
2296
+ const videoExtensions = new Set(['.avi', '.m4v', '.mkv', '.mov', '.mp4', '.mpeg', '.mpg', '.ogv', '.webm']);
2297
+ const getPathEndIndex = pathName => {
2298
+ const queryIndex = pathName.indexOf('?');
2299
+ const hashIndex = pathName.indexOf('#');
2300
+ if (queryIndex === -1) {
2301
+ return hashIndex === -1 ? pathName.length : hashIndex;
2302
+ }
2303
+ if (hashIndex === -1) {
2304
+ return queryIndex;
2305
+ }
2306
+ return Math.min(queryIndex, hashIndex);
2307
+ };
2308
+ const getPathName = uri => {
2309
+ if (uri.startsWith('file://')) {
2310
+ try {
2311
+ return new URL(uri).pathname;
2312
+ } catch {
2313
+ return uri;
2314
+ }
2315
+ }
2316
+ return uri;
2317
+ };
2318
+ const getLowerCaseExtension = uri => {
2319
+ const pathName = getPathName(uri);
2320
+ const endIndex = getPathEndIndex(pathName);
2321
+ const cleanPath = pathName.slice(0, endIndex);
2322
+ const lastDotIndex = cleanPath.lastIndexOf('.');
2323
+ const lastSlashIndex = cleanPath.lastIndexOf('/');
2324
+ if (lastDotIndex === -1 || lastDotIndex < lastSlashIndex) {
2325
+ return '';
2326
+ }
2327
+ return cleanPath.slice(lastDotIndex).toLowerCase();
2328
+ };
2329
+ const getEditorInputFromUri = uri => {
2330
+ if (uri.startsWith('diff://?')) {
2331
+ try {
2332
+ const parsed = new URL(uri);
2333
+ const uriLeft = parsed.searchParams.get('left');
2334
+ const uriRight = parsed.searchParams.get('right');
2335
+ if (uriLeft && uriRight) {
2336
+ return {
2337
+ type: 'diff-editor',
2338
+ uriLeft,
2339
+ uriRight
2340
+ };
2341
+ }
2342
+ } catch {
2343
+ // Ignore malformed legacy URIs and fall back to a text editor input.
2344
+ }
2345
+ }
2346
+ if (uri.startsWith('extension-detail://')) {
2347
+ const extensionIdWithPath = uri.slice('extension-detail://'.length);
2348
+ const extensionId = extensionIdWithPath.split('/', 1)[0];
2349
+ if (extensionId) {
2350
+ return {
2351
+ extensionId,
2352
+ type: 'extension-detail-view'
2353
+ };
2354
+ }
2355
+ }
2356
+ const extension = getLowerCaseExtension(uri);
2357
+ if (imageExtensions.has(extension)) {
2358
+ return {
2359
+ type: 'image',
2360
+ uri
2361
+ };
2362
+ }
2363
+ if (videoExtensions.has(extension)) {
2364
+ return {
2365
+ type: 'video',
2366
+ uri
2367
+ };
2368
+ }
2369
+ return {
2370
+ type: 'editor',
2371
+ uri
2372
+ };
2373
+ };
2374
+ const getNormalizedEditorInput = tab => {
2375
+ const {
2376
+ editorInput: tabEditorInput,
2377
+ uri: tabUri
2378
+ } = tab ?? {};
2379
+ let uri;
2380
+ if (typeof tabUri === 'string') {
2381
+ uri = tabUri;
2382
+ } else if (typeof tabEditorInput?.uri === 'string') {
2383
+ const {
2384
+ uri: tabEditorInputUri
2385
+ } = tabEditorInput;
2386
+ uri = tabEditorInputUri;
2387
+ }
2388
+ if (uri) {
2389
+ const inferredEditorInput = getEditorInputFromUri(uri);
2390
+ if (inferredEditorInput.type !== 'editor') {
2391
+ return inferredEditorInput;
2392
+ }
2393
+ }
2394
+ return tab?.editorInput;
2395
+ };
2396
+ const normalizeTabEditorInput = tab => {
2397
+ const editorInput = getNormalizedEditorInput(tab);
2398
+ if (!editorInput) {
2399
+ return tab;
2400
+ }
2401
+ const uri = typeof tab?.uri === 'string' ? tab.uri : getEditorInputUri(editorInput);
2402
+ return {
2403
+ ...tab,
2404
+ editorInput,
2405
+ editorType: getEditorInputEditorType(editorInput),
2406
+ uri
2407
+ };
2408
+ };
2409
+ const getNormalizedOpenEditorInput = uri => {
2410
+ return getEditorInputFromUri(uri);
2411
+ };
2412
+
2413
+ const shouldLoadContentForTab = tab => {
2414
+ if (tab.editorInput && tab.editorInput.type !== 'editor') {
2415
+ return false;
2416
+ }
2417
+ if (!tab.uri) {
2418
+ return false;
2419
+ }
2420
+ if (tab.loadingState === 'loading') {
2421
+ return false;
2422
+ }
2423
+ return tab.loadingState !== 'loaded' || tab.editorUid === -1;
2424
+ };
2425
+
2426
+ const getActiveTabId$1 = state => {
2325
2427
  const {
2326
2428
  layout
2327
2429
  } = state;
2328
2430
  const {
2431
+ activeGroupId,
2329
2432
  groups
2330
2433
  } = layout;
2331
- for (const group of groups) {
2332
- const tab = group.tabs.find(t => t.editorUid === editorUid);
2333
- if (tab) {
2334
- return tab;
2335
- }
2336
- }
2337
- return undefined;
2434
+ const activeGroup = groups.find(group => group.id === activeGroupId);
2435
+ return activeGroup?.activeTabId;
2338
2436
  };
2339
2437
 
2340
- /**
2341
- * Called when renderer reports viewlet finished creating.
2342
- * With reference nodes, attachment is handled automatically by virtual DOM rendering.
2343
- * No commands needed - state update is sufficient.
2344
- */
2345
- const handleViewletReady = (state, editorUid) => {
2346
- if (editorUid === -1) {
2347
- throw new Error('Invalid editorUid -1');
2348
- }
2349
- // Find the tab by viewletRequestId
2350
- const tab = findTabByEditorUid(state, editorUid);
2351
- if (!tab) {
2352
- // Tab was closed, dispose viewlet
2353
- return state;
2438
+ const getSelectedTabData = (state, groupIndex, index) => {
2439
+ const group = state.layout.groups[groupIndex];
2440
+ if (!group || index < 0 || index >= group.tabs.length) {
2441
+ return undefined;
2354
2442
  }
2355
-
2356
- // Mark viewlet as ready and set instance id
2357
- // Reference nodes will handle rendering at the correct position automatically
2358
- const newState = updateTab(state, tab.id, {
2359
- editorUid,
2360
- loadingState: 'loaded'
2361
- });
2362
-
2363
- // No attach commands needed - virtual DOM reference nodes handle positioning
2364
- return newState;
2365
- };
2366
-
2367
- const handleCreate = async command => {
2368
- // Safe to call - no visible side effects
2369
-
2370
- const instanceId = Math.random(); // TODO try to find a better way to get consistent integer ids (thread safe)
2371
-
2372
- await invoke('Layout.createViewlet', command.viewletModuleId, command.tabId, command.bounds, command.uri, instanceId);
2373
-
2374
- // After viewlet is created, mark it as ready
2375
- // Attachment is handled automatically by virtual DOM reference nodes
2376
- const {
2377
- newState: state,
2378
- oldState
2379
- } = get(command.uid);
2380
- const readyState = handleViewletReady(state, command.editorUid);
2381
- set(command.uid, oldState, readyState);
2382
- return readyState;
2383
- };
2384
-
2385
- const handleDetach = async command => {
2386
- // Hides viewlet but keeps it alive
2387
-
2388
- await invoke('Viewlet.detach', command.instanceId);
2389
- };
2390
-
2391
- const handleDispose = async command => {
2392
- // Fully destroys viewlet
2393
-
2394
- await invoke('Viewlet.dispose', command.instanceId);
2443
+ const tab = group.tabs[index];
2444
+ return {
2445
+ group,
2446
+ groupId: group.id,
2447
+ tab,
2448
+ tabId: tab.id
2449
+ };
2395
2450
  };
2396
2451
 
2397
- const handleSetBounds = async command => {
2398
- await invoke('Viewlet.setBounds', command.instanceId, command.bounds);
2452
+ const getUpdatedGroups = (groups, groupIndex, needsLoading, tabId) => {
2453
+ return groups.map((group, index) => {
2454
+ if (index !== groupIndex) {
2455
+ return {
2456
+ ...group,
2457
+ focused: false
2458
+ };
2459
+ }
2460
+ const tabs = needsLoading ? group.tabs.map(tab => {
2461
+ if (tab.id !== tabId) {
2462
+ return tab;
2463
+ }
2464
+ return {
2465
+ ...tab,
2466
+ errorMessage: '',
2467
+ loadingState: 'loading'
2468
+ };
2469
+ }) : group.tabs;
2470
+ return {
2471
+ ...group,
2472
+ activeTabId: tabId,
2473
+ focused: true,
2474
+ tabs
2475
+ };
2476
+ });
2399
2477
  };
2400
2478
 
2401
- const executeViewletCommands = async commands => {
2402
- for (const command of commands) {
2403
- switch (command.type) {
2404
- case 'attach':
2405
- await handleAttach(command);
2406
- break;
2407
- case 'create':
2408
- await handleCreate(command);
2409
- break;
2410
- case 'detach':
2411
- await handleDetach(command);
2412
- break;
2413
- case 'dispose':
2414
- await handleDispose(command);
2415
- break;
2416
- case 'setBounds':
2417
- await handleSetBounds(command);
2418
- break;
2419
- }
2420
- }
2479
+ const createViewlet = async (viewletModuleId, editorUid, tabId, bounds, uri) => {
2480
+ await invoke('Layout.createViewlet', viewletModuleId, editorUid, tabId, bounds, uri);
2421
2481
  };
2422
2482
 
2423
2483
  const findTabById = (state, tabId) => {
@@ -2439,6 +2499,49 @@ const findTabById = (state, tabId) => {
2439
2499
  return undefined;
2440
2500
  };
2441
2501
 
2502
+ const getSelectedTabBounds = state => {
2503
+ return {
2504
+ height: state.height - state.tabHeight,
2505
+ width: state.width,
2506
+ x: state.x,
2507
+ y: state.y + state.tabHeight
2508
+ };
2509
+ };
2510
+
2511
+ const getViewletModuleId$1 = async uri => {
2512
+ // Query RendererWorker for viewlet module ID (optional, may fail in tests)
2513
+ let viewletModuleId;
2514
+ try {
2515
+ viewletModuleId = await invoke('Layout.getModuleId', uri);
2516
+ } catch {
2517
+ // Viewlet creation is optional - silently ignore if RendererWorker isn't available
2518
+ }
2519
+ return viewletModuleId;
2520
+ };
2521
+
2522
+ const DiffEditor = 'DiffEditor';
2523
+ const EditorText = 'Editor';
2524
+ const ExtensionDetail = 'ExtensionDetail';
2525
+ const QuickPick = 'QuickPick';
2526
+
2527
+ const getViewletModuleIdForEditorInput = async editorInput => {
2528
+ switch (editorInput.type) {
2529
+ case 'diff-editor':
2530
+ return DiffEditor;
2531
+ case 'editor':
2532
+ case 'image':
2533
+ case 'video':
2534
+ return getViewletModuleId$1(editorInput.uri);
2535
+ case 'extension-detail-view':
2536
+ return ExtensionDetail;
2537
+ }
2538
+ };
2539
+
2540
+ const getViewletModuleId = async tab => {
2541
+ const normalizedTab = normalizeTabEditorInput(tab);
2542
+ return normalizedTab.editorInput ? getViewletModuleIdForEditorInput(normalizedTab.editorInput) : invoke('Layout.getModuleId', normalizedTab.uri);
2543
+ };
2544
+
2442
2545
  const startContentLoading = async (oldState, state, tabId, path, requestId) => {
2443
2546
  try {
2444
2547
  const getLatestState = () => {
@@ -2493,28 +2596,6 @@ const maybeCreateViewletForSelectedTab = async (state, newState, groupIndex, ind
2493
2596
  return maybeStartLoading(state, stateWithViewlet, tabId, tab, needsLoading, requestId);
2494
2597
  };
2495
2598
 
2496
- // Counter for request IDs to handle race conditions
2497
- let requestIdCounter = 0;
2498
- const getNextRequestId = () => {
2499
- return ++requestIdCounter;
2500
- };
2501
-
2502
- const shouldLoadContentForTab = tab => {
2503
- if (tab.editorInput && tab.editorInput.type !== 'editor') {
2504
- return false;
2505
- }
2506
- if (!tab.uri) {
2507
- return false;
2508
- }
2509
- if (tab.loadingState === 'loading') {
2510
- return false;
2511
- }
2512
- if (tab.loadingState === 'loaded' && tab.editorUid !== -1) {
2513
- return false;
2514
- }
2515
- return true;
2516
- };
2517
-
2518
2599
  const selectTab = async (state, groupIndex, index) => {
2519
2600
  const {
2520
2601
  layout,
@@ -2629,7 +2710,7 @@ const focusPreviousTab = async state => {
2629
2710
  }
2630
2711
 
2631
2712
  // If not at the first tab, select the previous tab, otherwise cycle to last
2632
- const previousTabIndex = activeTabIndex > 0 ? activeTabIndex - 1 : activeGroup.tabs.length - 1;
2713
+ const previousTabIndex = (activeTabIndex > 0 ? activeTabIndex : activeGroup.tabs.length) - 1;
2633
2714
  return selectTab(state, activeGroupIndex, previousTabIndex);
2634
2715
  };
2635
2716
 
@@ -2691,7 +2772,7 @@ const parseRawGroupId = rawGroupId => {
2691
2772
  if (!rawGroupId) {
2692
2773
  return undefined;
2693
2774
  }
2694
- const groupId = Number.parseFloat(rawGroupId);
2775
+ const groupId = Number(rawGroupId);
2695
2776
  return Number.isNaN(groupId) ? undefined : groupId;
2696
2777
  };
2697
2778
 
@@ -2965,6 +3046,9 @@ const getLabel = uri => {
2965
3046
  if (uri.startsWith('language-models://')) {
2966
3047
  return 'Language Models';
2967
3048
  }
3049
+ if (uri.startsWith('process-explorer://')) {
3050
+ return 'Process Explorer';
3051
+ }
2968
3052
  if (uri.startsWith(inlineDiffPrefix)) {
2969
3053
  return getInlineDiffLabel(uri);
2970
3054
  }
@@ -2980,6 +3064,8 @@ const getEditorInputTitle = editorInput => {
2980
3064
  return `${leftTitle} - ${rightTitle}`;
2981
3065
  }
2982
3066
  case 'editor':
3067
+ case 'image':
3068
+ case 'video':
2983
3069
  return getLabel(editorInput.uri);
2984
3070
  case 'extension-detail-view':
2985
3071
  return editorInput.extensionId;
@@ -3755,7 +3841,7 @@ const handleContextMenu = async (state, rawGroupId, x, y) => {
3755
3841
  if (rawGroupId === undefined) {
3756
3842
  return state;
3757
3843
  }
3758
- const groupId = rawGroupId === '' ? NoGroupId : Number.parseInt(rawGroupId, 10);
3844
+ const groupId = rawGroupId === '' ? NoGroupId : Number(rawGroupId);
3759
3845
  if (Number.isNaN(groupId)) {
3760
3846
  return state;
3761
3847
  }
@@ -4052,8 +4138,8 @@ const parse = sashId => {
4052
4138
  if (!beforeRaw || !afterRaw) {
4053
4139
  return undefined;
4054
4140
  }
4055
- const beforeGroupId = Number.parseFloat(beforeRaw);
4056
- const afterGroupId = Number.parseFloat(afterRaw);
4141
+ const beforeGroupId = Number(beforeRaw);
4142
+ const afterGroupId = Number(afterRaw);
4057
4143
  if (!Number.isFinite(beforeGroupId) || !Number.isFinite(afterGroupId)) {
4058
4144
  return undefined;
4059
4145
  }
@@ -4292,10 +4378,7 @@ const isValidMainAreaLayout = layout => {
4292
4378
  if (!Array.isArray(layoutObj.groups)) {
4293
4379
  return false;
4294
4380
  }
4295
- if (!layoutObj.groups.every(isValidEditorGroup)) {
4296
- return false;
4297
- }
4298
- return true;
4381
+ return !!layoutObj.groups.every(isValidEditorGroup);
4299
4382
  };
4300
4383
 
4301
4384
  const normalizeRestoredTab = tab => {
@@ -4343,7 +4426,7 @@ const tryRestoreLayout = savedState => {
4343
4426
  const groupDirection = normalizeLayoutDirection(group?.direction);
4344
4427
  return {
4345
4428
  ...group,
4346
- ...(groupDirection === undefined ? {} : {
4429
+ ...(groupDirection !== undefined && {
4347
4430
  direction: groupDirection
4348
4431
  }),
4349
4432
  tabs: Array.isArray(group?.tabs) ? group.tabs.map(normalizeRestoredTab) : []
@@ -5082,12 +5165,12 @@ const getChildrenWithCount = (nodes, startIndex, childCount) => {
5082
5165
  };
5083
5166
 
5084
5167
  const compareNodes = (oldNode, newNode) => {
5085
- const patches = [];
5086
5168
  // Check if node type changed - return null to signal incompatible nodes
5087
5169
  // (caller should handle this with a Replace operation)
5088
5170
  if (oldNode.type !== newNode.type) {
5089
5171
  return null;
5090
5172
  }
5173
+ const patches = [];
5091
5174
  // Handle reference nodes - special handling for uid changes
5092
5175
  if (oldNode.type === Reference) {
5093
5176
  if (oldNode.uid !== newNode.uid) {
@@ -5123,7 +5206,7 @@ const compareNodes = (oldNode, newNode) => {
5123
5206
  }
5124
5207
  // Check for removed attributes
5125
5208
  for (const key of oldKeys) {
5126
- if (!(key in newNode)) {
5209
+ if (!Object.hasOwn(newNode, key)) {
5127
5210
  patches.push({
5128
5211
  type: RemoveAttribute,
5129
5212
  key
@@ -5141,11 +5224,78 @@ const treeToArray = node => {
5141
5224
  return result;
5142
5225
  };
5143
5226
 
5227
+ const navigateToChild = (patches, currentChildIndex, index) => {
5228
+ if (currentChildIndex === -1) {
5229
+ patches.push({
5230
+ type: NavigateChild,
5231
+ index
5232
+ });
5233
+ return index;
5234
+ }
5235
+ if (currentChildIndex !== index) {
5236
+ patches.push({
5237
+ type: NavigateSibling,
5238
+ index
5239
+ });
5240
+ }
5241
+ return index;
5242
+ };
5243
+ const navigateToParent = (patches, currentChildIndex) => {
5244
+ if (currentChildIndex >= 0) {
5245
+ patches.push({
5246
+ type: NavigateParent
5247
+ });
5248
+ }
5249
+ return -1;
5250
+ };
5251
+ const addTree = (newNode, patches) => {
5252
+ patches.push({
5253
+ type: Add,
5254
+ nodes: treeToArray(newNode)
5255
+ });
5256
+ };
5257
+ const replaceTree = (newNode, patches) => {
5258
+ patches.push({
5259
+ type: Replace,
5260
+ nodes: treeToArray(newNode)
5261
+ });
5262
+ };
5263
+ const diffExistingChild = (oldNode, newNode, patches, currentChildIndex, index) => {
5264
+ const nodePatches = compareNodes(oldNode.node, newNode.node);
5265
+ if (nodePatches === null) {
5266
+ const nextChildIndex = navigateToChild(patches, currentChildIndex, index);
5267
+ replaceTree(newNode, patches);
5268
+ return nextChildIndex;
5269
+ }
5270
+ const hasChildrenToCompare = oldNode.children.length > 0 || newNode.children.length > 0;
5271
+ if (nodePatches.length === 0 && !hasChildrenToCompare) {
5272
+ return currentChildIndex;
5273
+ }
5274
+ const nextChildIndex = navigateToChild(patches, currentChildIndex, index);
5275
+ if (nodePatches.length > 0) {
5276
+ patches.push(...nodePatches);
5277
+ }
5278
+ if (hasChildrenToCompare) {
5279
+ diffChildren(oldNode.children, newNode.children, patches);
5280
+ }
5281
+ return nextChildIndex;
5282
+ };
5283
+ const diffRootNode = (oldNode, newNode, patches) => {
5284
+ const nodePatches = compareNodes(oldNode.node, newNode.node);
5285
+ if (nodePatches === null) {
5286
+ replaceTree(newNode, patches);
5287
+ return;
5288
+ }
5289
+ if (nodePatches.length > 0) {
5290
+ patches.push(...nodePatches);
5291
+ }
5292
+ if (oldNode.children.length > 0 || newNode.children.length > 0) {
5293
+ diffChildren(oldNode.children, newNode.children, patches);
5294
+ }
5295
+ };
5144
5296
  const diffChildren = (oldChildren, newChildren, patches) => {
5145
5297
  const maxLength = Math.max(oldChildren.length, newChildren.length);
5146
- // Track where we are: -1 means at parent, >= 0 means at child index
5147
5298
  let currentChildIndex = -1;
5148
- // Collect indices of children to remove (we'll add these patches at the end in reverse order)
5149
5299
  const indicesToRemove = [];
5150
5300
  for (let i = 0; i < maxLength; i++) {
5151
5301
  const oldNode = oldChildren[i];
@@ -5154,88 +5304,17 @@ const diffChildren = (oldChildren, newChildren, patches) => {
5154
5304
  continue;
5155
5305
  }
5156
5306
  if (!oldNode) {
5157
- // Add new node - we should be at the parent
5158
- if (currentChildIndex >= 0) {
5159
- // Navigate back to parent
5160
- patches.push({
5161
- type: NavigateParent
5162
- });
5163
- currentChildIndex = -1;
5164
- }
5165
- // Flatten the entire subtree so renderInternal can handle it
5166
- const flatNodes = treeToArray(newNode);
5167
- patches.push({
5168
- type: Add,
5169
- nodes: flatNodes
5170
- });
5171
- } else if (newNode) {
5172
- // Compare nodes to see if we need any patches
5173
- const nodePatches = compareNodes(oldNode.node, newNode.node);
5174
- // If nodePatches is null, the node types are incompatible - need to replace
5175
- if (nodePatches === null) {
5176
- // Navigate to this child
5177
- if (currentChildIndex === -1) {
5178
- patches.push({
5179
- type: NavigateChild,
5180
- index: i
5181
- });
5182
- currentChildIndex = i;
5183
- } else if (currentChildIndex !== i) {
5184
- patches.push({
5185
- type: NavigateSibling,
5186
- index: i
5187
- });
5188
- currentChildIndex = i;
5189
- }
5190
- // Replace the entire subtree
5191
- const flatNodes = treeToArray(newNode);
5192
- patches.push({
5193
- type: Replace,
5194
- nodes: flatNodes
5195
- });
5196
- // After replace, we're at the new element (same position)
5197
- continue;
5198
- }
5199
- // Check if we need to recurse into children
5200
- const hasChildrenToCompare = oldNode.children.length > 0 || newNode.children.length > 0;
5201
- // Only navigate to this element if we need to do something
5202
- if (nodePatches.length > 0 || hasChildrenToCompare) {
5203
- // Navigate to this child if not already there
5204
- if (currentChildIndex === -1) {
5205
- patches.push({
5206
- type: NavigateChild,
5207
- index: i
5208
- });
5209
- currentChildIndex = i;
5210
- } else if (currentChildIndex !== i) {
5211
- patches.push({
5212
- type: NavigateSibling,
5213
- index: i
5214
- });
5215
- currentChildIndex = i;
5216
- }
5217
- // Apply node patches (these apply to the current element, not children)
5218
- if (nodePatches.length > 0) {
5219
- patches.push(...nodePatches);
5220
- }
5221
- // Compare children recursively
5222
- if (hasChildrenToCompare) {
5223
- diffChildren(oldNode.children, newNode.children, patches);
5224
- }
5225
- }
5226
- } else {
5227
- // Remove old node - collect the index for later removal
5307
+ currentChildIndex = navigateToParent(patches, currentChildIndex);
5308
+ addTree(newNode, patches);
5309
+ continue;
5310
+ }
5311
+ if (!newNode) {
5228
5312
  indicesToRemove.push(i);
5313
+ continue;
5229
5314
  }
5315
+ currentChildIndex = diffExistingChild(oldNode, newNode, patches, currentChildIndex, i);
5230
5316
  }
5231
- // Navigate back to parent if we ended at a child
5232
- if (currentChildIndex >= 0) {
5233
- patches.push({
5234
- type: NavigateParent
5235
- });
5236
- }
5237
- // Add remove patches in reverse order (highest index first)
5238
- // This ensures indices remain valid as we remove
5317
+ navigateToParent(patches, currentChildIndex);
5239
5318
  for (let j = indicesToRemove.length - 1; j >= 0; j--) {
5240
5319
  patches.push({
5241
5320
  type: RemoveChild,
@@ -5244,33 +5323,11 @@ const diffChildren = (oldChildren, newChildren, patches) => {
5244
5323
  }
5245
5324
  };
5246
5325
  const diffTrees = (oldTree, newTree, patches, path) => {
5247
- // At the root level (path.length === 0), we're already AT the element
5248
- // So we compare the root node directly, then compare its children
5249
5326
  if (path.length === 0 && oldTree.length === 1 && newTree.length === 1) {
5250
- const oldNode = oldTree[0];
5251
- const newNode = newTree[0];
5252
- // Compare root nodes
5253
- const nodePatches = compareNodes(oldNode.node, newNode.node);
5254
- // If nodePatches is null, the root node types are incompatible - need to replace
5255
- if (nodePatches === null) {
5256
- const flatNodes = treeToArray(newNode);
5257
- patches.push({
5258
- type: Replace,
5259
- nodes: flatNodes
5260
- });
5261
- return;
5262
- }
5263
- if (nodePatches.length > 0) {
5264
- patches.push(...nodePatches);
5265
- }
5266
- // Compare children
5267
- if (oldNode.children.length > 0 || newNode.children.length > 0) {
5268
- diffChildren(oldNode.children, newNode.children, patches);
5269
- }
5270
- } else {
5271
- // Non-root level or multiple root elements - use the regular comparison
5272
- diffChildren(oldTree, newTree, patches);
5327
+ diffRootNode(oldTree[0], newTree[0], patches);
5328
+ return;
5273
5329
  }
5330
+ diffChildren(oldTree, newTree, patches);
5274
5331
  };
5275
5332
 
5276
5333
  const removeTrailingNavigationPatches = patches => {
@@ -5394,11 +5451,13 @@ const renderEditor = tab => {
5394
5451
  const renderTabActions = (isDirty, tabIndex, groupIndex) => {
5395
5452
  if (isDirty) {
5396
5453
  return [{
5454
+ 'aria-label': 'Close',
5397
5455
  childCount: 1,
5398
5456
  className: EditorTabCloseButton,
5399
5457
  'data-groupIndex': groupIndex,
5400
5458
  'data-index': tabIndex,
5401
- type: Div
5459
+ onClick: HandleClickClose,
5460
+ type: Button$2
5402
5461
  }, {
5403
5462
  childCount: 0,
5404
5463
  className: MaskIconCircleFilled,
@@ -5438,8 +5497,8 @@ const renderTab = (tab, isActive, tabIndex, groupIndex) => {
5438
5497
  className,
5439
5498
  'data-groupIndex': groupIndex,
5440
5499
  'data-index': tabIndex,
5441
- onClick: HandleClickTab,
5442
5500
  onContextMenu: HandleTabContextMenu,
5501
+ onMouseDown: HandleClickTab,
5443
5502
  role: 'tab',
5444
5503
  title: tab.uri || tab.title,
5445
5504
  type: Div
@@ -5908,7 +5967,7 @@ const saveState = state => {
5908
5967
  const {
5909
5968
  activeGroupId: originalActiveGroupId
5910
5969
  } = layout;
5911
- const activeGroupId = originalActiveGroupId !== undefined && !filteredGroups.some(g => g.id === originalActiveGroupId) ? undefined : originalActiveGroupId;
5970
+ const activeGroupId = originalActiveGroupId !== undefined && filteredGroups.every(g => g.id !== originalActiveGroupId) ? undefined : originalActiveGroupId;
5912
5971
  return {
5913
5972
  layout: {
5914
5973
  ...layout,
@@ -6196,11 +6255,11 @@ const listen = async () => {
6196
6255
  await Promise.all([initializeRendererWorker(), initializeIconThemeWorker(), initializeClipBoardWorker()]);
6197
6256
  };
6198
6257
 
6199
- const main$2 = async () => {
6258
+ const main$1 = async () => {
6200
6259
  await listen();
6201
6260
  };
6202
6261
 
6203
- const main$1 = async () => {
6262
+ const main = async () => {
6204
6263
  registerCommands(commandMap);
6205
6264
  const rpc = await create$4({
6206
6265
  commandMap: commandMap
@@ -6208,10 +6267,5 @@ const main$1 = async () => {
6208
6267
  set$1(rpc);
6209
6268
  };
6210
6269
 
6211
- const main = async () => {
6212
- await main$2();
6213
- await main$1();
6214
- };
6215
- main$2();
6216
-
6217
- export { main };
6270
+ await main$1();
6271
+ await main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/main-area-worker",
3
- "version": "9.15.0",
3
+ "version": "9.17.0",
4
4
  "description": "Main Area Worker",
5
5
  "repository": {
6
6
  "type": "git",