@lvce-editor/main-area-worker 9.8.0 → 9.11.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.
@@ -379,6 +379,26 @@ const closeSaved$1 = state => {
379
379
  return withGroups(state, newGroups);
380
380
  };
381
381
 
382
+ const closeTabsByUris = (state, uris) => {
383
+ let currentState = state;
384
+ for (const uri of uris) {
385
+ while (true) {
386
+ const matchingGroup = currentState.layout.groups.find(group => {
387
+ return group.tabs.some(tab => tab.uri === uri);
388
+ });
389
+ if (!matchingGroup) {
390
+ break;
391
+ }
392
+ const matchingTab = matchingGroup.tabs.find(tab => tab.uri === uri);
393
+ if (!matchingTab) {
394
+ break;
395
+ }
396
+ currentState = closeTab(currentState, matchingGroup.id, matchingTab.id);
397
+ }
398
+ }
399
+ return currentState;
400
+ };
401
+
382
402
  const getGroupById = (state, groupId) => {
383
403
  return state.layout.groups.find(group => group.id === groupId);
384
404
  };
@@ -1947,6 +1967,15 @@ const loadFileContent = async path => {
1947
1967
  const content = await readFile(path);
1948
1968
  return content;
1949
1969
  };
1970
+ const getLoadFileErrorMessage = error => {
1971
+ if (!(error instanceof Error)) {
1972
+ return 'Failed to load file content';
1973
+ }
1974
+ if (error.message.includes('EISDIR') || error.message.includes('illegal operation on a directory') || error.message.includes('is a directory')) {
1975
+ return 'Expected a file but received a folder';
1976
+ }
1977
+ return error.message;
1978
+ };
1950
1979
  const loadTabContentAsync = async (tabId, path, requestId, getLatestState) => {
1951
1980
  try {
1952
1981
  await loadFileContent(path);
@@ -1984,7 +2013,7 @@ const loadTabContentAsync = async (tabId, path, requestId, getLatestState) => {
1984
2013
  if (latestTab.loadingState !== 'loading') {
1985
2014
  return latestState;
1986
2015
  }
1987
- const errorMessage = error instanceof Error ? error.message : 'Failed to load file content';
2016
+ const errorMessage = getLoadFileErrorMessage(error);
1988
2017
  return updateTab(latestState, tabId, {
1989
2018
  errorMessage,
1990
2019
  loadingState: 'error'
@@ -2479,6 +2508,97 @@ const RetryOpen = 'retry-open';
2479
2508
  const SplitRight$1 = 'split-right';
2480
2509
  const TogglePreview$1 = 'toggle-preview';
2481
2510
 
2511
+ const parseRawGroupId = rawGroupId => {
2512
+ if (!rawGroupId) {
2513
+ return undefined;
2514
+ }
2515
+ const groupId = Number.parseFloat(rawGroupId);
2516
+ return Number.isNaN(groupId) ? undefined : groupId;
2517
+ };
2518
+
2519
+ const findTabByUri = (state, uri) => {
2520
+ const {
2521
+ layout
2522
+ } = state;
2523
+ const {
2524
+ groups
2525
+ } = layout;
2526
+ for (const group of groups) {
2527
+ const tab = group.tabs.find(t => t.uri === uri);
2528
+ if (tab) {
2529
+ return {
2530
+ groupId: group.id,
2531
+ tab
2532
+ };
2533
+ }
2534
+ }
2535
+ return undefined;
2536
+ };
2537
+
2538
+ const focusEditorGroup = (state, groupId) => {
2539
+ const {
2540
+ layout
2541
+ } = state;
2542
+ const {
2543
+ groups
2544
+ } = layout;
2545
+ const updatedGroups = groups.map(group => ({
2546
+ ...group,
2547
+ focused: group.id === groupId
2548
+ }));
2549
+ return {
2550
+ ...state,
2551
+ layout: {
2552
+ ...layout,
2553
+ activeGroupId: groupId,
2554
+ groups: updatedGroups
2555
+ }
2556
+ };
2557
+ };
2558
+
2559
+ const getActiveTabId = state => {
2560
+ const {
2561
+ layout
2562
+ } = state;
2563
+ const {
2564
+ activeGroupId,
2565
+ groups
2566
+ } = layout;
2567
+ const activeGroup = groups.find(g => g.id === activeGroupId);
2568
+ return activeGroup?.activeTabId;
2569
+ };
2570
+
2571
+ const getTabCount = state => {
2572
+ return state.layout.groups.reduce((sum, group) => sum + group.tabs.length, 0);
2573
+ };
2574
+
2575
+ const getCurrentState = state => {
2576
+ const {
2577
+ uid
2578
+ } = state;
2579
+ const stateFromStore = get(uid);
2580
+ if (!stateFromStore) {
2581
+ set(uid, state, state);
2582
+ return state;
2583
+ }
2584
+ const storedState = stateFromStore.newState;
2585
+ if (getTabCount(storedState) > getTabCount(state)) {
2586
+ return storedState;
2587
+ }
2588
+ set(uid, state, state);
2589
+ return state;
2590
+ };
2591
+
2592
+ const getEditorInputEditorType = editorInput => {
2593
+ switch (editorInput.type) {
2594
+ case 'diff-editor':
2595
+ case 'extension-detail-view':
2596
+ return 'custom';
2597
+ case 'editor':
2598
+ return 'text';
2599
+ }
2600
+ };
2601
+
2482
2602
  const getBasename$1 = uri => {
2483
2603
  const lastSlashIndex = uri.lastIndexOf('/');
2484
2604
  if (lastSlashIndex === -1) {
@@ -2513,6 +2633,32 @@ const getLabel = uri => {
2513
2633
  return getBasename$1(uri);
2514
2634
  };
2515
2635
 
2636
+ const getEditorInputTitle = editorInput => {
2637
+ switch (editorInput.type) {
2638
+ case 'diff-editor':
2639
+ {
2640
+ const leftTitle = getLabel(editorInput.uriLeft);
2641
+ const rightTitle = getLabel(editorInput.uriRight);
2642
+ return `${leftTitle} - ${rightTitle}`;
2643
+ }
2644
+ case 'editor':
2645
+ return getLabel(editorInput.uri);
2646
+ case 'extension-detail-view':
2647
+ return editorInput.extensionId;
2648
+ }
2649
+ };
2650
+
2651
+ const getEditorInputUri = editorInput => {
2652
+ switch (editorInput.type) {
2653
+ case 'diff-editor':
2654
+ return `diff://?left=${encodeURIComponent(editorInput.uriLeft)}&right=${encodeURIComponent(editorInput.uriRight)}`;
2655
+ case 'editor':
2656
+ return editorInput.uri;
2657
+ case 'extension-detail-view':
2658
+ return `extension-detail://${editorInput.extensionId}`;
2659
+ }
2660
+ };
2661
+
2516
2662
  const createEmptyGroup = (state, uri, requestId, preview = false, title = getLabel(uri), editorType = 'text', editorInput) => {
2517
2663
  const {
2518
2664
  layout
@@ -2671,94 +2817,85 @@ const ensureActiveGroup = (state, uri, preview = false, title = getLabel(uri), e
2671
2817
  return newState;
2672
2818
  };
2673
2819
 
2674
- const findTabByUri = (state, uri) => {
2675
- const {
2676
- layout
2677
- } = state;
2678
- const {
2679
- groups
2680
- } = layout;
2681
- for (const group of groups) {
2682
- const tab = group.tabs.find(t => t.uri === uri);
2683
- if (tab) {
2684
- return {
2685
- groupId: group.id,
2686
- tab
2687
- };
2688
- }
2820
+ const getStateWithTab = (currentState, editorInput, existingTab, shouldRetryExistingTab, uri, preview, title, editorType) => {
2821
+ if (shouldRetryExistingTab && existingTab) {
2822
+ const focusedState = focusEditorGroup(currentState, existingTab.groupId);
2823
+ return {
2824
+ stateWithTab: updateTab(focusedState, existingTab.tab.id, {
2825
+ editorInput,
2826
+ errorMessage: '',
2827
+ loadingState: 'loading',
2828
+ title,
2829
+ uri
2830
+ }),
2831
+ tabId: existingTab.tab.id
2832
+ };
2689
2833
  }
2690
- return undefined;
2834
+ const stateWithTab = ensureActiveGroup(currentState, uri, preview, title, editorType, editorInput);
2835
+ return {
2836
+ stateWithTab,
2837
+ tabId: getActiveTabId(stateWithTab)
2838
+ };
2691
2839
  };
2692
2840
 
2693
- const focusEditorGroup = (state, groupId) => {
2841
+ const Directory = 3;
2842
+ const DirectoryExpanded = 4;
2843
+
2844
+ const isLocalEditorInput = editorInput => {
2845
+ if (editorInput.type !== 'editor') {
2846
+ return false;
2847
+ }
2848
+ return editorInput.uri.startsWith('file://') || !editorInput.uri.includes('://');
2849
+ };
2850
+
2851
+ const shouldCheckDirectoryEditorInput = editorInput => {
2852
+ if (!isLocalEditorInput(editorInput)) {
2853
+ return false;
2854
+ }
2855
+ const baseName = editorInput.uri.slice(editorInput.uri.lastIndexOf('/') + 1);
2856
+ return baseName.endsWith('/') || baseName !== '' && !baseName.includes('.');
2857
+ };
2858
+
2859
+ const isDirectoryEditorInput = async editorInput => {
2860
+ if (!shouldCheckDirectoryEditorInput(editorInput)) {
2861
+ return false;
2862
+ }
2863
+ try {
2864
+ const type = await invoke('FileSystem.stat', editorInput.uri);
2865
+ return type === Directory;
2866
+ } catch {
2867
+ return false;
2868
+ }
2869
+ };
2870
+
2871
+ const switchTab = (state, groupId, tabId) => {
2694
2872
  const {
2695
2873
  layout
2696
2874
  } = state;
2697
2875
  const {
2698
2876
  groups
2699
2877
  } = layout;
2700
- const updatedGroups = groups.map(group => ({
2701
- ...group,
2702
- focused: group.id === groupId
2703
- }));
2878
+ const updatedGroups = groups.map(group => {
2879
+ if (group.id === groupId) {
2880
+ const tabExists = group.tabs.some(tab => tab.id === tabId);
2881
+ if (tabExists) {
2882
+ return {
2883
+ ...group,
2884
+ activeTabId: tabId
2885
+ };
2886
+ }
2887
+ }
2888
+ return group;
2889
+ });
2704
2890
  return {
2705
2891
  ...state,
2706
2892
  layout: {
2707
2893
  ...layout,
2708
- activeGroupId: groupId,
2709
2894
  groups: updatedGroups
2710
2895
  }
2711
2896
  };
2712
2897
  };
2713
2898
 
2714
- const getActiveTabId = state => {
2715
- const {
2716
- layout
2717
- } = state;
2718
- const {
2719
- activeGroupId,
2720
- groups
2721
- } = layout;
2722
- const activeGroup = groups.find(g => g.id === activeGroupId);
2723
- return activeGroup?.activeTabId;
2724
- };
2725
-
2726
- const getEditorInputEditorType = editorInput => {
2727
- switch (editorInput.type) {
2728
- case 'diff-editor':
2729
- case 'extension-detail-view':
2730
- return 'custom';
2731
- case 'editor':
2732
- return 'text';
2733
- }
2734
- };
2735
-
2736
- const getEditorInputTitle = editorInput => {
2737
- switch (editorInput.type) {
2738
- case 'diff-editor':
2739
- {
2740
- const leftTitle = getLabel(editorInput.uriLeft);
2741
- const rightTitle = getLabel(editorInput.uriRight);
2742
- return `${leftTitle} - ${rightTitle}`;
2743
- }
2744
- case 'editor':
2745
- return getLabel(editorInput.uri);
2746
- case 'extension-detail-view':
2747
- return editorInput.extensionId;
2748
- }
2749
- };
2750
-
2751
- const getEditorInputUri = editorInput => {
2752
- switch (editorInput.type) {
2753
- case 'diff-editor':
2754
- return `diff://?left=${encodeURIComponent(editorInput.uriLeft)}&right=${encodeURIComponent(editorInput.uriRight)}`;
2755
- case 'editor':
2756
- return editorInput.uri;
2757
- case 'extension-detail-view':
2758
- return `extension-detail://${editorInput.extensionId}`;
2759
- }
2760
- };
2761
-
2762
2899
  const getIconsCached = (dirents, fileIconCache) => {
2763
2900
  return dirents.map(dirent => fileIconCache[dirent]);
2764
2901
  };
@@ -2793,9 +2930,6 @@ const getMissingIconRequestsForTabs = (tabs, fileIconCache) => {
2793
2930
  return iconRequests;
2794
2931
  };
2795
2932
 
2796
- const Directory = 3;
2797
- const DirectoryExpanded = 4;
2798
-
2799
2933
  const getSimpleIconRequestType = direntType => {
2800
2934
  if (direntType === Directory || direntType === DirectoryExpanded) {
2801
2935
  return 2;
@@ -2846,73 +2980,6 @@ const getFileIconsForTabs = async (tabs, fileIconCache) => {
2846
2980
  };
2847
2981
  };
2848
2982
 
2849
- const switchTab = (state, groupId, tabId) => {
2850
- const {
2851
- layout
2852
- } = state;
2853
- const {
2854
- groups
2855
- } = layout;
2856
- const updatedGroups = groups.map(group => {
2857
- if (group.id === groupId) {
2858
- const tabExists = group.tabs.some(tab => tab.id === tabId);
2859
- if (tabExists) {
2860
- return {
2861
- ...group,
2862
- activeTabId: tabId
2863
- };
2864
- }
2865
- }
2866
- return group;
2867
- });
2868
- return {
2869
- ...state,
2870
- layout: {
2871
- ...layout,
2872
- groups: updatedGroups
2873
- }
2874
- };
2875
- };
2876
-
2877
- const getTabCount = state => {
2878
- return state.layout.groups.reduce((sum, group) => sum + group.tabs.length, 0);
2879
- };
2880
- const getCurrentState = state => {
2881
- const {
2882
- uid
2883
- } = state;
2884
- const stateFromStore = get(uid);
2885
- if (!stateFromStore) {
2886
- set(uid, state, state);
2887
- return state;
2888
- }
2889
- const storedState = stateFromStore.newState;
2890
- if (getTabCount(storedState) > getTabCount(state)) {
2891
- return storedState;
2892
- }
2893
- set(uid, state, state);
2894
- return state;
2895
- };
2896
- const getStateWithTab = (currentState, editorInput, existingTab, shouldRetryExistingTab, uri, preview, title, editorType) => {
2897
- if (shouldRetryExistingTab && existingTab) {
2898
- const focusedState = focusEditorGroup(currentState, existingTab.groupId);
2899
- return {
2900
- stateWithTab: updateTab(focusedState, existingTab.tab.id, {
2901
- editorInput,
2902
- errorMessage: '',
2903
- loadingState: 'loading',
2904
- title,
2905
- uri
2906
- }),
2907
- tabId: existingTab.tab.id
2908
- };
2909
- }
2910
- const stateWithTab = ensureActiveGroup(currentState, uri, preview, title, editorType, editorInput);
2911
- return {
2912
- stateWithTab,
2913
- tabId: getActiveTabId(stateWithTab)
2914
- };
2915
- };
2916
2983
  const updateTabIcon = async (uid, state, readyState, tabId) => {
2917
2984
  const newTab = findTabById(readyState, tabId);
2918
2985
  if (!newTab || !newTab.tab.uri) {
@@ -2946,6 +3013,7 @@ const updateTabIcon = async (uid, state, readyState, tabId) => {
2946
3013
  return undefined;
2947
3014
  }
2948
3015
  };
3016
+
2949
3017
  const openInput = async (state, options) => {
2950
3018
  object(state);
2951
3019
  object(options);
@@ -2959,19 +3027,30 @@ const openInput = async (state, options) => {
2959
3027
  const uri = getEditorInputUri(editorInput);
2960
3028
  const title = getEditorInputTitle(editorInput);
2961
3029
  const editorType = getEditorInputEditorType(editorInput);
2962
- const existingTab = findTabByUri(state, uri);
3030
+ const currentState = getCurrentState(state);
3031
+ const existingTab = findTabByUri(currentState, uri);
2963
3032
  const shouldRetryExistingTab = !!existingTab && existingTab.tab.loadingState === 'error';
2964
3033
  if (existingTab && !shouldRetryExistingTab) {
2965
- const focusedState = focusEditorGroup(state, existingTab.groupId);
3034
+ const focusedState = focusEditorGroup(currentState, existingTab.groupId);
2966
3035
  return switchTab(focusedState, existingTab.groupId, existingTab.tab.id);
2967
3036
  }
2968
- const previousTabId = getActiveTabId(state);
2969
- const currentState = getCurrentState(state);
3037
+ const previousTabId = getActiveTabId(currentState);
2970
3038
  const {
2971
3039
  stateWithTab,
2972
3040
  tabId
2973
3041
  } = getStateWithTab(currentState, editorInput, existingTab, shouldRetryExistingTab, uri, preview, title, editorType);
2974
3042
  set(uid, state, stateWithTab);
3043
+ if (await isDirectoryEditorInput(editorInput)) {
3044
+ const {
3045
+ newState: latestState
3046
+ } = get(uid);
3047
+ const errorState = updateTab(latestState, tabId, {
3048
+ errorMessage: 'Expected a file but received a folder',
3049
+ loadingState: 'error'
3050
+ });
3051
+ set(uid, state, errorState);
3052
+ return errorState;
3053
+ }
2975
3054
  try {
2976
3055
  const viewletModuleId = await getViewletModuleIdForEditorInput(editorInput);
2977
3056
  const {
@@ -3128,6 +3207,39 @@ const retryOpen = async state => {
3128
3207
  return openUri(state, tab.uri);
3129
3208
  };
3130
3209
 
3210
+ const getSegmentSize = segment => {
3211
+ return segment.groups.reduce((total, group) => total + group.size, 0);
3212
+ };
3213
+ const getGroupSegments = (groups, parentDirection) => {
3214
+ const segments = [];
3215
+ let index = 0;
3216
+ while (index < groups.length) {
3217
+ const group = groups[index];
3218
+ if (group.direction !== undefined && group.direction !== parentDirection) {
3219
+ const startIndex = index;
3220
+ const nestedGroups = [group];
3221
+ index++;
3222
+ while (index < groups.length && groups[index].direction === group.direction) {
3223
+ nestedGroups.push(groups[index]);
3224
+ index++;
3225
+ }
3226
+ segments.push({
3227
+ direction: group.direction,
3228
+ groups: nestedGroups,
3229
+ startIndex
3230
+ });
3231
+ continue;
3232
+ }
3233
+ segments.push({
3234
+ direction: undefined,
3235
+ groups: [group],
3236
+ startIndex: index
3237
+ });
3238
+ index++;
3239
+ }
3240
+ return segments;
3241
+ };
3242
+
3131
3243
  const rebalanceGroupSizes = groups => {
3132
3244
  const groupCount = groups.length;
3133
3245
  if (groupCount === 0) {
@@ -3141,6 +3253,111 @@ const rebalanceGroupSizes = groups => {
3141
3253
  size: index === groupCount - 1 ? lastSize : evenSize
3142
3254
  }));
3143
3255
  };
3256
+ const isTrailingSplit = direction => {
3257
+ return direction === Right || direction === 'down';
3258
+ };
3259
+ const getSplitLayoutDirection = direction => {
3260
+ return direction === Left || direction === Right ? Horizontal : Vertical;
3261
+ };
3262
+ const createNextState = (state, activeGroupId, groups) => {
3263
+ return {
3264
+ ...state,
3265
+ layout: {
3266
+ activeGroupId,
3267
+ direction: state.layout.direction,
3268
+ groups
3269
+ }
3270
+ };
3271
+ };
3272
+ const splitOnlyGroup = (state, groups, groupId, newGroupId, direction, splitLayoutDirection, baseNewGroup) => {
3273
+ const updatedGroups = groups.map(group => {
3274
+ if (group.id === groupId) {
3275
+ return {
3276
+ ...group,
3277
+ direction: undefined,
3278
+ focused: false,
3279
+ size: 50
3280
+ };
3281
+ }
3282
+ return group;
3283
+ });
3284
+ const newGroup = {
3285
+ ...baseNewGroup,
3286
+ direction: undefined,
3287
+ size: 50
3288
+ };
3289
+ const reorderedGroups = isTrailingSplit(direction) ? [...updatedGroups, newGroup] : [newGroup, ...updatedGroups];
3290
+ return {
3291
+ ...state,
3292
+ layout: {
3293
+ activeGroupId: newGroupId,
3294
+ direction: splitLayoutDirection,
3295
+ groups: reorderedGroups
3296
+ }
3297
+ };
3298
+ };
3299
+ const splitWithinMatchingNestedDirection = (state, groups, sourceGroup, groupId, newGroupId, direction, splitLayoutDirection, baseNewGroup) => {
3300
+ const sourceIndex = groups.findIndex(group => group.id === groupId);
3301
+ const updatedGroups = groups.map(group => {
3302
+ if (group.id === groupId) {
3303
+ return {
3304
+ ...group,
3305
+ focused: false,
3306
+ size: Number((group.size / 2).toFixed(6))
3307
+ };
3308
+ }
3309
+ return group;
3310
+ });
3311
+ const newGroup = {
3312
+ ...baseNewGroup,
3313
+ direction: splitLayoutDirection,
3314
+ size: Number((sourceGroup.size / 2).toFixed(6))
3315
+ };
3316
+ const insertIndex = isTrailingSplit(direction) ? sourceIndex + 1 : sourceIndex;
3317
+ const reorderedGroups = [...updatedGroups.slice(0, insertIndex), newGroup, ...updatedGroups.slice(insertIndex)];
3318
+ return createNextState(state, newGroupId, reorderedGroups);
3319
+ };
3320
+ const splitStandaloneSourceIntoNestedDirection = (state, groups, sourceGroup, groupId, newGroupId, direction, splitLayoutDirection, baseNewGroup) => {
3321
+ const sourceIndex = groups.findIndex(group => group.id === groupId);
3322
+ const halfSize = Number((sourceGroup.size / 2).toFixed(6));
3323
+ const updatedSourceGroup = {
3324
+ ...sourceGroup,
3325
+ direction: splitLayoutDirection,
3326
+ focused: false,
3327
+ size: halfSize
3328
+ };
3329
+ const newGroup = {
3330
+ ...baseNewGroup,
3331
+ direction: splitLayoutDirection,
3332
+ size: Number((sourceGroup.size - halfSize).toFixed(6))
3333
+ };
3334
+ const replacementGroups = isTrailingSplit(direction) ? [updatedSourceGroup, newGroup] : [newGroup, updatedSourceGroup];
3335
+ const reorderedGroups = [...groups.slice(0, sourceIndex), ...replacementGroups, ...groups.slice(sourceIndex + 1)];
3336
+ return createNextState(state, newGroupId, reorderedGroups);
3337
+ };
3338
+ const splitAtRootLevel = (state, groups, groupId, newGroupId, direction, baseNewGroup) => {
3339
+ const updatedGroups = groups.map(group => {
3340
+ if (group.id === groupId) {
3341
+ return {
3342
+ ...group,
3343
+ direction: undefined,
3344
+ focused: false,
3345
+ size: 50
3346
+ };
3347
+ }
3348
+ return group;
3349
+ });
3350
+ const newGroup = {
3351
+ ...baseNewGroup,
3352
+ direction: undefined,
3353
+ size: 50
3354
+ };
3355
+ const reorderedGroups = isTrailingSplit(direction) ? [...updatedGroups, newGroup] : (() => {
3356
+ const sourceIndex = updatedGroups.findIndex(group => group.id === groupId);
3357
+ return [...updatedGroups.slice(0, sourceIndex), newGroup, ...updatedGroups.slice(sourceIndex)];
3358
+ })();
3359
+ return createNextState(state, newGroupId, rebalanceGroupSizes(reorderedGroups));
3360
+ };
3144
3361
  const splitEditorGroup$1 = (state, groupId, direction) => {
3145
3362
  const {
3146
3363
  layout
@@ -3153,42 +3370,30 @@ const splitEditorGroup$1 = (state, groupId, direction) => {
3153
3370
  return state;
3154
3371
  }
3155
3372
  const newGroupId = create$1();
3156
- const isHorizontalSplit = direction === Left || direction === Right;
3157
- const newLayoutDirection = isHorizontalSplit ? Horizontal : Vertical;
3158
- const updatedGroups = groups.map(group => {
3159
- if (group.id === groupId) {
3160
- return {
3161
- ...group,
3162
- focused: false,
3163
- size: 50
3164
- };
3165
- }
3166
- return group;
3167
- });
3168
- const newGroup = {
3373
+ const splitLayoutDirection = getSplitLayoutDirection(direction);
3374
+ const baseNewGroup = {
3169
3375
  activeTabId: undefined,
3170
3376
  focused: true,
3171
3377
  id: newGroupId,
3172
3378
  isEmpty: true,
3173
- size: 50,
3379
+ size: sourceGroup.size / 2,
3174
3380
  tabs: []
3175
3381
  };
3176
- let reorderedGroups;
3177
- if (direction === Right || direction === 'down') {
3178
- reorderedGroups = [...updatedGroups, newGroup];
3179
- } else {
3180
- const sourceIndex = updatedGroups.findIndex(group => group.id === groupId);
3181
- reorderedGroups = [...updatedGroups.slice(0, sourceIndex), newGroup, ...updatedGroups.slice(sourceIndex)];
3382
+ if (groups.length === 1) {
3383
+ return splitOnlyGroup(state, groups, groupId, newGroupId, direction, splitLayoutDirection, baseNewGroup);
3182
3384
  }
3183
- const resizedGroups = rebalanceGroupSizes(reorderedGroups);
3184
- return {
3185
- ...state,
3186
- layout: {
3187
- activeGroupId: newGroupId,
3188
- direction: newLayoutDirection,
3189
- groups: resizedGroups
3190
- }
3191
- };
3385
+ if (sourceGroup.direction === splitLayoutDirection) {
3386
+ return splitWithinMatchingNestedDirection(state, groups, sourceGroup, groupId, newGroupId, direction, splitLayoutDirection, baseNewGroup);
3387
+ }
3388
+ if (splitLayoutDirection !== layout.direction && sourceGroup.direction === undefined) {
3389
+ return splitStandaloneSourceIntoNestedDirection(state, groups, sourceGroup, groupId, newGroupId, direction, splitLayoutDirection, baseNewGroup);
3390
+ }
3391
+ const segments = getGroupSegments(groups, layout.direction);
3392
+ const hasNestedSegments = segments.some(segment => segment.direction !== undefined);
3393
+ if (splitLayoutDirection === layout.direction && !hasNestedSegments) {
3394
+ return splitAtRootLevel(state, groups, groupId, newGroupId, direction, baseNewGroup);
3395
+ }
3396
+ return createNextState(state, newGroupId, groups);
3192
3397
  };
3193
3398
 
3194
3399
  const handleClickAction = async (state, action, rawGroupId) => {
@@ -3211,11 +3416,13 @@ const handleClickAction = async (state, action, rawGroupId) => {
3211
3416
  }
3212
3417
  switch (action) {
3213
3418
  case CloseGroup:
3214
- if (rawGroupId) {
3215
- const groupId = Number.parseInt(rawGroupId, 10);
3419
+ {
3420
+ const groupId = parseRawGroupId(rawGroupId);
3421
+ if (groupId === undefined) {
3422
+ return state;
3423
+ }
3216
3424
  return closeEditorGroup$1(state, groupId);
3217
3425
  }
3218
- return state;
3219
3426
  case RetryOpen:
3220
3427
  return retryOpen(state);
3221
3428
  case SplitRight$1:
@@ -4047,6 +4254,7 @@ const Pin = 'Pin';
4047
4254
  const SplitAndMove = 'Split & Move';
4048
4255
  const MoveIntoNewWindow = 'Move into New Window';
4049
4256
  const CopyIntoNewWindow = 'Copy into New Window';
4257
+ const NewTextFile = 'New Text File';
4050
4258
  const NewWindow = 'New Window';
4051
4259
  const OpenFile = 'Open File';
4052
4260
  const SplitDown = 'Split Down';
@@ -4078,6 +4286,9 @@ const splitEditorGroup = () => {
4078
4286
  const newWindow$1 = () => {
4079
4287
  return i18nString(NewWindow);
4080
4288
  };
4289
+ const newTextFile = () => {
4290
+ return i18nString(NewTextFile);
4291
+ };
4081
4292
  const close = () => {
4082
4293
  return i18nString(Close);
4083
4294
  };
@@ -4164,6 +4375,11 @@ const getNewWindowMenuEntries = state => {
4164
4375
  const getMenuEntries$2 = (state, groupId) => {
4165
4376
  const groupArgs = getMenuEntryArgs(groupId);
4166
4377
  const entries = [{
4378
+ command: 'MainArea.newFile',
4379
+ flags: None,
4380
+ id: 'newTextFile',
4381
+ label: newTextFile()
4382
+ }, {
4167
4383
  args: [QuickPick, 'file'],
4168
4384
  command: 'Viewlet.openWidget',
4169
4385
  flags: None,
@@ -4394,47 +4610,112 @@ const getEditorGroupCss = layout => {
4394
4610
  });
4395
4611
  };
4396
4612
 
4397
- const getSashCss = layout => {
4398
- if (layout.groups.length <= 1) {
4399
- return [];
4613
+ const MIN_GROUP_WIDTH_PX = 250;
4614
+ const getSashOffset = (layout, groupIndex, width) => {
4615
+ const {
4616
+ direction,
4617
+ groups
4618
+ } = layout;
4619
+ const percentOffset = groups.slice(0, groupIndex).reduce((total, group) => total + group.size, 0);
4620
+ if (direction !== Horizontal || !width || !Number.isFinite(width)) {
4621
+ return `${percentOffset}%`;
4400
4622
  }
4401
- const sashPositionVariable = layout.direction === Horizontal ? '--SashLeft' : '--SashTop';
4623
+ const effectiveGroupSizes = groups.map(group => Math.max(group.size / 100 * width, MIN_GROUP_WIDTH_PX));
4624
+ const hasOverflowingGroups = effectiveGroupSizes.some((size, index) => size !== groups[index].size / 100 * width);
4625
+ if (!hasOverflowingGroups) {
4626
+ return `${percentOffset}%`;
4627
+ }
4628
+ const pixelOffset = effectiveGroupSizes.slice(0, groupIndex).reduce((total, size) => total + size, 0);
4629
+ return `${pixelOffset}px`;
4630
+ };
4631
+
4632
+ const escapeCssAttributeValue = value => {
4633
+ return value.replaceAll('\\', '\\\\').replaceAll('"', '\\"');
4634
+ };
4635
+ const toSashProperty = direction => {
4636
+ return direction === Horizontal ? 'left' : 'top';
4637
+ };
4638
+ const renderSashCss = ({
4639
+ property,
4640
+ sashId,
4641
+ value
4642
+ }) => {
4643
+ const escapedSashId = escapeCssAttributeValue(sashId);
4644
+ return `[data-sash-id="${escapedSashId}"] {
4645
+ ${property}: ${value};
4646
+ }`;
4647
+ };
4648
+ const getProtoSashCss = (layout, width) => {
4649
+ const segments = getGroupSegments(layout.groups, layout.direction);
4650
+ const hasNestedSegments = segments.some(segment => segment.direction !== undefined);
4402
4651
  const rules = [];
4403
- let sashOffset = 0;
4404
- for (let i = 1; i < layout.groups.length; i++) {
4405
- sashOffset += layout.groups[i - 1].size;
4406
- const beforeGroupId = layout.groups[i - 1].id;
4407
- const afterGroupId = layout.groups[i].id;
4652
+ let segmentOffset = 0;
4653
+ for (let i = 1; i < segments.length; i++) {
4654
+ segmentOffset += getSegmentSize(segments[i - 1]);
4655
+ const beforeGroupId = segments[i - 1].groups.at(-1)?.id || 0;
4656
+ const afterGroupId = segments[i].groups[0].id;
4408
4657
  const sashId = create(beforeGroupId, afterGroupId);
4409
- rules.push(`.Sash[data-sashId="${sashId}"] {
4410
- ${sashPositionVariable}: ${sashOffset}%;
4411
- }`);
4658
+ const value = hasNestedSegments || layout.direction !== Horizontal || !width ? `${segmentOffset}%` : getSashOffset(layout, i, width);
4659
+ rules.push({
4660
+ property: toSashProperty(layout.direction),
4661
+ sashId,
4662
+ value
4663
+ });
4664
+ }
4665
+ for (const segment of segments) {
4666
+ if (segment.direction === undefined || segment.groups.length <= 1) {
4667
+ continue;
4668
+ }
4669
+ const segmentSize = getSegmentSize(segment);
4670
+ let nestedOffset = 0;
4671
+ for (let i = 1; i < segment.groups.length; i++) {
4672
+ nestedOffset += segment.groups[i - 1].size;
4673
+ const beforeGroupId = segment.groups[i - 1].id;
4674
+ const afterGroupId = segment.groups[i].id;
4675
+ const sashId = create(beforeGroupId, afterGroupId);
4676
+ const value = `${Number((nestedOffset / segmentSize * 100).toFixed(6))}%`;
4677
+ rules.push({
4678
+ property: toSashProperty(segment.direction),
4679
+ sashId,
4680
+ value
4681
+ });
4682
+ }
4412
4683
  }
4413
4684
  return rules;
4414
4685
  };
4686
+ const getSashCss = (layout, width = 0) => {
4687
+ if (layout.groups.length <= 1) {
4688
+ return [];
4689
+ }
4690
+ return getProtoSashCss(layout, width).map(renderSashCss);
4691
+ };
4415
4692
 
4416
- const getCss = layout => {
4693
+ const getCss = (layout, width = 0) => {
4417
4694
  const rules = [`.MainArea {
4418
4695
  }`, `.editor-groups-container {
4419
4696
  overflow: auto;
4697
+ }`, `.SashBorder {
4698
+ background: var(--SashBorder, gray);
4699
+ }`, `.SashBorderHorizontal {
4700
+ width: 100%;
4701
+ height: 1px;
4702
+ }`, `.SashBorderVertical {
4703
+ width: 1px;
4704
+ height: 100%;
4420
4705
  }`, `.EditorGroup {
4421
4706
  min-width: 250px;
4422
4707
  width: var(--EditorGroupWidth, auto);
4423
4708
  /*height: var(--EditorGroupHeight, auto);*/
4424
- }`, `.MainArea .SashVertical {
4425
- left: var(--SashLeft);
4426
- }`, `.MainArea .SashHorizontal {
4427
- top: var(--SashTop);
4428
4709
  }`];
4429
4710
  if (layout) {
4430
- rules.push(...getEditorGroupCss(layout), ...getSashCss(layout));
4711
+ rules.push(...getEditorGroupCss(layout), ...getSashCss(layout, width));
4431
4712
  }
4432
4713
  const css = rules.join('\n');
4433
4714
  return css;
4434
4715
  };
4435
4716
 
4436
4717
  const renderCss = (oldState, newState) => {
4437
- const css = getCss(newState.layout);
4718
+ const css = getCss(newState.layout, newState.width);
4438
4719
  return [SetCss, newState.uid, css];
4439
4720
  };
4440
4721
 
@@ -4748,25 +5029,6 @@ const HandleSashPointerDown = 16;
4748
5029
  const HandleSashPointerMove = 17;
4749
5030
  const HandleSashPointerUp = 18;
4750
5031
 
4751
- const MIN_GROUP_WIDTH_PX = 250;
4752
- const getSashOffset = (layout, groupIndex, width) => {
4753
- const {
4754
- direction,
4755
- groups
4756
- } = layout;
4757
- const percentOffset = groups.slice(0, groupIndex).reduce((total, group) => total + group.size, 0);
4758
- if (direction !== Horizontal || !width || !Number.isFinite(width)) {
4759
- return `${percentOffset}%`;
4760
- }
4761
- const effectiveGroupSizes = groups.map(group => Math.max(group.size / 100 * width, MIN_GROUP_WIDTH_PX));
4762
- const hasOverflowingGroups = effectiveGroupSizes.some((size, index) => size !== groups[index].size / 100 * width);
4763
- if (!hasOverflowingGroups) {
4764
- return `${percentOffset}%`;
4765
- }
4766
- const pixelOffset = effectiveGroupSizes.slice(0, groupIndex).reduce((total, size) => total + size, 0);
4767
- return `${pixelOffset}px`;
4768
- };
4769
-
4770
5032
  const renderContent = content => {
4771
5033
  return [{
4772
5034
  childCount: 1,
@@ -5002,7 +5264,11 @@ const renderEmptyGroupCloseButton = (group, groupIndex) => {
5002
5264
  onClick: HandleClickAction,
5003
5265
  title: closeEditorGroup(),
5004
5266
  type: Button$2
5005
- }, text('✕')];
5267
+ }, {
5268
+ childCount: 0,
5269
+ className: MaskIconClose,
5270
+ type: Div
5271
+ }];
5006
5272
  };
5007
5273
 
5008
5274
  const renderWaterMark = groupId => {
@@ -5056,18 +5322,20 @@ const getSashClassName = direction => {
5056
5322
  return direction === Horizontal ? 'Sash SashVertical' : 'Sash SashHorizontal';
5057
5323
  };
5058
5324
 
5059
- const renderSash = (direction, sashId, style) => {
5325
+ const getSashBorderClassName = direction => {
5326
+ return direction === 1 ? 'SashBorder SashBorderVertical' : 'SashBorder SashBorderHorizontal';
5327
+ };
5328
+ const renderSash = (direction, sashId) => {
5060
5329
  return [{
5061
5330
  childCount: 1,
5062
5331
  className: getSashClassName(direction),
5063
5332
  'data-sashId': sashId,
5064
5333
  onPointerDown: HandleSashPointerDown,
5065
5334
  role: 'none',
5066
- style,
5067
5335
  type: Button$2
5068
5336
  }, {
5069
5337
  childCount: 0,
5070
- className: 'SashBorder',
5338
+ className: getSashBorderClassName(direction),
5071
5339
  type: Div
5072
5340
  }];
5073
5341
  };
@@ -5086,19 +5354,81 @@ const getDirectionClassName = (direction, isSplit) => {
5086
5354
  }
5087
5355
  return direction === Horizontal ? EditorGroupsVertical : EditorGroupsHorizontal;
5088
5356
  };
5089
- const getMainAreaVirtualDom = (layout, splitButtonEnabled = false, width = 0) => {
5357
+ const getContainerClassName = (direction, childCount) => {
5358
+ const directionClassName = getDirectionClassName(direction, childCount > 1);
5359
+ return directionClassName ? `${EDITOR_GROUPS_CONTAINER} ${directionClassName}` : EDITOR_GROUPS_CONTAINER;
5360
+ };
5361
+ const getSizeProperty = direction => {
5362
+ return direction === Vertical ? 'height' : 'width';
5363
+ };
5364
+ const renderSegmentChildren = (direction, groups, splitButtonEnabled) => {
5365
+ const segments = getGroupSegments(groups, direction);
5366
+ const children = [];
5367
+ let childCount = 0;
5368
+ const sizeProperty = getSizeProperty(direction);
5369
+ for (let i = 0; i < segments.length; i++) {
5370
+ const segment = segments[i];
5371
+ if (i > 0) {
5372
+ const previousSegment = segments[i - 1];
5373
+ const beforeGroupId = previousSegment.groups.at(-1)?.id || 0;
5374
+ const afterGroupId = segment.groups[0].id;
5375
+ const sashId = create(beforeGroupId, afterGroupId);
5376
+ children.push(...renderSash(direction, sashId));
5377
+ childCount++;
5378
+ }
5379
+ if (segment.direction === undefined) {
5380
+ children.push(...renderEditorGroup(segment.groups[0], segment.startIndex, splitButtonEnabled, sizeProperty));
5381
+ childCount++;
5382
+ continue;
5383
+ }
5384
+ const nestedDirection = segment.direction;
5385
+ const nestedSizeProperty = getSizeProperty(nestedDirection);
5386
+ const nestedChildCount = segment.groups.length + segment.groups.length - 1;
5387
+ const nestedChildren = [];
5388
+ let nestedCount = 0;
5389
+ const segmentSize = getSegmentSize(segment);
5390
+ for (let j = 0; j < segment.groups.length; j++) {
5391
+ if (j > 0) {
5392
+ const beforeGroupId = segment.groups[j - 1].id;
5393
+ const afterGroupId = segment.groups[j].id;
5394
+ const sashId = create(beforeGroupId, afterGroupId);
5395
+ nestedChildren.push(...renderSash(nestedDirection, sashId));
5396
+ nestedCount++;
5397
+ }
5398
+ const group = segment.groups[j];
5399
+ const normalizedSize = Number((group.size / segmentSize * 100).toFixed(6));
5400
+ nestedChildren.push(...renderEditorGroup({
5401
+ ...group,
5402
+ size: normalizedSize
5403
+ }, segment.startIndex + j, splitButtonEnabled, nestedSizeProperty));
5404
+ nestedCount++;
5405
+ }
5406
+ children.push({
5407
+ childCount: nestedChildCount,
5408
+ className: getContainerClassName(nestedDirection, segment.groups.length),
5409
+ role: None$1,
5410
+ style: `${sizeProperty}:${segmentSize}%;`,
5411
+ type: Div
5412
+ });
5413
+ children.push(...nestedChildren);
5414
+ childCount++;
5415
+ childCount += nestedCount;
5416
+ }
5417
+ return {
5418
+ childCount,
5419
+ children
5420
+ };
5421
+ };
5422
+ const getMainAreaVirtualDom = (layout, splitButtonEnabled = false) => {
5090
5423
  const {
5091
5424
  direction,
5092
5425
  groups
5093
5426
  } = layout;
5094
- const sizeProperty = direction === Vertical ? 'height' : 'width';
5427
+ const sizeProperty = getSizeProperty(direction);
5095
5428
  if (groups.length === 1) {
5096
5429
  return renderSingleEditorGroup(layout, splitButtonEnabled, sizeProperty);
5097
5430
  }
5098
- const children = [];
5099
- const isSplit = groups.length > 1;
5100
- const directionClassName = getDirectionClassName(direction, isSplit);
5101
- const editorGroupsContainerClassName = directionClassName ? `${EDITOR_GROUPS_CONTAINER} ${directionClassName}` : EDITOR_GROUPS_CONTAINER;
5431
+ const editorGroupsContainerClassName = getContainerClassName(direction, groups.length);
5102
5432
  if (groups.length === 0) {
5103
5433
  return [{
5104
5434
  childCount: 1,
@@ -5113,22 +5443,10 @@ const getMainAreaVirtualDom = (layout, splitButtonEnabled = false, width = 0) =>
5113
5443
  type: Div
5114
5444
  }];
5115
5445
  }
5116
- let childCount = 0;
5117
- for (let i = 0; i < groups.length; i++) {
5118
- if (i > 0) {
5119
- // Insert sash between groups
5120
- const beforeGroupId = groups[i - 1].id;
5121
- const afterGroupId = groups[i].id;
5122
- const sashId = create(beforeGroupId, afterGroupId);
5123
- const offset = getSashOffset(layout, i, width);
5124
- const style = direction === Horizontal ? `left:${offset};` : `top:${offset};`;
5125
- children.push(...renderSash(direction, sashId, style));
5126
- childCount++;
5127
- }
5128
- const editorGroupDom = renderEditorGroup(groups[i], i, splitButtonEnabled, sizeProperty);
5129
- children.push(...editorGroupDom);
5130
- childCount++;
5131
- }
5446
+ const {
5447
+ childCount,
5448
+ children
5449
+ } = renderSegmentChildren(direction, groups, splitButtonEnabled);
5132
5450
  return [{
5133
5451
  childCount: 1,
5134
5452
  className: Main,
@@ -5146,13 +5464,12 @@ const renderItems = (oldState, newState) => {
5146
5464
  initial,
5147
5465
  layout,
5148
5466
  splitButtonEnabled,
5149
- uid,
5150
- width
5467
+ uid
5151
5468
  } = newState;
5152
5469
  if (initial) {
5153
5470
  return [SetDom2, uid, []];
5154
5471
  }
5155
- const dom = getMainAreaVirtualDom(layout, splitButtonEnabled, width);
5472
+ const dom = getMainAreaVirtualDom(layout, splitButtonEnabled);
5156
5473
  return [SetDom2, uid, dom];
5157
5474
  };
5158
5475
 
@@ -5268,10 +5585,16 @@ const save = async state => {
5268
5585
  };
5269
5586
 
5270
5587
  const getFilteredGroups = groups => {
5271
- return groups.map(group => ({
5272
- ...group,
5273
- tabs: group.tabs.filter(tab => !tab.uri?.startsWith('untitled://')).map(normalizeTabEditorInput)
5274
- })).filter(group => group.tabs.length > 0);
5588
+ return groups.map(group => {
5589
+ const tabs = group.tabs.filter(tab => !tab.uri?.startsWith('untitled://')).map(normalizeTabEditorInput);
5590
+ const activeTabId = tabs.some(tab => tab.id === group.activeTabId) ? group.activeTabId : tabs[0]?.id;
5591
+ return {
5592
+ ...group,
5593
+ activeTabId,
5594
+ isEmpty: tabs.length === 0,
5595
+ tabs
5596
+ };
5597
+ });
5275
5598
  };
5276
5599
 
5277
5600
  const saveState = state => {
@@ -5431,6 +5754,26 @@ const splitUp = (state, groupId) => {
5431
5754
  };
5432
5755
 
5433
5756
  const commandMap = {
5757
+ 'Main.closeActiveEditor': wrapCommand(closeActiveEditor),
5758
+ 'Main.closeAll': wrapCommand(closeAll$1),
5759
+ 'Main.closeAllEditors': wrapCommand(closeAll$1),
5760
+ 'Main.closeFocusedTab': wrapCommand(closeFocusedTab),
5761
+ 'Main.closeOthers': wrapCommand(closeOtherTabs),
5762
+ 'Main.closeSaved': wrapCommand(closeSaved$1),
5763
+ 'Main.closeTabsByUris': wrapCommand(closeTabsByUris),
5764
+ 'Main.closeTabsRight': wrapCommand(closeTabsRight),
5765
+ 'Main.copyPath': wrapCommand(copyPath$1),
5766
+ 'Main.copyRelativePath': wrapCommand(copyRelativePath$1),
5767
+ 'Main.focusNext': wrapCommand(focusNextTab),
5768
+ 'Main.focusNextTab': wrapCommand(focusNextTab),
5769
+ 'Main.focusPrevious': wrapCommand(focusPreviousTab),
5770
+ 'Main.focusPreviousTab': wrapCommand(focusPreviousTab),
5771
+ 'Main.handleTabContextMenu': wrapCommand(handleTabContextMenu),
5772
+ 'Main.openInput': wrapCommand(openInput),
5773
+ 'Main.openUri': wrapCommand(openUri),
5774
+ 'Main.openUris': wrapCommand(openUris),
5775
+ 'Main.save': wrapCommand(save),
5776
+ 'Main.splitRight': wrapCommand(splitRight),
5434
5777
  'MainArea.closeActiveEditor': wrapCommand(closeActiveEditor),
5435
5778
  'MainArea.closeAll': wrapCommand(closeAll$1),
5436
5779
  'MainArea.closeAllEditors': wrapCommand(closeAll$1),
@@ -5438,6 +5781,7 @@ const commandMap = {
5438
5781
  'MainArea.closeFocusedTab': wrapCommand(closeFocusedTab),
5439
5782
  'MainArea.closeOthers': wrapCommand(closeOtherTabs),
5440
5783
  'MainArea.closeSaved': wrapCommand(closeSaved$1),
5784
+ 'MainArea.closeTabsByUris': wrapCommand(closeTabsByUris),
5441
5785
  'MainArea.closeTabsRight': wrapCommand(closeTabsRight),
5442
5786
  'MainArea.copyPath': wrapCommand(copyPath$1),
5443
5787
  'MainArea.copyRelativePath': wrapCommand(copyRelativePath$1),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/main-area-worker",
3
- "version": "9.8.0",
3
+ "version": "9.11.0",
4
4
  "description": "Main Area Worker",
5
5
  "repository": {
6
6
  "type": "git",