@lvce-editor/main-area-worker 9.27.0 → 9.28.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/dist/mainAreaWorkerMain.js +616 -554
- package/package.json +1 -1
|
@@ -599,7 +599,14 @@ class IpcError extends VError {
|
|
|
599
599
|
const cause = new Error(message);
|
|
600
600
|
// @ts-ignore
|
|
601
601
|
cause.code = code;
|
|
602
|
-
|
|
602
|
+
if (stack) {
|
|
603
|
+
Object.defineProperty(cause, 'stack', {
|
|
604
|
+
configurable: true,
|
|
605
|
+
enumerable: false,
|
|
606
|
+
value: stack,
|
|
607
|
+
writable: true
|
|
608
|
+
});
|
|
609
|
+
}
|
|
603
610
|
super(cause, betterMessage);
|
|
604
611
|
} else {
|
|
605
612
|
super(betterMessage);
|
|
@@ -1898,144 +1905,484 @@ const closeTab = (state, groupId, tabId) => {
|
|
|
1898
1905
|
return withGroups(stateWithClosedTab, newGroups);
|
|
1899
1906
|
};
|
|
1900
1907
|
|
|
1901
|
-
const
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
const group = layout.groups.find(g => g.id === groupId);
|
|
1906
|
-
return group?.tabs.find(t => t.id === tabId);
|
|
1908
|
+
const handleAttach = async command => {
|
|
1909
|
+
// TODO find a better way to append editors
|
|
1910
|
+
const parentNodeSelector = '.editor-groups-container, .EditorGroup';
|
|
1911
|
+
await invoke('Layout.attachViewlet', parentNodeSelector, command.instanceId);
|
|
1907
1912
|
};
|
|
1908
1913
|
|
|
1909
|
-
const
|
|
1910
|
-
|
|
1914
|
+
const getTitle$1 = async editorUid => {
|
|
1915
|
+
try {
|
|
1916
|
+
const title = await invoke('Viewlet.getTitle', editorUid);
|
|
1917
|
+
return typeof title === 'string' && title ? title : undefined;
|
|
1918
|
+
} catch {
|
|
1919
|
+
return undefined;
|
|
1920
|
+
}
|
|
1921
|
+
};
|
|
1922
|
+
const createViewlet = async (viewletModuleId, editorUid, tabId, bounds, uri) => {
|
|
1923
|
+
await invoke('Layout.createViewlet', viewletModuleId, editorUid, tabId, bounds, uri);
|
|
1924
|
+
return getTitle$1(editorUid);
|
|
1911
1925
|
};
|
|
1912
1926
|
|
|
1913
|
-
const
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1927
|
+
const {
|
|
1928
|
+
get,
|
|
1929
|
+
getCommandIds,
|
|
1930
|
+
getKeys: getKeys$1,
|
|
1931
|
+
registerCommands,
|
|
1932
|
+
set,
|
|
1933
|
+
wrapAsyncCommand,
|
|
1934
|
+
wrapGetter,
|
|
1935
|
+
wrapSerialAsyncCommand,
|
|
1936
|
+
wrapSerialCommand
|
|
1937
|
+
} = create$f();
|
|
1938
|
+
|
|
1939
|
+
const create$2 = () => {
|
|
1940
|
+
return Math.random();
|
|
1925
1941
|
};
|
|
1926
1942
|
|
|
1927
|
-
const
|
|
1943
|
+
const updateTab = (state, tabId, updates) => {
|
|
1928
1944
|
const {
|
|
1929
1945
|
layout
|
|
1930
1946
|
} = state;
|
|
1931
1947
|
const {
|
|
1932
1948
|
groups
|
|
1933
1949
|
} = layout;
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
const entries = state.layout.groups.flatMap((group, groupIndex) => {
|
|
1953
|
-
return group.tabs.map((tab, tabIndex) => ({
|
|
1954
|
-
group,
|
|
1955
|
-
groupIndex,
|
|
1956
|
-
tab,
|
|
1957
|
-
tabIndex
|
|
1958
|
-
}));
|
|
1950
|
+
const updatedGroups = groups.map(group => {
|
|
1951
|
+
const tabIndex = group.tabs.findIndex(t => t.id === tabId);
|
|
1952
|
+
if (tabIndex === -1) {
|
|
1953
|
+
return group;
|
|
1954
|
+
}
|
|
1955
|
+
const updatedTabs = group.tabs.map((tab, index) => {
|
|
1956
|
+
if (index === tabIndex) {
|
|
1957
|
+
return {
|
|
1958
|
+
...tab,
|
|
1959
|
+
...updates
|
|
1960
|
+
};
|
|
1961
|
+
}
|
|
1962
|
+
return tab;
|
|
1963
|
+
});
|
|
1964
|
+
return {
|
|
1965
|
+
...group,
|
|
1966
|
+
tabs: updatedTabs
|
|
1967
|
+
};
|
|
1959
1968
|
});
|
|
1960
|
-
return
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
...group,
|
|
1968
|
-
size: baseSize + (index === groups.length - 1 ? remainder : 0)
|
|
1969
|
-
}));
|
|
1969
|
+
return {
|
|
1970
|
+
...state,
|
|
1971
|
+
layout: {
|
|
1972
|
+
...layout,
|
|
1973
|
+
groups: updatedGroups
|
|
1974
|
+
}
|
|
1975
|
+
};
|
|
1970
1976
|
};
|
|
1971
1977
|
|
|
1972
|
-
const
|
|
1973
|
-
if (Number.isNaN(groupId)) {
|
|
1974
|
-
return state;
|
|
1975
|
-
}
|
|
1978
|
+
const findTab = (state, tabId) => {
|
|
1976
1979
|
const {
|
|
1977
1980
|
layout
|
|
1978
1981
|
} = state;
|
|
1979
1982
|
const {
|
|
1980
|
-
activeGroupId,
|
|
1981
1983
|
groups
|
|
1982
1984
|
} = layout;
|
|
1983
|
-
const
|
|
1984
|
-
|
|
1985
|
-
|
|
1985
|
+
for (const group of groups) {
|
|
1986
|
+
const tab = group.tabs.find(t => t.id === tabId);
|
|
1987
|
+
if (tab) {
|
|
1988
|
+
return tab;
|
|
1989
|
+
}
|
|
1986
1990
|
}
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
+
return undefined;
|
|
1992
|
+
};
|
|
1993
|
+
const loadFileContent = async path => {
|
|
1994
|
+
const content = await readFile(path);
|
|
1995
|
+
return content;
|
|
1996
|
+
};
|
|
1997
|
+
const getLoadFileErrorMessage = error => {
|
|
1998
|
+
if (!(error instanceof Error)) {
|
|
1999
|
+
return 'Failed to load file content';
|
|
2000
|
+
}
|
|
2001
|
+
if (error.message.includes('EISDIR') || error.message.includes('illegal operation on a directory') || error.message.includes('is a directory')) {
|
|
2002
|
+
return 'Expected a file but received a folder';
|
|
2003
|
+
}
|
|
2004
|
+
return error.message;
|
|
1991
2005
|
};
|
|
2006
|
+
const loadTabContentAsync = async (tabId, path, requestId, getLatestState) => {
|
|
2007
|
+
try {
|
|
2008
|
+
await loadFileContent(path);
|
|
1992
2009
|
|
|
1993
|
-
|
|
1994
|
-
|
|
2010
|
+
// Check for race condition: get the latest state
|
|
2011
|
+
const latestState = getLatestState();
|
|
2012
|
+
const latestTab = findTab(latestState, tabId);
|
|
2013
|
+
|
|
2014
|
+
// If the tab no longer exists, discard this result
|
|
2015
|
+
if (!latestTab) {
|
|
2016
|
+
return latestState;
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
// If the tab is no longer in loading state, discard this result (newer request started)
|
|
2020
|
+
if (latestTab.loadingState !== 'loading') {
|
|
2021
|
+
return latestState;
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
// Assign editorUid if tab doesn't have one yet
|
|
2025
|
+
const editorUid = latestTab.editorUid === -1 ? create$2() : latestTab.editorUid;
|
|
2026
|
+
return updateTab(latestState, tabId, {
|
|
2027
|
+
editorUid,
|
|
2028
|
+
errorMessage: undefined,
|
|
2029
|
+
loadingState: 'loaded'
|
|
2030
|
+
});
|
|
2031
|
+
} catch (error) {
|
|
2032
|
+
// Check for race condition before updating with error
|
|
2033
|
+
const latestState = getLatestState();
|
|
2034
|
+
const latestTab = findTab(latestState, tabId);
|
|
2035
|
+
if (!latestTab) {
|
|
2036
|
+
return latestState;
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
// If the tab is no longer in loading state, discard this result (newer request started)
|
|
2040
|
+
if (latestTab.loadingState !== 'loading') {
|
|
2041
|
+
return latestState;
|
|
2042
|
+
}
|
|
2043
|
+
const errorMessage = getLoadFileErrorMessage(error);
|
|
2044
|
+
return updateTab(latestState, tabId, {
|
|
2045
|
+
errorMessage,
|
|
2046
|
+
loadingState: 'error'
|
|
2047
|
+
});
|
|
2048
|
+
}
|
|
1995
2049
|
};
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
if (!focusedGroup) {
|
|
2050
|
+
|
|
2051
|
+
/**
|
|
2052
|
+
* SAFE: Create viewlet for a tab (no visible side effects)
|
|
2053
|
+
* Can be called eagerly when tab is opened - the viewlet loads in the background.
|
|
2054
|
+
*/
|
|
2055
|
+
const createViewletForTab = (state, tabId, viewletModuleId, bounds) => {
|
|
2056
|
+
const tab = findTab(state, tabId);
|
|
2057
|
+
if (!tab) {
|
|
2005
2058
|
return state;
|
|
2006
2059
|
}
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
if (activeTabId === undefined) {
|
|
2060
|
+
|
|
2061
|
+
// If tab already has an editorUid or is loading/loaded, don't recreate
|
|
2062
|
+
if (tab.editorUid !== -1 || tab.loadingState === 'loading' || tab.loadingState === 'loaded') {
|
|
2011
2063
|
return state;
|
|
2012
2064
|
}
|
|
2013
|
-
|
|
2065
|
+
const editorUid = create$2();
|
|
2066
|
+
const newState = updateTab(state, tabId, {
|
|
2067
|
+
editorUid
|
|
2068
|
+
});
|
|
2069
|
+
return newState;
|
|
2014
2070
|
};
|
|
2015
2071
|
|
|
2016
|
-
|
|
2072
|
+
/**
|
|
2073
|
+
* Called when switching tabs.
|
|
2074
|
+
* With reference nodes, attachment/detachment is handled automatically by virtual DOM rendering.
|
|
2075
|
+
* No attach/detach commands needed - virtual DOM will render the correct reference at each position.
|
|
2076
|
+
*/
|
|
2077
|
+
const switchViewlet = (state, fromTabId, toTabId) => {
|
|
2078
|
+
// No commands needed - virtual DOM reference nodes handle attachment automatically
|
|
2079
|
+
// Virtual DOM will:
|
|
2080
|
+
// 1. Remove the old reference node from the previous active tab
|
|
2081
|
+
// 2. Add the new reference node for the now-active tab
|
|
2082
|
+
// This achieves the same effect as detach/attach without explicit commands
|
|
2083
|
+
|
|
2084
|
+
return {
|
|
2085
|
+
commands: [],
|
|
2086
|
+
newState: state
|
|
2087
|
+
};
|
|
2088
|
+
};
|
|
2089
|
+
|
|
2090
|
+
const findTabByEditorUid = (state, editorUid) => {
|
|
2017
2091
|
const {
|
|
2018
2092
|
layout
|
|
2019
2093
|
} = state;
|
|
2020
2094
|
const {
|
|
2021
|
-
activeGroupId,
|
|
2022
2095
|
groups
|
|
2023
2096
|
} = layout;
|
|
2024
|
-
const
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
if (!group) {
|
|
2030
|
-
return state;
|
|
2031
|
-
}
|
|
2032
|
-
const {
|
|
2033
|
-
activeTabId
|
|
2034
|
-
} = group;
|
|
2035
|
-
if (activeTabId === undefined) {
|
|
2036
|
-
return state;
|
|
2097
|
+
for (const group of groups) {
|
|
2098
|
+
const tab = group.tabs.find(t => t.editorUid === editorUid);
|
|
2099
|
+
if (tab) {
|
|
2100
|
+
return tab;
|
|
2101
|
+
}
|
|
2037
2102
|
}
|
|
2038
|
-
|
|
2103
|
+
return undefined;
|
|
2104
|
+
};
|
|
2105
|
+
|
|
2106
|
+
/**
|
|
2107
|
+
* Called when renderer reports viewlet finished creating.
|
|
2108
|
+
* With reference nodes, attachment is handled automatically by virtual DOM rendering.
|
|
2109
|
+
* No commands needed - state update is sufficient.
|
|
2110
|
+
*/
|
|
2111
|
+
const handleViewletReady = (state, editorUid, title) => {
|
|
2112
|
+
if (editorUid === -1) {
|
|
2113
|
+
throw new Error('Invalid editorUid -1');
|
|
2114
|
+
}
|
|
2115
|
+
// Find the tab by viewletRequestId
|
|
2116
|
+
const tab = findTabByEditorUid(state, editorUid);
|
|
2117
|
+
if (!tab) {
|
|
2118
|
+
// Tab was closed, dispose viewlet
|
|
2119
|
+
return state;
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
// Mark viewlet as ready and set instance id
|
|
2123
|
+
// Reference nodes will handle rendering at the correct position automatically
|
|
2124
|
+
const newState = updateTab(state, tab.id, {
|
|
2125
|
+
editorUid,
|
|
2126
|
+
loadingState: 'loaded',
|
|
2127
|
+
...(title && {
|
|
2128
|
+
title
|
|
2129
|
+
})
|
|
2130
|
+
});
|
|
2131
|
+
|
|
2132
|
+
// No attach commands needed - virtual DOM reference nodes handle positioning
|
|
2133
|
+
return newState;
|
|
2134
|
+
};
|
|
2135
|
+
|
|
2136
|
+
/**
|
|
2137
|
+
* Dispose viewlet when tab is closed.
|
|
2138
|
+
*/
|
|
2139
|
+
const disposeViewletForTab = (state, tabId) => {
|
|
2140
|
+
const tab = findTab(state, tabId);
|
|
2141
|
+
if (!tab || tab.editorUid === -1) {
|
|
2142
|
+
return {
|
|
2143
|
+
commands: [],
|
|
2144
|
+
newState: state
|
|
2145
|
+
};
|
|
2146
|
+
}
|
|
2147
|
+
const commands = [{
|
|
2148
|
+
instanceId: tab.editorUid,
|
|
2149
|
+
type: 'dispose'
|
|
2150
|
+
}];
|
|
2151
|
+
return {
|
|
2152
|
+
commands,
|
|
2153
|
+
newState: state
|
|
2154
|
+
};
|
|
2155
|
+
};
|
|
2156
|
+
|
|
2157
|
+
const handleCreate = async command => {
|
|
2158
|
+
// Safe to call - no visible side effects
|
|
2159
|
+
|
|
2160
|
+
const title = await createViewlet(command.viewletModuleId, command.editorUid, command.tabId, command.bounds, command.uri || '');
|
|
2161
|
+
|
|
2162
|
+
// After viewlet is created, mark it as ready
|
|
2163
|
+
// Attachment is handled automatically by virtual DOM reference nodes
|
|
2164
|
+
const {
|
|
2165
|
+
newState: state,
|
|
2166
|
+
oldState
|
|
2167
|
+
} = get(command.uid);
|
|
2168
|
+
const readyState = handleViewletReady(state, command.editorUid, title);
|
|
2169
|
+
set(command.uid, oldState, readyState);
|
|
2170
|
+
return readyState;
|
|
2171
|
+
};
|
|
2172
|
+
|
|
2173
|
+
const handleDetach = async command => {
|
|
2174
|
+
// Hides viewlet but keeps it alive
|
|
2175
|
+
|
|
2176
|
+
await invoke('Viewlet.detach', command.instanceId);
|
|
2177
|
+
};
|
|
2178
|
+
|
|
2179
|
+
const handleDispose = async command => {
|
|
2180
|
+
// Fully destroys viewlet
|
|
2181
|
+
|
|
2182
|
+
await invoke('Viewlet.dispose', command.instanceId);
|
|
2183
|
+
};
|
|
2184
|
+
|
|
2185
|
+
const handleSetBounds = async command => {
|
|
2186
|
+
await invoke('Viewlet.setBounds', command.instanceId, command.bounds);
|
|
2187
|
+
};
|
|
2188
|
+
|
|
2189
|
+
const executeViewletCommand = async command => {
|
|
2190
|
+
switch (command.type) {
|
|
2191
|
+
case 'attach':
|
|
2192
|
+
await handleAttach(command);
|
|
2193
|
+
return;
|
|
2194
|
+
case 'create':
|
|
2195
|
+
await handleCreate(command);
|
|
2196
|
+
return;
|
|
2197
|
+
case 'detach':
|
|
2198
|
+
await handleDetach(command);
|
|
2199
|
+
return;
|
|
2200
|
+
case 'dispose':
|
|
2201
|
+
await handleDispose(command);
|
|
2202
|
+
return;
|
|
2203
|
+
case 'setBounds':
|
|
2204
|
+
await handleSetBounds(command);
|
|
2205
|
+
return;
|
|
2206
|
+
}
|
|
2207
|
+
};
|
|
2208
|
+
const executeViewletCommands = async commands => {
|
|
2209
|
+
for (const command of commands) {
|
|
2210
|
+
await executeViewletCommand(command);
|
|
2211
|
+
}
|
|
2212
|
+
};
|
|
2213
|
+
|
|
2214
|
+
const findTabInState = (state, groupId, tabId) => {
|
|
2215
|
+
const {
|
|
2216
|
+
layout
|
|
2217
|
+
} = state;
|
|
2218
|
+
const group = layout.groups.find(g => g.id === groupId);
|
|
2219
|
+
return group?.tabs.find(t => t.id === tabId);
|
|
2220
|
+
};
|
|
2221
|
+
|
|
2222
|
+
const closeTabWithViewlet = async (state, groupId, tabId) => {
|
|
2223
|
+
const tab = findTabInState(state, groupId, tabId);
|
|
2224
|
+
const commands = [];
|
|
2225
|
+
if (tab && tab.editorUid !== -1) {
|
|
2226
|
+
const {
|
|
2227
|
+
commands: disposeCommands
|
|
2228
|
+
} = disposeViewletForTab(state, tabId);
|
|
2229
|
+
commands.push(...disposeCommands);
|
|
2230
|
+
}
|
|
2231
|
+
const newState = closeTab(state, groupId, tabId);
|
|
2232
|
+
const group = state.layout.groups.find(g => g.id === groupId);
|
|
2233
|
+
const wasActiveTab = group?.activeTabId === tabId;
|
|
2234
|
+
if (wasActiveTab) {
|
|
2235
|
+
const newGroup = newState.layout.groups.find(g => g.id === groupId);
|
|
2236
|
+
const newActiveTabId = newGroup?.activeTabId;
|
|
2237
|
+
if (newActiveTabId !== undefined) {
|
|
2238
|
+
const {
|
|
2239
|
+
commands: switchCommands,
|
|
2240
|
+
newState: switchedState
|
|
2241
|
+
} = switchViewlet(newState);
|
|
2242
|
+
commands.push(...switchCommands);
|
|
2243
|
+
await executeViewletCommands(commands);
|
|
2244
|
+
return switchedState;
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
if (commands.length > 0) {
|
|
2248
|
+
await executeViewletCommands(commands);
|
|
2249
|
+
}
|
|
2250
|
+
return newState;
|
|
2251
|
+
};
|
|
2252
|
+
|
|
2253
|
+
const saveEditor = async editorUid => {
|
|
2254
|
+
return invoke('Editor.save', editorUid);
|
|
2255
|
+
};
|
|
2256
|
+
|
|
2257
|
+
const closeTabAndSave = async (state, groupId, tabId) => {
|
|
2258
|
+
const tab = findTabInState(state, groupId, tabId);
|
|
2259
|
+
if (!tab) {
|
|
2260
|
+
return state;
|
|
2261
|
+
}
|
|
2262
|
+
if (tab.editorUid !== -1 && tab.isDirty) {
|
|
2263
|
+
const editorState = await saveEditor(tab.editorUid);
|
|
2264
|
+
if (!editorState?.modified && tab.uri) {
|
|
2265
|
+
await handleModifiedStatusChange$1(tab.uri, false);
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
if (tab.uri?.startsWith('simple-browser://')) {
|
|
2269
|
+
return closeTabWithViewlet(state, groupId, tabId);
|
|
2270
|
+
}
|
|
2271
|
+
return closeTab(state, groupId, tabId);
|
|
2272
|
+
};
|
|
2273
|
+
|
|
2274
|
+
const getFocusedGroup = state => {
|
|
2275
|
+
const {
|
|
2276
|
+
layout
|
|
2277
|
+
} = state;
|
|
2278
|
+
const {
|
|
2279
|
+
groups
|
|
2280
|
+
} = layout;
|
|
2281
|
+
return groups.find(group => group.focused);
|
|
2282
|
+
};
|
|
2283
|
+
|
|
2284
|
+
const closeActiveEditor = async state => {
|
|
2285
|
+
const focusedGroup = getFocusedGroup(state);
|
|
2286
|
+
if (!focusedGroup) {
|
|
2287
|
+
return state;
|
|
2288
|
+
}
|
|
2289
|
+
const {
|
|
2290
|
+
activeTabId
|
|
2291
|
+
} = focusedGroup;
|
|
2292
|
+
if (activeTabId === undefined) {
|
|
2293
|
+
return state;
|
|
2294
|
+
}
|
|
2295
|
+
return closeTabAndSave(state, focusedGroup.id, activeTabId);
|
|
2296
|
+
};
|
|
2297
|
+
|
|
2298
|
+
const closeAll$1 = state => {
|
|
2299
|
+
const entries = state.layout.groups.flatMap((group, groupIndex) => {
|
|
2300
|
+
return group.tabs.map((tab, tabIndex) => ({
|
|
2301
|
+
group,
|
|
2302
|
+
groupIndex,
|
|
2303
|
+
tab,
|
|
2304
|
+
tabIndex
|
|
2305
|
+
}));
|
|
2306
|
+
});
|
|
2307
|
+
return withEmptyGroups(addClosedTabs(state, entries));
|
|
2308
|
+
};
|
|
2309
|
+
|
|
2310
|
+
const redistributeSizesWithRemainder = groups => {
|
|
2311
|
+
const baseSize = Math.floor(100 / groups.length);
|
|
2312
|
+
const remainder = 100 % groups.length;
|
|
2313
|
+
return groups.map((group, index) => ({
|
|
2314
|
+
...group,
|
|
2315
|
+
size: baseSize + (index === groups.length - 1 ? remainder : 0)
|
|
2316
|
+
}));
|
|
2317
|
+
};
|
|
2318
|
+
|
|
2319
|
+
const closeEditorGroup$1 = (state, groupId) => {
|
|
2320
|
+
if (Number.isNaN(groupId)) {
|
|
2321
|
+
return state;
|
|
2322
|
+
}
|
|
2323
|
+
const {
|
|
2324
|
+
layout
|
|
2325
|
+
} = state;
|
|
2326
|
+
const {
|
|
2327
|
+
activeGroupId,
|
|
2328
|
+
groups
|
|
2329
|
+
} = layout;
|
|
2330
|
+
const groupIndex = getGroupIndexById(state, groupId);
|
|
2331
|
+
if (groupIndex === -1 || groups.length <= 1) {
|
|
2332
|
+
return state;
|
|
2333
|
+
}
|
|
2334
|
+
const remainingGroups = groups.filter(group => group.id !== groupId);
|
|
2335
|
+
const redistributedGroups = redistributeSizesWithRemainder(remainingGroups);
|
|
2336
|
+
const newActiveGroupId = activeGroupId === groupId ? remainingGroups[0].id : activeGroupId;
|
|
2337
|
+
return withGroupsAndActiveGroup(state, redistributedGroups, newActiveGroupId);
|
|
2338
|
+
};
|
|
2339
|
+
|
|
2340
|
+
const isFocused = group => {
|
|
2341
|
+
return group.focused;
|
|
2342
|
+
};
|
|
2343
|
+
const closeFocusedTab = async state => {
|
|
2344
|
+
const {
|
|
2345
|
+
layout
|
|
2346
|
+
} = state;
|
|
2347
|
+
const {
|
|
2348
|
+
groups
|
|
2349
|
+
} = layout;
|
|
2350
|
+
const focusedGroup = groups.find(isFocused);
|
|
2351
|
+
if (!focusedGroup) {
|
|
2352
|
+
return state;
|
|
2353
|
+
}
|
|
2354
|
+
const {
|
|
2355
|
+
activeTabId
|
|
2356
|
+
} = focusedGroup;
|
|
2357
|
+
if (activeTabId === undefined) {
|
|
2358
|
+
return state;
|
|
2359
|
+
}
|
|
2360
|
+
return closeTabAndSave(state, focusedGroup.id, activeTabId);
|
|
2361
|
+
};
|
|
2362
|
+
|
|
2363
|
+
const closeOtherTabs = (state, groupId) => {
|
|
2364
|
+
const {
|
|
2365
|
+
layout
|
|
2366
|
+
} = state;
|
|
2367
|
+
const {
|
|
2368
|
+
activeGroupId,
|
|
2369
|
+
groups
|
|
2370
|
+
} = layout;
|
|
2371
|
+
const targetGroupId = groupId ?? activeGroupId;
|
|
2372
|
+
if (targetGroupId === undefined) {
|
|
2373
|
+
return state;
|
|
2374
|
+
}
|
|
2375
|
+
const group = groups.find(g => g.id === targetGroupId);
|
|
2376
|
+
if (!group) {
|
|
2377
|
+
return state;
|
|
2378
|
+
}
|
|
2379
|
+
const {
|
|
2380
|
+
activeTabId
|
|
2381
|
+
} = group;
|
|
2382
|
+
if (activeTabId === undefined) {
|
|
2383
|
+
return state;
|
|
2384
|
+
}
|
|
2385
|
+
const closedTabs = group.tabs.map((tab, tabIndex) => ({
|
|
2039
2386
|
group,
|
|
2040
2387
|
groupIndex: groups.findIndex(g => g.id === targetGroupId),
|
|
2041
2388
|
tab,
|
|
@@ -2125,473 +2472,188 @@ const closeTabsByUris = (state, uris) => {
|
|
|
2125
2472
|
while (nextState) {
|
|
2126
2473
|
currentState = nextState;
|
|
2127
2474
|
nextState = closeFirstTabByUri(currentState, uri);
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
2130
|
-
return currentState;
|
|
2131
|
-
};
|
|
2132
|
-
|
|
2133
|
-
const getGroupById = (state, groupId) => {
|
|
2134
|
-
return state.layout.groups.find(group => group.id === groupId);
|
|
2135
|
-
};
|
|
2136
|
-
|
|
2137
|
-
const closeTabsRight = (state, groupId) => {
|
|
2138
|
-
const {
|
|
2139
|
-
layout
|
|
2140
|
-
} = state;
|
|
2141
|
-
const {
|
|
2142
|
-
groups
|
|
2143
|
-
} = layout;
|
|
2144
|
-
const group = getGroupById(state, groupId);
|
|
2145
|
-
if (!group) {
|
|
2146
|
-
return state;
|
|
2147
|
-
}
|
|
2148
|
-
const {
|
|
2149
|
-
activeTabId,
|
|
2150
|
-
tabs
|
|
2151
|
-
} = group;
|
|
2152
|
-
if (activeTabId === undefined) {
|
|
2153
|
-
return state;
|
|
2154
|
-
}
|
|
2155
|
-
const activeTabIndex = tabs.findIndex(tab => tab.id === activeTabId);
|
|
2156
|
-
if (activeTabIndex === -1) {
|
|
2157
|
-
return state;
|
|
2158
|
-
}
|
|
2159
|
-
const newTabs = tabs.slice(0, activeTabIndex + 1);
|
|
2160
|
-
if (newTabs.length === tabs.length) {
|
|
2161
|
-
return state;
|
|
2162
|
-
}
|
|
2163
|
-
const groupIndex = groups.findIndex(g => g.id === groupId);
|
|
2164
|
-
const closedTabs = tabs.slice(activeTabIndex + 1).map((tab, index) => ({
|
|
2165
|
-
group,
|
|
2166
|
-
groupIndex,
|
|
2167
|
-
tab,
|
|
2168
|
-
tabIndex: activeTabIndex + index + 1
|
|
2169
|
-
}));
|
|
2170
|
-
const newGroups = groups.map(g => {
|
|
2171
|
-
if (g.id === groupId) {
|
|
2172
|
-
return {
|
|
2173
|
-
...g,
|
|
2174
|
-
isEmpty: newTabs.length === 0,
|
|
2175
|
-
tabs: newTabs
|
|
2176
|
-
};
|
|
2177
|
-
}
|
|
2178
|
-
return g;
|
|
2179
|
-
});
|
|
2180
|
-
return withGroups(addClosedTabs(state, closedTabs), newGroups);
|
|
2181
|
-
};
|
|
2182
|
-
|
|
2183
|
-
const findGroupById = (state, groupId) => {
|
|
2184
|
-
const {
|
|
2185
|
-
layout
|
|
2186
|
-
} = state;
|
|
2187
|
-
const {
|
|
2188
|
-
groups
|
|
2189
|
-
} = layout;
|
|
2190
|
-
return groups.find(group => group.id === groupId);
|
|
2191
|
-
};
|
|
2192
|
-
|
|
2193
|
-
const copyIntoNewWindow$1 = async state => {
|
|
2194
|
-
const {
|
|
2195
|
-
activeGroupId
|
|
2196
|
-
} = state.layout;
|
|
2197
|
-
const group = findGroupById(state, activeGroupId ?? 0);
|
|
2198
|
-
if (!group) {
|
|
2199
|
-
return state;
|
|
2200
|
-
}
|
|
2201
|
-
const {
|
|
2202
|
-
activeTabId
|
|
2203
|
-
} = group;
|
|
2204
|
-
if (activeTabId === undefined) {
|
|
2205
|
-
return state;
|
|
2206
|
-
}
|
|
2207
|
-
const tab = group.tabs.find(tab => tab.id === activeTabId);
|
|
2208
|
-
if (!tab?.uri) {
|
|
2209
|
-
return state;
|
|
2210
|
-
}
|
|
2211
|
-
await invoke('ElectronWindow.openNewWithUri', tab.uri);
|
|
2212
|
-
return state;
|
|
2213
|
-
};
|
|
2214
|
-
|
|
2215
|
-
const copyPath$1 = async (state, path) => {
|
|
2216
|
-
string(path);
|
|
2217
|
-
await writeClipBoardText(path);
|
|
2218
|
-
return state;
|
|
2219
|
-
};
|
|
2220
|
-
|
|
2221
|
-
const copyRelativePath$1 = async (state, path) => {
|
|
2222
|
-
string(path);
|
|
2223
|
-
// TODO use clipboard worker
|
|
2224
|
-
const relativePath = await invoke('Workspace.pathBaseName', path);
|
|
2225
|
-
await invoke('ClipBoard.writeText', relativePath);
|
|
2226
|
-
return state;
|
|
2227
|
-
};
|
|
2228
|
-
|
|
2229
|
-
const Horizontal = 1;
|
|
2230
|
-
const Vertical = 2;
|
|
2231
|
-
const isLayoutDirection = direction => {
|
|
2232
|
-
return direction === Horizontal || direction === Vertical;
|
|
2233
|
-
};
|
|
2234
|
-
const normalizeLayoutDirection = direction => {
|
|
2235
|
-
if (isLayoutDirection(direction)) {
|
|
2236
|
-
return direction;
|
|
2237
|
-
}
|
|
2238
|
-
if (direction === 'horizontal') {
|
|
2239
|
-
return Horizontal;
|
|
2240
|
-
}
|
|
2241
|
-
if (direction === 'vertical') {
|
|
2242
|
-
return Vertical;
|
|
2243
|
-
}
|
|
2244
|
-
return undefined;
|
|
2245
|
-
};
|
|
2246
|
-
|
|
2247
|
-
const {
|
|
2248
|
-
get,
|
|
2249
|
-
getCommandIds,
|
|
2250
|
-
getKeys: getKeys$1,
|
|
2251
|
-
registerCommands,
|
|
2252
|
-
set,
|
|
2253
|
-
wrapAsyncCommand,
|
|
2254
|
-
wrapGetter,
|
|
2255
|
-
wrapSerialAsyncCommand,
|
|
2256
|
-
wrapSerialCommand
|
|
2257
|
-
} = create$f();
|
|
2258
|
-
|
|
2259
|
-
const create$2 = (uid, uri, x, y, width, height, platform, assetDir, tabHeight = 35) => {
|
|
2260
|
-
const state = {
|
|
2261
|
-
assetDir,
|
|
2262
|
-
closedTabs: [],
|
|
2263
|
-
fileIconCache: {},
|
|
2264
|
-
height,
|
|
2265
|
-
homeDirUri: '',
|
|
2266
|
-
iframes: [],
|
|
2267
|
-
initial: true,
|
|
2268
|
-
layout: {
|
|
2269
|
-
activeGroupId: undefined,
|
|
2270
|
-
direction: Horizontal,
|
|
2271
|
-
groups: []
|
|
2272
|
-
},
|
|
2273
|
-
maxOpenEditorGroups: Infinity,
|
|
2274
|
-
maxOpenEditors: Infinity,
|
|
2275
|
-
minGroupHeightPx: 80,
|
|
2276
|
-
minGroupWidthPx: 250,
|
|
2277
|
-
platform,
|
|
2278
|
-
splitButtonEnabled: false,
|
|
2279
|
-
tabHeight,
|
|
2280
|
-
uid,
|
|
2281
|
-
width,
|
|
2282
|
-
workspaceuri: uri,
|
|
2283
|
-
x,
|
|
2284
|
-
y
|
|
2285
|
-
};
|
|
2286
|
-
set(uid, state, state);
|
|
2287
|
-
};
|
|
2288
|
-
|
|
2289
|
-
const isEqual$1 = (oldState, newState) => {
|
|
2290
|
-
return false;
|
|
2291
|
-
};
|
|
2292
|
-
|
|
2293
|
-
const isEqual = (oldState, newState) => {
|
|
2294
|
-
return oldState.layout === newState.layout;
|
|
2295
|
-
};
|
|
2296
|
-
|
|
2297
|
-
const RenderItems = 4;
|
|
2298
|
-
const RenderCss = 10;
|
|
2299
|
-
const RenderIncremental = 11;
|
|
2300
|
-
|
|
2301
|
-
const modules = [isEqual, isEqual$1];
|
|
2302
|
-
const numbers = [RenderIncremental, RenderCss];
|
|
2303
|
-
|
|
2304
|
-
const diff = (oldState, newState) => {
|
|
2305
|
-
const diffResult = [];
|
|
2306
|
-
for (let i = 0; i < modules.length; i++) {
|
|
2307
|
-
const fn = modules[i];
|
|
2308
|
-
if (!fn(oldState, newState)) {
|
|
2309
|
-
diffResult.push(numbers[i]);
|
|
2310
|
-
}
|
|
2311
|
-
}
|
|
2312
|
-
return diffResult;
|
|
2313
|
-
};
|
|
2314
|
-
|
|
2315
|
-
const diff2 = uid => {
|
|
2316
|
-
const {
|
|
2317
|
-
newState,
|
|
2318
|
-
oldState
|
|
2319
|
-
} = get(uid);
|
|
2320
|
-
const result = diff(oldState, newState);
|
|
2321
|
-
return result;
|
|
2322
|
-
};
|
|
2323
|
-
|
|
2324
|
-
const handleAttach = async command => {
|
|
2325
|
-
// TODO find a better way to append editors
|
|
2326
|
-
const parentNodeSelector = '.editor-groups-container, .EditorGroup';
|
|
2327
|
-
await invoke('Layout.attachViewlet', parentNodeSelector, command.instanceId);
|
|
2328
|
-
};
|
|
2329
|
-
|
|
2330
|
-
const getTitle$1 = async editorUid => {
|
|
2331
|
-
try {
|
|
2332
|
-
const title = await invoke('Viewlet.getTitle', editorUid);
|
|
2333
|
-
return typeof title === 'string' && title ? title : undefined;
|
|
2334
|
-
} catch {
|
|
2335
|
-
return undefined;
|
|
2336
|
-
}
|
|
2337
|
-
};
|
|
2338
|
-
const createViewlet = async (viewletModuleId, editorUid, tabId, bounds, uri) => {
|
|
2339
|
-
await invoke('Layout.createViewlet', viewletModuleId, editorUid, tabId, bounds, uri);
|
|
2340
|
-
return getTitle$1(editorUid);
|
|
2341
|
-
};
|
|
2342
|
-
|
|
2343
|
-
const create$1 = () => {
|
|
2344
|
-
return Math.random();
|
|
2345
|
-
};
|
|
2346
|
-
|
|
2347
|
-
const updateTab = (state, tabId, updates) => {
|
|
2348
|
-
const {
|
|
2349
|
-
layout
|
|
2350
|
-
} = state;
|
|
2351
|
-
const {
|
|
2352
|
-
groups
|
|
2353
|
-
} = layout;
|
|
2354
|
-
const updatedGroups = groups.map(group => {
|
|
2355
|
-
const tabIndex = group.tabs.findIndex(t => t.id === tabId);
|
|
2356
|
-
if (tabIndex === -1) {
|
|
2357
|
-
return group;
|
|
2358
|
-
}
|
|
2359
|
-
const updatedTabs = group.tabs.map((tab, index) => {
|
|
2360
|
-
if (index === tabIndex) {
|
|
2361
|
-
return {
|
|
2362
|
-
...tab,
|
|
2363
|
-
...updates
|
|
2364
|
-
};
|
|
2365
|
-
}
|
|
2366
|
-
return tab;
|
|
2367
|
-
});
|
|
2368
|
-
return {
|
|
2369
|
-
...group,
|
|
2370
|
-
tabs: updatedTabs
|
|
2371
|
-
};
|
|
2372
|
-
});
|
|
2373
|
-
return {
|
|
2374
|
-
...state,
|
|
2375
|
-
layout: {
|
|
2376
|
-
...layout,
|
|
2377
|
-
groups: updatedGroups
|
|
2378
|
-
}
|
|
2379
|
-
};
|
|
2380
|
-
};
|
|
2381
|
-
|
|
2382
|
-
const findTab = (state, tabId) => {
|
|
2383
|
-
const {
|
|
2384
|
-
layout
|
|
2385
|
-
} = state;
|
|
2386
|
-
const {
|
|
2387
|
-
groups
|
|
2388
|
-
} = layout;
|
|
2389
|
-
for (const group of groups) {
|
|
2390
|
-
const tab = group.tabs.find(t => t.id === tabId);
|
|
2391
|
-
if (tab) {
|
|
2392
|
-
return tab;
|
|
2393
|
-
}
|
|
2394
|
-
}
|
|
2395
|
-
return undefined;
|
|
2396
|
-
};
|
|
2397
|
-
const loadFileContent = async path => {
|
|
2398
|
-
const content = await readFile(path);
|
|
2399
|
-
return content;
|
|
2400
|
-
};
|
|
2401
|
-
const getLoadFileErrorMessage = error => {
|
|
2402
|
-
if (!(error instanceof Error)) {
|
|
2403
|
-
return 'Failed to load file content';
|
|
2404
|
-
}
|
|
2405
|
-
if (error.message.includes('EISDIR') || error.message.includes('illegal operation on a directory') || error.message.includes('is a directory')) {
|
|
2406
|
-
return 'Expected a file but received a folder';
|
|
2407
|
-
}
|
|
2408
|
-
return error.message;
|
|
2409
|
-
};
|
|
2410
|
-
const loadTabContentAsync = async (tabId, path, requestId, getLatestState) => {
|
|
2411
|
-
try {
|
|
2412
|
-
await loadFileContent(path);
|
|
2413
|
-
|
|
2414
|
-
// Check for race condition: get the latest state
|
|
2415
|
-
const latestState = getLatestState();
|
|
2416
|
-
const latestTab = findTab(latestState, tabId);
|
|
2417
|
-
|
|
2418
|
-
// If the tab no longer exists, discard this result
|
|
2419
|
-
if (!latestTab) {
|
|
2420
|
-
return latestState;
|
|
2421
|
-
}
|
|
2422
|
-
|
|
2423
|
-
// If the tab is no longer in loading state, discard this result (newer request started)
|
|
2424
|
-
if (latestTab.loadingState !== 'loading') {
|
|
2425
|
-
return latestState;
|
|
2426
|
-
}
|
|
2427
|
-
|
|
2428
|
-
// Assign editorUid if tab doesn't have one yet
|
|
2429
|
-
const editorUid = latestTab.editorUid === -1 ? create$1() : latestTab.editorUid;
|
|
2430
|
-
return updateTab(latestState, tabId, {
|
|
2431
|
-
editorUid,
|
|
2432
|
-
errorMessage: undefined,
|
|
2433
|
-
loadingState: 'loaded'
|
|
2434
|
-
});
|
|
2435
|
-
} catch (error) {
|
|
2436
|
-
// Check for race condition before updating with error
|
|
2437
|
-
const latestState = getLatestState();
|
|
2438
|
-
const latestTab = findTab(latestState, tabId);
|
|
2439
|
-
if (!latestTab) {
|
|
2440
|
-
return latestState;
|
|
2441
|
-
}
|
|
2442
|
-
|
|
2443
|
-
// If the tab is no longer in loading state, discard this result (newer request started)
|
|
2444
|
-
if (latestTab.loadingState !== 'loading') {
|
|
2445
|
-
return latestState;
|
|
2446
|
-
}
|
|
2447
|
-
const errorMessage = getLoadFileErrorMessage(error);
|
|
2448
|
-
return updateTab(latestState, tabId, {
|
|
2449
|
-
errorMessage,
|
|
2450
|
-
loadingState: 'error'
|
|
2451
|
-
});
|
|
2475
|
+
}
|
|
2452
2476
|
}
|
|
2477
|
+
return currentState;
|
|
2453
2478
|
};
|
|
2454
2479
|
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
const
|
|
2460
|
-
const
|
|
2461
|
-
|
|
2480
|
+
const getGroupById = (state, groupId) => {
|
|
2481
|
+
return state.layout.groups.find(group => group.id === groupId);
|
|
2482
|
+
};
|
|
2483
|
+
|
|
2484
|
+
const closeTabsRight = (state, groupId) => {
|
|
2485
|
+
const {
|
|
2486
|
+
layout
|
|
2487
|
+
} = state;
|
|
2488
|
+
const {
|
|
2489
|
+
groups
|
|
2490
|
+
} = layout;
|
|
2491
|
+
const group = getGroupById(state, groupId);
|
|
2492
|
+
if (!group) {
|
|
2462
2493
|
return state;
|
|
2463
2494
|
}
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2495
|
+
const {
|
|
2496
|
+
activeTabId,
|
|
2497
|
+
tabs
|
|
2498
|
+
} = group;
|
|
2499
|
+
if (activeTabId === undefined) {
|
|
2467
2500
|
return state;
|
|
2468
2501
|
}
|
|
2469
|
-
const
|
|
2470
|
-
|
|
2471
|
-
|
|
2502
|
+
const activeTabIndex = tabs.findIndex(tab => tab.id === activeTabId);
|
|
2503
|
+
if (activeTabIndex === -1) {
|
|
2504
|
+
return state;
|
|
2505
|
+
}
|
|
2506
|
+
const newTabs = tabs.slice(0, activeTabIndex + 1);
|
|
2507
|
+
if (newTabs.length === tabs.length) {
|
|
2508
|
+
return state;
|
|
2509
|
+
}
|
|
2510
|
+
const groupIndex = groups.findIndex(g => g.id === groupId);
|
|
2511
|
+
const closedTabs = tabs.slice(activeTabIndex + 1).map((tab, index) => ({
|
|
2512
|
+
group,
|
|
2513
|
+
groupIndex,
|
|
2514
|
+
tab,
|
|
2515
|
+
tabIndex: activeTabIndex + index + 1
|
|
2516
|
+
}));
|
|
2517
|
+
const newGroups = groups.map(g => {
|
|
2518
|
+
if (g.id === groupId) {
|
|
2519
|
+
return {
|
|
2520
|
+
...g,
|
|
2521
|
+
isEmpty: newTabs.length === 0,
|
|
2522
|
+
tabs: newTabs
|
|
2523
|
+
};
|
|
2524
|
+
}
|
|
2525
|
+
return g;
|
|
2472
2526
|
});
|
|
2473
|
-
return
|
|
2474
|
-
};
|
|
2475
|
-
|
|
2476
|
-
/**
|
|
2477
|
-
* Called when switching tabs.
|
|
2478
|
-
* With reference nodes, attachment/detachment is handled automatically by virtual DOM rendering.
|
|
2479
|
-
* No attach/detach commands needed - virtual DOM will render the correct reference at each position.
|
|
2480
|
-
*/
|
|
2481
|
-
const switchViewlet = (state, fromTabId, toTabId) => {
|
|
2482
|
-
// No commands needed - virtual DOM reference nodes handle attachment automatically
|
|
2483
|
-
// Virtual DOM will:
|
|
2484
|
-
// 1. Remove the old reference node from the previous active tab
|
|
2485
|
-
// 2. Add the new reference node for the now-active tab
|
|
2486
|
-
// This achieves the same effect as detach/attach without explicit commands
|
|
2487
|
-
|
|
2488
|
-
return {
|
|
2489
|
-
commands: [],
|
|
2490
|
-
newState: state
|
|
2491
|
-
};
|
|
2527
|
+
return withGroups(addClosedTabs(state, closedTabs), newGroups);
|
|
2492
2528
|
};
|
|
2493
2529
|
|
|
2494
|
-
const
|
|
2530
|
+
const findGroupById = (state, groupId) => {
|
|
2495
2531
|
const {
|
|
2496
2532
|
layout
|
|
2497
2533
|
} = state;
|
|
2498
2534
|
const {
|
|
2499
2535
|
groups
|
|
2500
2536
|
} = layout;
|
|
2501
|
-
|
|
2502
|
-
const tab = group.tabs.find(t => t.editorUid === editorUid);
|
|
2503
|
-
if (tab) {
|
|
2504
|
-
return tab;
|
|
2505
|
-
}
|
|
2506
|
-
}
|
|
2507
|
-
return undefined;
|
|
2537
|
+
return groups.find(group => group.id === groupId);
|
|
2508
2538
|
};
|
|
2509
2539
|
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
throw new Error('Invalid editorUid -1');
|
|
2540
|
+
const copyIntoNewWindow$1 = async state => {
|
|
2541
|
+
const {
|
|
2542
|
+
activeGroupId
|
|
2543
|
+
} = state.layout;
|
|
2544
|
+
const group = findGroupById(state, activeGroupId ?? 0);
|
|
2545
|
+
if (!group) {
|
|
2546
|
+
return state;
|
|
2518
2547
|
}
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2548
|
+
const {
|
|
2549
|
+
activeTabId
|
|
2550
|
+
} = group;
|
|
2551
|
+
if (activeTabId === undefined) {
|
|
2523
2552
|
return state;
|
|
2524
2553
|
}
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
...(title && {
|
|
2532
|
-
title
|
|
2533
|
-
})
|
|
2534
|
-
});
|
|
2535
|
-
|
|
2536
|
-
// No attach commands needed - virtual DOM reference nodes handle positioning
|
|
2537
|
-
return newState;
|
|
2554
|
+
const tab = group.tabs.find(tab => tab.id === activeTabId);
|
|
2555
|
+
if (!tab?.uri) {
|
|
2556
|
+
return state;
|
|
2557
|
+
}
|
|
2558
|
+
await invoke('ElectronWindow.openNewWithUri', tab.uri);
|
|
2559
|
+
return state;
|
|
2538
2560
|
};
|
|
2539
2561
|
|
|
2540
|
-
const
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
// After viewlet is created, mark it as ready
|
|
2546
|
-
// Attachment is handled automatically by virtual DOM reference nodes
|
|
2547
|
-
const {
|
|
2548
|
-
newState: state,
|
|
2549
|
-
oldState
|
|
2550
|
-
} = get(command.uid);
|
|
2551
|
-
const readyState = handleViewletReady(state, command.editorUid, title);
|
|
2552
|
-
set(command.uid, oldState, readyState);
|
|
2553
|
-
return readyState;
|
|
2562
|
+
const copyPath$1 = async (state, path) => {
|
|
2563
|
+
string(path);
|
|
2564
|
+
await writeClipBoardText(path);
|
|
2565
|
+
return state;
|
|
2554
2566
|
};
|
|
2555
2567
|
|
|
2556
|
-
const
|
|
2557
|
-
|
|
2568
|
+
const copyRelativePath$1 = async (state, path) => {
|
|
2569
|
+
string(path);
|
|
2570
|
+
// TODO use clipboard worker
|
|
2571
|
+
const relativePath = await invoke('Workspace.pathBaseName', path);
|
|
2572
|
+
await invoke('ClipBoard.writeText', relativePath);
|
|
2573
|
+
return state;
|
|
2574
|
+
};
|
|
2558
2575
|
|
|
2559
|
-
|
|
2576
|
+
const Horizontal = 1;
|
|
2577
|
+
const Vertical = 2;
|
|
2578
|
+
const isLayoutDirection = direction => {
|
|
2579
|
+
return direction === Horizontal || direction === Vertical;
|
|
2580
|
+
};
|
|
2581
|
+
const normalizeLayoutDirection = direction => {
|
|
2582
|
+
if (isLayoutDirection(direction)) {
|
|
2583
|
+
return direction;
|
|
2584
|
+
}
|
|
2585
|
+
if (direction === 'horizontal') {
|
|
2586
|
+
return Horizontal;
|
|
2587
|
+
}
|
|
2588
|
+
if (direction === 'vertical') {
|
|
2589
|
+
return Vertical;
|
|
2590
|
+
}
|
|
2591
|
+
return undefined;
|
|
2560
2592
|
};
|
|
2561
2593
|
|
|
2562
|
-
const
|
|
2563
|
-
|
|
2594
|
+
const create$1 = (uid, uri, x, y, width, height, platform, assetDir, tabHeight = 35) => {
|
|
2595
|
+
const state = {
|
|
2596
|
+
assetDir,
|
|
2597
|
+
closedTabs: [],
|
|
2598
|
+
fileIconCache: {},
|
|
2599
|
+
height,
|
|
2600
|
+
homeDirUri: '',
|
|
2601
|
+
iframes: [],
|
|
2602
|
+
initial: true,
|
|
2603
|
+
layout: {
|
|
2604
|
+
activeGroupId: undefined,
|
|
2605
|
+
direction: Horizontal,
|
|
2606
|
+
groups: []
|
|
2607
|
+
},
|
|
2608
|
+
maxOpenEditorGroups: Infinity,
|
|
2609
|
+
maxOpenEditors: Infinity,
|
|
2610
|
+
minGroupHeightPx: 80,
|
|
2611
|
+
minGroupWidthPx: 250,
|
|
2612
|
+
platform,
|
|
2613
|
+
splitButtonEnabled: false,
|
|
2614
|
+
tabHeight,
|
|
2615
|
+
uid,
|
|
2616
|
+
width,
|
|
2617
|
+
workspaceuri: uri,
|
|
2618
|
+
x,
|
|
2619
|
+
y
|
|
2620
|
+
};
|
|
2621
|
+
set(uid, state, state);
|
|
2622
|
+
};
|
|
2564
2623
|
|
|
2565
|
-
|
|
2624
|
+
const isEqual$1 = (oldState, newState) => {
|
|
2625
|
+
return false;
|
|
2566
2626
|
};
|
|
2567
2627
|
|
|
2568
|
-
const
|
|
2569
|
-
|
|
2628
|
+
const isEqual = (oldState, newState) => {
|
|
2629
|
+
return oldState.layout === newState.layout;
|
|
2570
2630
|
};
|
|
2571
2631
|
|
|
2572
|
-
const
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
case 'setBounds':
|
|
2587
|
-
await handleSetBounds(command);
|
|
2588
|
-
return;
|
|
2632
|
+
const RenderItems = 4;
|
|
2633
|
+
const RenderCss = 10;
|
|
2634
|
+
const RenderIncremental = 11;
|
|
2635
|
+
|
|
2636
|
+
const modules = [isEqual, isEqual$1];
|
|
2637
|
+
const numbers = [RenderIncremental, RenderCss];
|
|
2638
|
+
|
|
2639
|
+
const diff = (oldState, newState) => {
|
|
2640
|
+
const diffResult = [];
|
|
2641
|
+
for (let i = 0; i < modules.length; i++) {
|
|
2642
|
+
const fn = modules[i];
|
|
2643
|
+
if (!fn(oldState, newState)) {
|
|
2644
|
+
diffResult.push(numbers[i]);
|
|
2645
|
+
}
|
|
2589
2646
|
}
|
|
2647
|
+
return diffResult;
|
|
2590
2648
|
};
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2649
|
+
|
|
2650
|
+
const diff2 = uid => {
|
|
2651
|
+
const {
|
|
2652
|
+
newState,
|
|
2653
|
+
oldState
|
|
2654
|
+
} = get(uid);
|
|
2655
|
+
const result = diff(oldState, newState);
|
|
2656
|
+
return result;
|
|
2595
2657
|
};
|
|
2596
2658
|
|
|
2597
2659
|
const requestIdState = {
|
|
@@ -3485,9 +3547,9 @@ const createEmptyGroup$1 = (state, uri, requestId, preview = false, title = getL
|
|
|
3485
3547
|
const {
|
|
3486
3548
|
groups
|
|
3487
3549
|
} = layout;
|
|
3488
|
-
const groupId = create$
|
|
3489
|
-
const tabId = create$
|
|
3490
|
-
const editorUid = create$
|
|
3550
|
+
const groupId = create$2();
|
|
3551
|
+
const tabId = create$2();
|
|
3552
|
+
const editorUid = create$2();
|
|
3491
3553
|
const newTab = {
|
|
3492
3554
|
editorInput,
|
|
3493
3555
|
editorType,
|
|
@@ -3528,7 +3590,7 @@ const openTab = (state, groupId, tab) => {
|
|
|
3528
3590
|
};
|
|
3529
3591
|
const newTab = 'id' in normalizedTab && normalizedTab.id !== undefined ? normalizedTab : {
|
|
3530
3592
|
...normalizedTab,
|
|
3531
|
-
id: create$
|
|
3593
|
+
id: create$2()
|
|
3532
3594
|
};
|
|
3533
3595
|
const {
|
|
3534
3596
|
layout
|
|
@@ -3615,8 +3677,8 @@ const ensureActiveGroup = (state, uri, preview = false, title = getLabel(uri), e
|
|
|
3615
3677
|
}
|
|
3616
3678
|
|
|
3617
3679
|
// Create a new tab with the URI in the active group
|
|
3618
|
-
const tabId = create$
|
|
3619
|
-
const editorUid = create$
|
|
3680
|
+
const tabId = create$2();
|
|
3681
|
+
const editorUid = create$2();
|
|
3620
3682
|
const newTab = {
|
|
3621
3683
|
editorInput,
|
|
3622
3684
|
editorType,
|
|
@@ -4149,7 +4211,7 @@ const splitEditorGroup$1 = (state, groupId, direction) => {
|
|
|
4149
4211
|
if (!sourceGroup) {
|
|
4150
4212
|
return state;
|
|
4151
4213
|
}
|
|
4152
|
-
const newGroupId = create$
|
|
4214
|
+
const newGroupId = create$2();
|
|
4153
4215
|
const splitLayoutDirection = getSplitLayoutDirection(direction);
|
|
4154
4216
|
const baseNewGroup = {
|
|
4155
4217
|
activeTabId: undefined,
|
|
@@ -4366,8 +4428,8 @@ const newFile = async state => {
|
|
|
4366
4428
|
getActiveTabId(newState);
|
|
4367
4429
|
|
|
4368
4430
|
// Create a new empty tab
|
|
4369
|
-
const tabId = create$
|
|
4370
|
-
const editorUid = create$
|
|
4431
|
+
const tabId = create$2();
|
|
4432
|
+
const editorUid = create$2();
|
|
4371
4433
|
const newTab = {
|
|
4372
4434
|
editorType: 'text',
|
|
4373
4435
|
editorUid,
|
|
@@ -4861,7 +4923,7 @@ const createViewlets = async (layout, viewletModuleIds, bounds) => {
|
|
|
4861
4923
|
for (const group of layout.groups) {
|
|
4862
4924
|
const activeTab = group.tabs.find(tab => tab.id === group.activeTabId);
|
|
4863
4925
|
if (activeTab && viewletModuleIds[activeTab.id]) {
|
|
4864
|
-
const editorUid = activeTab.editorUid === -1 ? create$
|
|
4926
|
+
const editorUid = activeTab.editorUid === -1 ? create$2() : activeTab.editorUid;
|
|
4865
4927
|
editorUids[activeTab.id] = editorUid;
|
|
4866
4928
|
const title = await createViewlet(viewletModuleIds[activeTab.id], editorUid, activeTab.id, bounds, activeTab.uri);
|
|
4867
4929
|
if (title) {
|
|
@@ -5049,15 +5111,15 @@ const getMenuEntryArgs = groupId => {
|
|
|
5049
5111
|
};
|
|
5050
5112
|
|
|
5051
5113
|
const emptyObject = {};
|
|
5052
|
-
const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
|
|
5053
5114
|
const i18nString = (key, placeholders = emptyObject) => {
|
|
5054
5115
|
if (placeholders === emptyObject) {
|
|
5055
5116
|
return key;
|
|
5056
5117
|
}
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5118
|
+
let result = key;
|
|
5119
|
+
for (const [placeholder, replacement] of Object.entries(placeholders)) {
|
|
5120
|
+
result = result.split(`{${placeholder}}`).join(String(replacement));
|
|
5121
|
+
}
|
|
5122
|
+
return result;
|
|
5061
5123
|
};
|
|
5062
5124
|
|
|
5063
5125
|
const Close = 'Close';
|
|
@@ -6500,7 +6562,7 @@ const createEmptyGroup = () => {
|
|
|
6500
6562
|
return {
|
|
6501
6563
|
activeTabId: undefined,
|
|
6502
6564
|
focused: false,
|
|
6503
|
-
id: create$
|
|
6565
|
+
id: create$2(),
|
|
6504
6566
|
isEmpty: true,
|
|
6505
6567
|
size: 100,
|
|
6506
6568
|
tabs: []
|
|
@@ -6675,7 +6737,7 @@ const splitDown = (state, groupId) => {
|
|
|
6675
6737
|
|
|
6676
6738
|
// If there are no groups, create an initial empty group first
|
|
6677
6739
|
if (groups.length === 0) {
|
|
6678
|
-
const initialGroupId = create$
|
|
6740
|
+
const initialGroupId = create$2();
|
|
6679
6741
|
const initialGroup = {
|
|
6680
6742
|
activeTabId: undefined,
|
|
6681
6743
|
focused: true,
|
|
@@ -6710,7 +6772,7 @@ const splitLeft = (state, groupId) => {
|
|
|
6710
6772
|
groups
|
|
6711
6773
|
} = layout;
|
|
6712
6774
|
if (groups.length === 0) {
|
|
6713
|
-
const initialGroupId = create$
|
|
6775
|
+
const initialGroupId = create$2();
|
|
6714
6776
|
const initialGroup = {
|
|
6715
6777
|
activeTabId: undefined,
|
|
6716
6778
|
focused: true,
|
|
@@ -6744,7 +6806,7 @@ const splitRight = (state, groupId) => {
|
|
|
6744
6806
|
|
|
6745
6807
|
// If there are no groups, create an initial empty group first
|
|
6746
6808
|
if (groups.length === 0) {
|
|
6747
|
-
const initialGroupId = create$
|
|
6809
|
+
const initialGroupId = create$2();
|
|
6748
6810
|
const initialGroup = {
|
|
6749
6811
|
activeTabId: undefined,
|
|
6750
6812
|
focused: true,
|
|
@@ -6775,7 +6837,7 @@ const splitUp = (state, groupId) => {
|
|
|
6775
6837
|
groups
|
|
6776
6838
|
} = layout;
|
|
6777
6839
|
if (groups.length === 0) {
|
|
6778
|
-
const initialGroupId = create$
|
|
6840
|
+
const initialGroupId = create$2();
|
|
6779
6841
|
const initialGroup = {
|
|
6780
6842
|
activeTabId: undefined,
|
|
6781
6843
|
focused: true,
|
|
@@ -6838,7 +6900,7 @@ const commandMap = {
|
|
|
6838
6900
|
'MainArea.copyIntoNewWindow': wrapSerialCommand(copyIntoNewWindow$1),
|
|
6839
6901
|
'MainArea.copyPath': wrapSerialCommand(copyPath$1),
|
|
6840
6902
|
'MainArea.copyRelativePath': wrapSerialCommand(copyRelativePath$1),
|
|
6841
|
-
'MainArea.create': create$
|
|
6903
|
+
'MainArea.create': create$1,
|
|
6842
6904
|
'MainArea.diff2': diff2,
|
|
6843
6905
|
'MainArea.flipEditorLayout': wrapSerialCommand(flipLayout),
|
|
6844
6906
|
'MainArea.focusNext': wrapSerialCommand(focusNextTab),
|