@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 +2 -0
- package/dist/mainAreaWorkerMain.js +549 -495
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,13 +13,13 @@ const create$f = () => {
|
|
|
13
13
|
},
|
|
14
14
|
diff(uid, modules, numbers) {
|
|
15
15
|
const {
|
|
16
|
-
|
|
17
|
-
|
|
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,
|
|
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(
|
|
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
|
|
824
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
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:
|
|
1928
|
-
maxOpenEditors:
|
|
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
|
-
|
|
2332
|
-
|
|
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
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
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
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
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
|
|
2398
|
-
|
|
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
|
|
2402
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
4056
|
-
const afterGroupId = Number
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5251
|
-
|
|
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
|
-
|
|
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 &&
|
|
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$
|
|
6258
|
+
const main$1 = async () => {
|
|
6200
6259
|
await listen();
|
|
6201
6260
|
};
|
|
6202
6261
|
|
|
6203
|
-
const main
|
|
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
|
-
|
|
6212
|
-
|
|
6213
|
-
await main$1();
|
|
6214
|
-
};
|
|
6215
|
-
main$2();
|
|
6216
|
-
|
|
6217
|
-
export { main };
|
|
6270
|
+
await main$1();
|
|
6271
|
+
await main();
|