@lvce-editor/explorer-view 2.54.0 → 2.56.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.
@@ -889,7 +889,7 @@ const Copy$2 = 3;
889
889
  const Rename$2 = 4;
890
890
 
891
891
  const rpcs = Object.create(null);
892
- const set$f = (id, rpc) => {
892
+ const set$g = (id, rpc) => {
893
893
  rpcs[id] = rpc;
894
894
  };
895
895
  const get$1 = id => {
@@ -913,7 +913,7 @@ const create$2 = rpcId => {
913
913
  return rpc.invokeAndTransfer(method, ...params);
914
914
  },
915
915
  set(rpc) {
916
- set$f(rpcId, rpc);
916
+ set$g(rpcId, rpc);
917
917
  },
918
918
  async dispose() {
919
919
  const rpc = get$1(rpcId);
@@ -995,6 +995,23 @@ const applyFileOperations = async operations => {
995
995
  }
996
996
  };
997
997
 
998
+ const DELTA_EDITING = 100;
999
+
1000
+ const BlockDevice = 1;
1001
+ const CharacterDevice = 2;
1002
+ const Directory = 3;
1003
+ const DirectoryExpanded = 4;
1004
+ const DirectoryExpanding = 5;
1005
+ const File = 7;
1006
+ const Socket = 8;
1007
+ const Symlink = 9;
1008
+ const SymLinkFile = 10;
1009
+ const SymLinkFolder = 11;
1010
+ const Unknown = 12;
1011
+ const EditingFile = File + DELTA_EDITING;
1012
+ const EditingFolder = Directory + DELTA_EDITING;
1013
+ const EditingDirectoryExpanded = DirectoryExpanded + DELTA_EDITING;
1014
+
998
1015
  const dirname = (pathSeparator, path) => {
999
1016
  const index = path.lastIndexOf(pathSeparator);
1000
1017
  if (index === -1) {
@@ -1015,16 +1032,32 @@ const join2 = (path, childPath) => {
1015
1032
  return `${path}/${childPath}`;
1016
1033
  };
1017
1034
 
1035
+ const getActualType = (type, expanded) => {
1036
+ const actualType = type === Directory && expanded ? DirectoryExpanded : type;
1037
+ return actualType;
1038
+ };
1018
1039
  const createTree = (items, root) => {
1019
1040
  const tree = Object.create(null);
1020
1041
  const rootLength = root.length;
1021
- for (const item of items) {
1042
+ const paths = items.map(item => {
1022
1043
  const relativePath = item.path.slice(rootLength);
1023
1044
  const dirname = dirname2(relativePath);
1045
+ return dirname;
1046
+ });
1047
+ for (const item of items) {
1048
+ const {
1049
+ type,
1050
+ name,
1051
+ path
1052
+ } = item;
1053
+ const relativePath = path.slice(rootLength);
1054
+ const dirname = dirname2(relativePath);
1055
+ const isExpanded = paths.includes(relativePath);
1056
+ const actualType = getActualType(type, isExpanded);
1024
1057
  tree[dirname] ||= [];
1025
1058
  tree[dirname].push({
1026
- name: item.name,
1027
- type: item.type
1059
+ name,
1060
+ type: actualType
1028
1061
  });
1029
1062
  }
1030
1063
  return tree;
@@ -1076,23 +1109,6 @@ const getPaths = items => {
1076
1109
  return items.map(getPath);
1077
1110
  };
1078
1111
 
1079
- const DELTA_EDITING = 100;
1080
-
1081
- const BlockDevice = 1;
1082
- const CharacterDevice = 2;
1083
- const Directory = 3;
1084
- const DirectoryExpanded = 4;
1085
- const DirectoryExpanding = 5;
1086
- const File = 7;
1087
- const Socket = 8;
1088
- const Symlink = 9;
1089
- const SymLinkFile = 10;
1090
- const SymLinkFolder = 11;
1091
- const Unknown = 12;
1092
- const EditingFile = File + DELTA_EDITING;
1093
- const EditingFolder = Directory + DELTA_EDITING;
1094
- const EditingDirectoryExpanded = DirectoryExpanded + DELTA_EDITING;
1095
-
1096
1112
  const getSimpleIconRequestType = direntType => {
1097
1113
  if (direntType === Directory || direntType === DirectoryExpanded || direntType === EditingDirectoryExpanded || direntType === EditingFolder) {
1098
1114
  return 2;
@@ -1197,13 +1213,14 @@ const getPathParts = (root, uri, pathSeparator) => {
1197
1213
  const parts = [];
1198
1214
  let index = root.length - 1;
1199
1215
  let depth = 0;
1200
- while ((index = uri.indexOf('/', index + 1)) !== -1) {
1216
+ while ((index = uri.indexOf(pathSeparator, index + 1)) !== -1) {
1201
1217
  const partUri = uri.slice(0, index);
1202
1218
  parts.push({
1203
1219
  path: partUri,
1204
1220
  depth: depth++,
1205
1221
  root,
1206
- pathSeparator
1222
+ pathSeparator,
1223
+ expanded: true
1207
1224
  });
1208
1225
  }
1209
1226
  return parts;
@@ -1310,14 +1327,14 @@ const sortExplorerItems = rawDirents => {
1310
1327
  };
1311
1328
 
1312
1329
  // TODO figure out whether this uses too much memory (name,path -> redundant, depth could be computed on demand)
1313
- const toDisplayDirent = (parentPath, parentDepth, rawDirent, index, length) => {
1314
- const path = join2(parentPath, rawDirent.name);
1330
+ const toDisplayDirent = (parentPath, parentDepth, rawDirentType, rawDirentName, index, length) => {
1331
+ const path = join2(parentPath, rawDirentName);
1315
1332
  return {
1316
- name: rawDirent.name,
1333
+ name: rawDirentName,
1317
1334
  posInSet: index + 1,
1318
1335
  setSize: length,
1319
1336
  depth: parentDepth + 1,
1320
- type: rawDirent.type,
1337
+ type: rawDirentType,
1321
1338
  path,
1322
1339
  // TODO storing absolute path might be too costly, could also store relative path here
1323
1340
  icon: '',
@@ -1325,7 +1342,7 @@ const toDisplayDirent = (parentPath, parentDepth, rawDirent, index, length) => {
1325
1342
  };
1326
1343
  };
1327
1344
 
1328
- const toDisplayDirents = (pathSeparator, rawDirents, parentDirentPath, parentDirentDepth, excluded) => {
1345
+ const toDisplayDirents = (pathSeparator, rawDirents, parentDirentPath, parentDirentDepth, excluded, expanded = false) => {
1329
1346
  rawDirents = sortExplorerItems(rawDirents);
1330
1347
  const result = [];
1331
1348
  const visibleItems = rawDirents.filter(item => {
@@ -1337,7 +1354,7 @@ const toDisplayDirents = (pathSeparator, rawDirents, parentDirentPath, parentDir
1337
1354
  const count = visibleItems.length;
1338
1355
  for (let i = 0; i < visibleItems.length; i++) {
1339
1356
  const rawDirent = visibleItems[i];
1340
- result.push(toDisplayDirent(parentDirentPath, parentDirentDepth, rawDirent, i, count));
1357
+ result.push(toDisplayDirent(parentDirentPath, parentDirentDepth, rawDirent.type, rawDirent.name, i, count));
1341
1358
  }
1342
1359
  return result;
1343
1360
  };
@@ -1448,6 +1465,7 @@ const CopyPath = 'Copy Path';
1448
1465
  const CopyRelativePath = 'Copy Relative Path';
1449
1466
  const Cut$1 = 'Cut';
1450
1467
  const Delete$1 = 'Delete';
1468
+ const FileNameCannotStartWithSlash = 'A file or folder name cannot start with a slash.';
1451
1469
  const FileOrFolderNameMustBeProvider = 'A file or folder name must be provided.';
1452
1470
  const FilesExplorer = 'Files Explorer';
1453
1471
  const NewFile$1 = 'New File...';
@@ -1512,6 +1530,9 @@ const openFolder$1 = () => {
1512
1530
  const fileOrFolderNameMustBeProvided = () => {
1513
1531
  return i18nString(FileOrFolderNameMustBeProvider);
1514
1532
  };
1533
+ const fileCannotStartWithSlash = () => {
1534
+ return i18nString(FileNameCannotStartWithSlash);
1535
+ };
1515
1536
  const typeAFileName = () => {
1516
1537
  return i18nString(TypeAFileName);
1517
1538
  };
@@ -1521,6 +1542,9 @@ const validateFileName2 = name => {
1521
1542
  const editingErrorMessage = fileOrFolderNameMustBeProvided();
1522
1543
  return editingErrorMessage;
1523
1544
  }
1545
+ if (name.startsWith('/')) {
1546
+ return fileCannotStartWithSlash();
1547
+ }
1524
1548
  return '';
1525
1549
  };
1526
1550
 
@@ -3680,6 +3704,49 @@ const handleKeyDown = (state, key) => {
3680
3704
  };
3681
3705
  };
3682
3706
 
3707
+ const scrollInto = (index, minLineY, maxLineY) => {
3708
+ const diff = maxLineY - minLineY;
3709
+ const smallerHalf = Math.floor(diff / 2);
3710
+ const largerHalf = diff - smallerHalf;
3711
+ if (index < minLineY) {
3712
+ return {
3713
+ newMinLineY: index - smallerHalf,
3714
+ newMaxLineY: index + largerHalf
3715
+ };
3716
+ }
3717
+ if (index >= maxLineY) {
3718
+ return {
3719
+ newMinLineY: index - smallerHalf,
3720
+ newMaxLineY: index + largerHalf
3721
+ };
3722
+ }
3723
+ return {
3724
+ newMinLineY: minLineY,
3725
+ newMaxLineY: maxLineY
3726
+ };
3727
+ };
3728
+
3729
+ const adjustScrollAfterPaste = (state, focusedIndex) => {
3730
+ const {
3731
+ minLineY,
3732
+ maxLineY,
3733
+ itemHeight
3734
+ } = state;
3735
+ const {
3736
+ newMinLineY,
3737
+ newMaxLineY
3738
+ } = scrollInto(focusedIndex, minLineY, maxLineY);
3739
+ const newDeltaY = newMinLineY * itemHeight;
3740
+ return {
3741
+ ...state,
3742
+ focusedIndex,
3743
+ focused: true,
3744
+ minLineY: newMinLineY,
3745
+ maxLineY: newMaxLineY,
3746
+ deltaY: newDeltaY
3747
+ };
3748
+ };
3749
+
3683
3750
  const generateUniqueName = (baseName, existingPaths, root) => {
3684
3751
  // Handle files with extensions
3685
3752
  const lastDotIndex = baseName.lastIndexOf('.');
@@ -3756,17 +3823,13 @@ const handlePasteCopy = async (state, nativeFiles) => {
3756
3823
  // TODO only update folder at which level it changed
3757
3824
  const latestState = await refresh(state);
3758
3825
 
3759
- // Focus on the first newly created file
3826
+ // Focus on the first newly created file and adjust scroll position
3760
3827
  const newFilePaths = operations.map(operation => operation.path);
3761
3828
  if (newFilePaths.length > 0) {
3762
3829
  const firstNewFilePath = newFilePaths[0];
3763
3830
  const newFileIndex = getIndex(latestState.items, firstNewFilePath);
3764
3831
  if (newFileIndex !== -1) {
3765
- return {
3766
- ...latestState,
3767
- focusedIndex: newFileIndex,
3768
- focused: true
3769
- };
3832
+ return adjustScrollAfterPaste(latestState, newFileIndex);
3770
3833
  }
3771
3834
  }
3772
3835
  // If there are no items, ensure focusedIndex is 0
@@ -3784,7 +3847,20 @@ const handlePasteCut = async (state, nativeFiles) => {
3784
3847
  const target = `${state.root}${state.pathSeparator}${getBaseName(state.pathSeparator, source)}`;
3785
3848
  await rename$1(source, target);
3786
3849
  }
3787
- return state;
3850
+
3851
+ // Refresh the state after cut operations
3852
+ const latestState = await refresh(state);
3853
+
3854
+ // Focus on the first pasted file and adjust scroll position
3855
+ if (nativeFiles.files.length > 0) {
3856
+ const firstPastedFile = nativeFiles.files[0];
3857
+ const targetPath = `${state.root}${state.pathSeparator}${getBaseName(state.pathSeparator, firstPastedFile)}`;
3858
+ const pastedFileIndex = getIndex(latestState.items, targetPath);
3859
+ if (pastedFileIndex !== -1) {
3860
+ return adjustScrollAfterPaste(latestState, pastedFileIndex);
3861
+ }
3862
+ }
3863
+ return latestState;
3788
3864
  };
3789
3865
 
3790
3866
  const handlePasteNone = async (state, nativeFiles) => {
@@ -3797,18 +3873,6 @@ const Copy = 'copy';
3797
3873
  const Cut = 'cut';
3798
3874
 
3799
3875
  const getPasteHandler = type => {
3800
- // TODO detect cut/paste event, not sure if that is possible
3801
- // TODO check that pasted folder is not a parent folder of opened folder
3802
- // TODO support pasting multiple paths
3803
- // TODO what happens when pasting multiple paths, but some of them error?
3804
- // how many error messages should be shown? Should the operation be undone?
3805
- // TODO what if it is a large folder and takes a long time to copy? Should show progress
3806
- // TODO what if there is a permission error? Probably should show a modal to ask for permission
3807
- // TODO if error is EEXISTS, just rename the copy (e.g. file-copy-1.txt, file-copy-2.txt)
3808
- // TODO actual target should be selected folder
3809
- // TODO but what if a file is currently selected? Then maybe the parent folder
3810
- // TODO but will it work if the folder is a symlink?
3811
- // TODO handle error gracefully when copy fails
3812
3876
  switch (type) {
3813
3877
  case None$3:
3814
3878
  return handlePasteNone;
@@ -3823,6 +3887,9 @@ const getPasteHandler = type => {
3823
3887
 
3824
3888
  const handlePaste = async state => {
3825
3889
  const nativeFiles = await readNativeFiles();
3890
+ if (!nativeFiles) {
3891
+ return state;
3892
+ }
3826
3893
  // TODO detect cut/paste event, not sure if that is possible
3827
3894
  // TODO check that pasted folder is not a parent folder of opened folder
3828
3895
  // TODO support pasting multiple paths
@@ -4312,7 +4379,11 @@ const removeDirent = async state => {
4312
4379
  // TODO use file operations, bulk edit and explorer refresh
4313
4380
  await removePaths(toRemove);
4314
4381
  const newState = await refresh(state);
4315
- return newState;
4382
+ return {
4383
+ ...newState,
4384
+ focused: true,
4385
+ focus: List
4386
+ };
4316
4387
  };
4317
4388
 
4318
4389
  const getEditingType = direntType => {
@@ -5132,28 +5203,6 @@ const mergeVisibleWithHiddenItems = (visibleItems, hiddenItems) => {
5132
5203
  return unique;
5133
5204
  };
5134
5205
 
5135
- const scrollInto = (index, minLineY, maxLineY) => {
5136
- const diff = maxLineY - minLineY;
5137
- const smallerHalf = Math.floor(diff / 2);
5138
- const largerHalf = diff - smallerHalf;
5139
- if (index < minLineY) {
5140
- return {
5141
- newMinLineY: index - smallerHalf,
5142
- newMaxLineY: index + largerHalf
5143
- };
5144
- }
5145
- if (index >= maxLineY) {
5146
- return {
5147
- newMinLineY: index - smallerHalf,
5148
- newMaxLineY: index + largerHalf
5149
- };
5150
- }
5151
- return {
5152
- newMinLineY: minLineY,
5153
- newMaxLineY: maxLineY
5154
- };
5155
- };
5156
-
5157
5206
  // TODO maybe just insert items into explorer and refresh whole explorer
5158
5207
  const revealItemHidden = async (state, uri) => {
5159
5208
  const {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/explorer-view",
3
- "version": "2.54.0",
3
+ "version": "2.56.0",
4
4
  "description": "Explorer Worker",
5
5
  "repository": {
6
6
  "type": "git",