@danixl30/file-explorer-cli 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/LICENSE +21 -0
- package/dist/alerts/AlertContextProvider.js +13 -0
- package/dist/alerts/components/AlertDisplay.js +19 -0
- package/dist/alerts/logic/alert.logic.js +16 -0
- package/dist/app.js +27 -0
- package/dist/bookmark/Bookmark.js +123 -0
- package/dist/bookmark/components/BookmarkList.js +47 -0
- package/dist/bookmark/context/BookmarkPaneContext.js +9 -0
- package/dist/bookmark/events/add.item.js +8 -0
- package/dist/bookmark/implementations/clean.bookmarks.js +6 -0
- package/dist/bookmark/implementations/get.all.bookmarks.js +2 -0
- package/dist/bookmark/implementations/save.bookmarks.js +5 -0
- package/dist/bookmark/logic/bookmark.manager.js +25 -0
- package/dist/bookmark/services/clean.bookmarks.js +1 -0
- package/dist/bookmark/services/get.all.bookmarks.js +1 -0
- package/dist/bookmark/services/write.bookmarks.js +1 -0
- package/dist/bookmark/types/bookmark.js +1 -0
- package/dist/cli.js +31 -0
- package/dist/clipboard/ClipboardProvider.js +8 -0
- package/dist/clipboard/logic/clipboard-logic.js +10 -0
- package/dist/core/application/event-handler/event-handler.js +1 -0
- package/dist/core/application/event-handler/listener/event-listener.js +1 -0
- package/dist/core/application/event-handler/types/event.js +1 -0
- package/dist/core/application/event-handler/types/subscription.js +1 -0
- package/dist/core/application/id-generator/id.generator.js +1 -0
- package/dist/core/application/init-layout/init-layout.js +1 -0
- package/dist/core/application/input-manager/input-manager.js +1 -0
- package/dist/core/application/input-manager/types/input-manager-result.js +1 -0
- package/dist/core/application/on-init/on-init.js +1 -0
- package/dist/core/application/on-init-job/lazy/on-init-job-lazy.js +1 -0
- package/dist/core/application/on-init-job/on-init-job.js +1 -0
- package/dist/core/application/pagination-manager/pagination-manager.js +1 -0
- package/dist/core/application/pagination-manager/types/pagination-result.js +1 -0
- package/dist/core/application/service/application-service.js +1 -0
- package/dist/core/application/state/state-factory.js +1 -0
- package/dist/core/application/state/state-provider.js +1 -0
- package/dist/core/application/state-observers/state-observer.js +1 -0
- package/dist/core/application/timer/sleep/sleep.js +1 -0
- package/dist/core/application/timer/timer.js +19 -0
- package/dist/core/application/value-provider/value-provider.js +1 -0
- package/dist/core/infraestructure/db/bookmark.db.js +4 -0
- package/dist/core/infraestructure/event-handler/context/EventProvider.js +9 -0
- package/dist/core/infraestructure/event-handler/listener/event-listener-factory.js +8 -0
- package/dist/core/infraestructure/event-handler/useEventHadler.js +27 -0
- package/dist/core/infraestructure/initLayout/useLayoutEffectOnInit.js +4 -0
- package/dist/core/infraestructure/input-manager/useInputManager.js +14 -0
- package/dist/core/infraestructure/on-init/useEffectOnInit.js +9 -0
- package/dist/core/infraestructure/on-init-job/nativeOnInitJob.js +37 -0
- package/dist/core/infraestructure/on-init-job/nativeOnInitJobLazy.js +32 -0
- package/dist/core/infraestructure/pagination-manager/data-transforms/infinite.js +1 -0
- package/dist/core/infraestructure/pagination-manager/data-transforms/normal.js +1 -0
- package/dist/core/infraestructure/pagination-manager/usePaginationManager.js +59 -0
- package/dist/core/infraestructure/state/useRefStateProvider.js +39 -0
- package/dist/core/infraestructure/state/useStateProvider.js +35 -0
- package/dist/core/infraestructure/state-observer/useEffectStateObserver.js +16 -0
- package/dist/core/infraestructure/timer/sleep.js +2 -0
- package/dist/core/infraestructure/uuid/uuid.generator.js +2 -0
- package/dist/core/infraestructure/value-provider/useRefValueProvider.js +12 -0
- package/dist/core/utils/argument.type.js +1 -0
- package/dist/core/utils/optional.js +1 -0
- package/dist/file-display/FileDisplay.js +456 -0
- package/dist/file-display/components/Loading.js +4 -0
- package/dist/file-display/components/NodeDetails.js +50 -0
- package/dist/file-display/components/NodeList.js +61 -0
- package/dist/file-display/events/node.written.js +7 -0
- package/dist/file-display/hooks/useResize.js +24 -0
- package/dist/file-display/implementation/create.dir.js +7 -0
- package/dist/file-display/implementation/delete.js +9 -0
- package/dist/file-display/implementation/execute.command.async.js +6 -0
- package/dist/file-display/implementation/execute.command.sync.js +9 -0
- package/dist/file-display/implementation/execute.file.js +4 -0
- package/dist/file-display/implementation/filter.glob.js +4 -0
- package/dist/file-display/implementation/get.all.files.js +44 -0
- package/dist/file-display/implementation/get.directory.js +8 -0
- package/dist/file-display/implementation/get.upper.directory.js +17 -0
- package/dist/file-display/implementation/move.trash.js +9 -0
- package/dist/file-display/implementation/node.details.js +79 -0
- package/dist/file-display/implementation/paste.operation.js +40 -0
- package/dist/file-display/implementation/rename.js +6 -0
- package/dist/file-display/logic/file.manager.js +107 -0
- package/dist/file-display/logic/history.manager.js +47 -0
- package/dist/file-display/logic/node.detail.js +8 -0
- package/dist/file-display/logic/operations.manager.js +103 -0
- package/dist/file-display/services/create.dir.js +1 -0
- package/dist/file-display/services/delete.js +1 -0
- package/dist/file-display/services/execute.command.async.js +1 -0
- package/dist/file-display/services/execute.command.sync.js +1 -0
- package/dist/file-display/services/execute.file.js +1 -0
- package/dist/file-display/services/filter.glob.js +1 -0
- package/dist/file-display/services/get.all.files.js +1 -0
- package/dist/file-display/services/get.directory.js +1 -0
- package/dist/file-display/services/get.upper.directory.js +1 -0
- package/dist/file-display/services/move.trash.js +1 -0
- package/dist/file-display/services/node.details.js +1 -0
- package/dist/file-display/services/paste.operation.js +1 -0
- package/dist/file-display/services/remane.js +1 -0
- package/dist/file-display/types/node.details.js +1 -0
- package/dist/file-display/types/node.js +1 -0
- package/dist/file-display/utils/icon.parser.js +144 -0
- package/dist/input-capture/InputCapureWrapper.js +10 -0
- package/dist/input-capture/InputContextCapture.js +26 -0
- package/dist/tabs/TabsContext.js +17 -0
- package/dist/tabs/events/update.tab.data.js +8 -0
- package/dist/tabs/events/update.tab.selection.js +8 -0
- package/dist/tabs/logic/tabsManagerLogic.js +58 -0
- package/dist/tabs/wrapper/tabs.wrapper.js +38 -0
- package/dist/text-field/TextFieldCaptureContext.js +24 -0
- package/dist/text-field/TextFieldWrapper.js +21 -0
- package/package.json +62 -0
- package/readme.md +57 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
import { Alert, Spinner } from '@inkjs/ui';
|
2
|
+
import { Box, Text } from 'ink';
|
3
|
+
import React from 'react';
|
4
|
+
import { nativeOnInitJob } from '../../core/infraestructure/on-init-job/nativeOnInitJob.js';
|
5
|
+
import { useEffectOnInit } from '../../core/infraestructure/on-init/useEffectOnInit.js';
|
6
|
+
import { useEffectStateObserver } from '../../core/infraestructure/state-observer/useEffectStateObserver.js';
|
7
|
+
import { useRefStateFactory } from '../../core/infraestructure/state/useRefStateProvider.js';
|
8
|
+
import { getNodeDetails } from '../implementation/node.details.js';
|
9
|
+
import { nodeDetailLogic } from '../logic/node.detail.js';
|
10
|
+
import { getNodeIcon } from '../utils/icon.parser.js';
|
11
|
+
function formatBytes(bytes, decimals = 2) {
|
12
|
+
if (!+bytes)
|
13
|
+
return '0 Bytes';
|
14
|
+
const k = 1024;
|
15
|
+
const dm = decimals < 0 ? 0 : decimals;
|
16
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
17
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
18
|
+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
19
|
+
}
|
20
|
+
export const NodeDetails = (props) => {
|
21
|
+
const details = nodeDetailLogic(props.node, nativeOnInitJob(useRefStateFactory(), useEffectStateObserver, useEffectOnInit), getNodeDetails());
|
22
|
+
if (details.error.value)
|
23
|
+
return React.createElement(Alert, { variant: "error" }, details.error.value.message);
|
24
|
+
if (details.isLoading.value || !details.data.value)
|
25
|
+
return (React.createElement(Box, { borderStyle: "single", width: "100%", alignItems: "stretch", flexDirection: "row" },
|
26
|
+
React.createElement(Spinner, { label: "Loading..." })));
|
27
|
+
return (React.createElement(Box, { borderStyle: "single", flexDirection: "column", width: "50%" },
|
28
|
+
React.createElement(Text, null, 'Path: ' + details.data.value.path),
|
29
|
+
React.createElement(Text, null, 'Name: ' +
|
30
|
+
getNodeIcon(details.data.value) +
|
31
|
+
details.data.value.name),
|
32
|
+
React.createElement(Text, null, 'Size: ' + formatBytes(details.data.value.size)),
|
33
|
+
details.data.value.createdDate && (React.createElement(Text, null, 'Created at: ' +
|
34
|
+
details.data.value.createdDate.toLocaleString())),
|
35
|
+
details.data.value.lastModifiedDate && (React.createElement(Text, null, 'Last modified: ' +
|
36
|
+
details.data.value.lastModifiedDate.toLocaleString())),
|
37
|
+
details.data.value.additionalData && (React.createElement(React.Fragment, null,
|
38
|
+
React.createElement(Text, null, 'Available: ' +
|
39
|
+
formatBytes(details.data.value.additionalData.available)),
|
40
|
+
React.createElement(Text, null, 'Used: ' +
|
41
|
+
formatBytes(details.data.value.additionalData.used)),
|
42
|
+
React.createElement(Text, null, 'Percent occupied: ' +
|
43
|
+
details.data.value.additionalData.percent),
|
44
|
+
React.createElement(Text, null, 'Partition schema: ' +
|
45
|
+
details.data.value.additionalData.partitionSchema),
|
46
|
+
React.createElement(Text, null, 'File system: ' +
|
47
|
+
details.data.value.additionalData.fileSystem),
|
48
|
+
React.createElement(Text, null, 'Device type: ' +
|
49
|
+
details.data.value.additionalData.deviceType)))));
|
50
|
+
};
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import { Badge } from '@inkjs/ui';
|
2
|
+
import { Box, Text } from 'ink';
|
3
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
4
|
+
import { useResize } from '../hooks/useResize.js';
|
5
|
+
import { getNodeIcon } from '../utils/icon.parser.js';
|
6
|
+
const createRange = (start, end) => {
|
7
|
+
const arr = [];
|
8
|
+
for (let index = start; index <= end; index++)
|
9
|
+
arr.push(index);
|
10
|
+
return arr;
|
11
|
+
};
|
12
|
+
const calculatePercent = (nodes, index) => Math.round((index * 100) / nodes.length);
|
13
|
+
export const NodeList = (props) => {
|
14
|
+
const { selections, nodes, index } = props;
|
15
|
+
const [lines, setLines] = useState(0);
|
16
|
+
const [[top, down], setSubIndexes] = useState([
|
17
|
+
-1,
|
18
|
+
lines - 2,
|
19
|
+
]);
|
20
|
+
const [_columns, rows] = useResize();
|
21
|
+
useEffect(() => {
|
22
|
+
if (index === -1) {
|
23
|
+
setSubIndexes([-1, lines - 2]);
|
24
|
+
return;
|
25
|
+
}
|
26
|
+
if (index < top) {
|
27
|
+
setSubIndexes([index, index + lines - 1]);
|
28
|
+
return;
|
29
|
+
}
|
30
|
+
if (index > down) {
|
31
|
+
setSubIndexes([index - lines + 1, index]);
|
32
|
+
return;
|
33
|
+
}
|
34
|
+
}, [lines, props.index, nodes, top, down]);
|
35
|
+
useEffect(() => {
|
36
|
+
const lines = rows - 6 - (props.isTabs ? 3 : 0);
|
37
|
+
setLines(lines);
|
38
|
+
setSubIndexes([-1, lines - 2]);
|
39
|
+
}, [rows, props.isTabs]);
|
40
|
+
const range = useMemo(() => createRange(top, down), [top, down]);
|
41
|
+
return (React.createElement(React.Fragment, null,
|
42
|
+
React.createElement(Box, { flexDirection: "column", height: "100%" },
|
43
|
+
top >= -1 && down <= lines - 2 && (React.createElement(Text, { backgroundColor: props.index === -1 ? 'blue' : undefined }, "..")),
|
44
|
+
props.nodes?.length > 0 &&
|
45
|
+
range.map((e) => {
|
46
|
+
if (e === -1)
|
47
|
+
return React.createElement(React.Fragment, null);
|
48
|
+
const file = props.nodes[e];
|
49
|
+
if (!file)
|
50
|
+
return React.createElement(React.Fragment, null);
|
51
|
+
return (React.createElement(React.Fragment, null,
|
52
|
+
React.createElement(Text, { backgroundColor: index === e ? 'blue' : undefined }, `${selections.length > 0 &&
|
53
|
+
selections.some((e) => e.path === file.path)
|
54
|
+
? ' '
|
55
|
+
: selections.length > 0 &&
|
56
|
+
selections.some((e) => e.path !== file.path)
|
57
|
+
? ' '
|
58
|
+
: ''}${getNodeIcon(file)} ${file.name}`)));
|
59
|
+
})),
|
60
|
+
React.createElement(Badge, { color: "blueBright" }, calculatePercent(nodes ?? [], index + 1) + '%')));
|
61
|
+
};
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import { useStdout } from 'ink';
|
2
|
+
import { useEffect, useState } from 'react';
|
3
|
+
export const useResize = () => {
|
4
|
+
const { stdout } = useStdout();
|
5
|
+
const [size, setSize] = useState({
|
6
|
+
columns: stdout.columns,
|
7
|
+
rows: stdout.rows,
|
8
|
+
});
|
9
|
+
useEffect(() => {
|
10
|
+
function onResize() {
|
11
|
+
setSize({
|
12
|
+
columns: process.stdout.columns,
|
13
|
+
rows: process.stdout.rows,
|
14
|
+
});
|
15
|
+
}
|
16
|
+
process.stdout.on('resize', onResize);
|
17
|
+
process.stdout.write('\x1b[?1049h');
|
18
|
+
return () => {
|
19
|
+
process.stdout.off('resize', onResize);
|
20
|
+
process.stdout.write('\x1b[?1049l');
|
21
|
+
};
|
22
|
+
}, []);
|
23
|
+
return [size.columns, size.rows];
|
24
|
+
};
|
@@ -0,0 +1,44 @@
|
|
1
|
+
import { lstatSync, readdirSync } from 'node:fs';
|
2
|
+
import { join, parse } from 'node:path';
|
3
|
+
import { list } from 'drivelist';
|
4
|
+
export const getAllFiles = async (path) => {
|
5
|
+
if (path === '/' && process.platform === 'win32') {
|
6
|
+
const drives = await list();
|
7
|
+
return drives
|
8
|
+
.map((e) => e.mountpoints.map((mout) => ({
|
9
|
+
path: mout.path.replace('\\', '/'),
|
10
|
+
name: `(${mout.path.replace('\\', '')}) ${e.description}`,
|
11
|
+
type: 'DRIVE',
|
12
|
+
})))
|
13
|
+
.flat();
|
14
|
+
}
|
15
|
+
const nodes = readdirSync(path);
|
16
|
+
return nodes
|
17
|
+
.map((e) => e.replaceAll('\\', '/'))
|
18
|
+
.map((node) => {
|
19
|
+
try {
|
20
|
+
const nodePath = join(path, node).replaceAll('\\', '/');
|
21
|
+
if (lstatSync(nodePath).isDirectory())
|
22
|
+
return {
|
23
|
+
path: nodePath,
|
24
|
+
type: 'DIR',
|
25
|
+
name: parse(node).base,
|
26
|
+
};
|
27
|
+
return {
|
28
|
+
path: nodePath,
|
29
|
+
type: 'FILE',
|
30
|
+
name: parse(node).base,
|
31
|
+
};
|
32
|
+
}
|
33
|
+
catch (_e) {
|
34
|
+
return undefined;
|
35
|
+
}
|
36
|
+
})
|
37
|
+
.filter((e) => e != undefined)
|
38
|
+
.reduce((acc, e) => {
|
39
|
+
if (e.type === 'FILE')
|
40
|
+
return [acc[0], [...acc[1], e]];
|
41
|
+
return [[...acc[0], e], acc[1]];
|
42
|
+
}, [[], []])
|
43
|
+
.flat();
|
44
|
+
};
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { existsSync } from 'node:fs';
|
2
|
+
export const getUpperDirectory = async (path) => {
|
3
|
+
if (path === '/')
|
4
|
+
return path;
|
5
|
+
if (path.split('/').length === 2 &&
|
6
|
+
path.endsWith('/') &&
|
7
|
+
process.platform === 'win32')
|
8
|
+
return '/';
|
9
|
+
let pathToSearch = path.split('/').toSpliced(-1, 1).join('/');
|
10
|
+
if (process.platform === 'win32' && !path)
|
11
|
+
return '/';
|
12
|
+
if (pathToSearch.split('/').length === 1 && process.platform === 'win32')
|
13
|
+
pathToSearch = pathToSearch + '/';
|
14
|
+
if (!existsSync(pathToSearch))
|
15
|
+
return path;
|
16
|
+
return pathToSearch;
|
17
|
+
};
|
@@ -0,0 +1,9 @@
|
|
1
|
+
import { execSync } from 'node:child_process';
|
2
|
+
import { resolve } from 'node:path';
|
3
|
+
import trash from 'trash';
|
4
|
+
export const moveNodeToTrash = async (node) => {
|
5
|
+
if (process.platform === 'win32')
|
6
|
+
execSync(`recycle -f ${resolve(node.path)}`);
|
7
|
+
else
|
8
|
+
await trash(node.path);
|
9
|
+
};
|
@@ -0,0 +1,79 @@
|
|
1
|
+
import { statSync } from 'node:fs';
|
2
|
+
import { readdir, stat } from 'node:fs/promises';
|
3
|
+
import { join } from 'node:path';
|
4
|
+
import { list } from 'drivelist';
|
5
|
+
import { getDiskInfoSync } from 'node-disk-info';
|
6
|
+
import { useEffect, useRef } from 'react';
|
7
|
+
const dirSize = (isMounted) => {
|
8
|
+
const calculate = async (dir) => {
|
9
|
+
if (!isMounted.current)
|
10
|
+
throw new Error();
|
11
|
+
const files = await readdir(dir, { withFileTypes: true });
|
12
|
+
const paths = files.map(async (file) => {
|
13
|
+
const path = join(dir, file.name);
|
14
|
+
if (file.isDirectory())
|
15
|
+
return calculate(path);
|
16
|
+
if (file.isFile()) {
|
17
|
+
if (!isMounted.current)
|
18
|
+
throw new Error();
|
19
|
+
const { size } = await stat(path);
|
20
|
+
return size;
|
21
|
+
}
|
22
|
+
return 0;
|
23
|
+
});
|
24
|
+
return (await Promise.all(paths))
|
25
|
+
.flat()
|
26
|
+
.reduce((acc, size) => acc + size, 0);
|
27
|
+
};
|
28
|
+
return calculate;
|
29
|
+
};
|
30
|
+
export const getNodeDetails = () => {
|
31
|
+
const isMounted = useRef(true);
|
32
|
+
useEffect(() => {
|
33
|
+
isMounted.current = true;
|
34
|
+
return () => {
|
35
|
+
isMounted.current = false;
|
36
|
+
};
|
37
|
+
}, []);
|
38
|
+
return async (node) => {
|
39
|
+
if (node.type === 'DRIVE') {
|
40
|
+
const drives = await list();
|
41
|
+
const infos = getDiskInfoSync();
|
42
|
+
const disk = drives.find((e) => e.mountpoints.some((e) => e.path.replaceAll('\\', '/') === node.path));
|
43
|
+
if (!disk)
|
44
|
+
throw new Error('Disk not exist');
|
45
|
+
const info = infos.find((e) => node.path.startsWith(e.mounted));
|
46
|
+
if (!info)
|
47
|
+
throw new Error('Not disk info');
|
48
|
+
return {
|
49
|
+
...node,
|
50
|
+
size: disk.size ?? info.blocks,
|
51
|
+
additionalData: {
|
52
|
+
used: info.used,
|
53
|
+
percent: info.capacity,
|
54
|
+
fileSystem: info.filesystem,
|
55
|
+
available: info.available,
|
56
|
+
partitionSchema: disk.partitionTableType?.toUpperCase() ?? 'No schema',
|
57
|
+
deviceType: disk.isCard
|
58
|
+
? 'CARD'
|
59
|
+
: disk.isUSB
|
60
|
+
? 'USB'
|
61
|
+
: disk.isSCSI
|
62
|
+
? 'SCSI'
|
63
|
+
: disk.isVirtual
|
64
|
+
? 'VIRTUAL'
|
65
|
+
: 'REMOVABLE',
|
66
|
+
},
|
67
|
+
};
|
68
|
+
}
|
69
|
+
const stats = statSync(node.path);
|
70
|
+
return {
|
71
|
+
...node,
|
72
|
+
size: node.type === 'FILE'
|
73
|
+
? stats.size
|
74
|
+
: await dirSize(isMounted)(node.path),
|
75
|
+
createdDate: stats.birthtime,
|
76
|
+
lastModifiedDate: stats.mtime,
|
77
|
+
};
|
78
|
+
};
|
79
|
+
};
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import { copyFileSync, cpSync, existsSync, lstatSync, renameSync, } from 'node:fs';
|
2
|
+
import { join, parse } from 'node:path';
|
3
|
+
const generateFilename = (data, path) => {
|
4
|
+
const name = parse(data.path);
|
5
|
+
const numberGenerator = (index = 1) => {
|
6
|
+
if (data.type === 'FILE') {
|
7
|
+
if (existsSync(join(path, name.name + ` (${index})` + name.ext)))
|
8
|
+
return numberGenerator(++index);
|
9
|
+
return name.name + ` (${index})` + name.ext;
|
10
|
+
}
|
11
|
+
if (existsSync(join(path, name.base + ` (${index})`)))
|
12
|
+
return numberGenerator(++index);
|
13
|
+
return name.base + ` (${index})`;
|
14
|
+
};
|
15
|
+
if (existsSync(join(path, name.base)))
|
16
|
+
return numberGenerator();
|
17
|
+
return name.base;
|
18
|
+
};
|
19
|
+
const pasteWorker = (data, destinate, operation, replace) => {
|
20
|
+
const filename = replace
|
21
|
+
? parse(data.path).base
|
22
|
+
: generateFilename(data, destinate);
|
23
|
+
if (operation === 'CUT') {
|
24
|
+
renameSync(data.path, join(destinate, `/${filename}`));
|
25
|
+
}
|
26
|
+
if (operation === 'COPY' && data.type === 'FILE') {
|
27
|
+
copyFileSync(data.path, join(destinate, `/${filename}`));
|
28
|
+
}
|
29
|
+
if (operation === 'COPY' && data.type === 'DIR') {
|
30
|
+
cpSync(data.path, join(destinate, `/${filename}`), {
|
31
|
+
recursive: true,
|
32
|
+
force: true,
|
33
|
+
});
|
34
|
+
}
|
35
|
+
};
|
36
|
+
export const pasteOperation = async (data, destinate, replace) => {
|
37
|
+
if (!lstatSync(destinate).isDirectory())
|
38
|
+
throw new Error('The destinate must be a directory');
|
39
|
+
data.items.map((node) => pasteWorker(node, destinate, data.opeation, replace));
|
40
|
+
};
|
@@ -0,0 +1,107 @@
|
|
1
|
+
import { createUpdateTabSelectionEvent } from '../../tabs/events/update.tab.selection.js';
|
2
|
+
export const fileManagerLogic = (stateProvider, stateObserver, eventHandler, onInitJob, path, getAllFiles, uppderDir, nextDir, executeFile, history, selection, filterByGlobPattern) => {
|
3
|
+
const pathState = stateProvider(path);
|
4
|
+
const selectionsState = stateProvider(selection);
|
5
|
+
const selectedNode = stateProvider(undefined);
|
6
|
+
const filePaginated = onInitJob(() => getAllFiles(pathState.state.value), pathState.state);
|
7
|
+
const toggleSelected = (node) => {
|
8
|
+
if (selectionsState.state.value.some((e) => e.path === node.path))
|
9
|
+
selectionsState.setState(selectionsState.state.value.filter((e) => e.path !== node.path));
|
10
|
+
else
|
11
|
+
selectionsState.setState([...selectionsState.state.value, node]);
|
12
|
+
};
|
13
|
+
const inevertSelection = () => {
|
14
|
+
selectionsState.setState(filePaginated.data.value.filter((e) => !selectionsState.state.value.some((node) => e.path === node.path) && e.type !== 'DRIVE'));
|
15
|
+
};
|
16
|
+
const selectAll = () => {
|
17
|
+
selectionsState.setState(filePaginated.data.value.filter((e) => e.type !== 'DRIVE'));
|
18
|
+
};
|
19
|
+
const unSelectAll = () => {
|
20
|
+
selectionsState.setState([]);
|
21
|
+
};
|
22
|
+
const selectNode = async (node) => {
|
23
|
+
if (filePaginated.isLoading.value || filePaginated.isReloading.value)
|
24
|
+
return;
|
25
|
+
if (node.type === 'DIR' || node.type === 'DRIVE') {
|
26
|
+
const nextPath = await nextDir(node);
|
27
|
+
const prevPath = pathState.state.value;
|
28
|
+
pathState.setState(nextPath);
|
29
|
+
history.push(nextPath, prevPath);
|
30
|
+
return;
|
31
|
+
}
|
32
|
+
await executeFile(node);
|
33
|
+
};
|
34
|
+
const gotToUpper = async () => {
|
35
|
+
if (filePaginated.isLoading.value)
|
36
|
+
return;
|
37
|
+
const nextPath = await uppderDir(pathState.state.value);
|
38
|
+
if (!nextPath)
|
39
|
+
return;
|
40
|
+
const prevPath = pathState.state.value;
|
41
|
+
pathState.setState(nextPath);
|
42
|
+
history.push(pathState.state.value, prevPath);
|
43
|
+
};
|
44
|
+
const popHistory = () => {
|
45
|
+
if (filePaginated.isLoading.value)
|
46
|
+
return;
|
47
|
+
const nextPath = history.pop(pathState.state.value);
|
48
|
+
if (!nextPath)
|
49
|
+
return;
|
50
|
+
pathState.setState(nextPath);
|
51
|
+
};
|
52
|
+
const forwardHistory = () => {
|
53
|
+
if (filePaginated.isLoading.value)
|
54
|
+
return;
|
55
|
+
const nextPath = history.forward(pathState.state.value);
|
56
|
+
if (!nextPath)
|
57
|
+
return;
|
58
|
+
pathState.setState(nextPath);
|
59
|
+
};
|
60
|
+
const refresh = () => filePaginated.reload();
|
61
|
+
const selectNodeToDetails = (node) => {
|
62
|
+
selectedNode.setState(node);
|
63
|
+
};
|
64
|
+
const cleanNodeSelected = () => {
|
65
|
+
selectedNode.setState(undefined);
|
66
|
+
};
|
67
|
+
const navigateTo = (path) => {
|
68
|
+
const prevPath = pathState.state.value;
|
69
|
+
pathState.setState(path);
|
70
|
+
history.push(pathState.state.value, prevPath);
|
71
|
+
};
|
72
|
+
const selectByGlobPattern = (pattern) => {
|
73
|
+
selectionsState.setState(filterByGlobPattern(filePaginated.data.value.filter((e) => e.type !== 'DRIVE'), pattern));
|
74
|
+
};
|
75
|
+
stateObserver(() => {
|
76
|
+
if (!filePaginated.data.value)
|
77
|
+
return;
|
78
|
+
selectionsState.setState(selectionsState.state.value.filter((e) => filePaginated.data.value.some((node) => e.path === node.path)));
|
79
|
+
}, filePaginated.data);
|
80
|
+
stateObserver(() => {
|
81
|
+
selectionsState.setState([]);
|
82
|
+
}, pathState.state);
|
83
|
+
stateObserver(() => {
|
84
|
+
eventHandler.publish(createUpdateTabSelectionEvent(selectionsState.state.value));
|
85
|
+
}, selectionsState.state);
|
86
|
+
return {
|
87
|
+
files: filePaginated.data,
|
88
|
+
isLoading: filePaginated.isLoading,
|
89
|
+
isError: filePaginated.error,
|
90
|
+
forwardHistory,
|
91
|
+
popHistory,
|
92
|
+
selectNode,
|
93
|
+
refresh,
|
94
|
+
path: pathState.state,
|
95
|
+
gotToUpper,
|
96
|
+
selections: selectionsState.state,
|
97
|
+
toggleSelected,
|
98
|
+
selectAll,
|
99
|
+
inevertSelection,
|
100
|
+
unSelectAll,
|
101
|
+
selectedNode: selectedNode.state,
|
102
|
+
selectNodeToDetails,
|
103
|
+
navigateTo,
|
104
|
+
cleanNodeSelected,
|
105
|
+
selectByGlobPattern,
|
106
|
+
};
|
107
|
+
};
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import { createUpdateTabDataEvent } from '../../tabs/events/update.tab.data.js';
|
2
|
+
export const historyManager = (valueProvider, eventHandler, historyInitial) => {
|
3
|
+
const backHitory = valueProvider(historyInitial.previous);
|
4
|
+
const forwardHistory = valueProvider(historyInitial.forward);
|
5
|
+
const push = (path, currentPath) => {
|
6
|
+
backHitory.value.push(currentPath);
|
7
|
+
forwardHistory.value = [];
|
8
|
+
eventHandler.publish(createUpdateTabDataEvent({
|
9
|
+
path: path,
|
10
|
+
history: {
|
11
|
+
previous: backHitory.value,
|
12
|
+
forward: forwardHistory.value,
|
13
|
+
},
|
14
|
+
}));
|
15
|
+
};
|
16
|
+
const pop = (currentPath) => {
|
17
|
+
const path = backHitory.value.pop();
|
18
|
+
if (path)
|
19
|
+
forwardHistory.value.push(currentPath);
|
20
|
+
eventHandler.publish(createUpdateTabDataEvent({
|
21
|
+
path: path ?? currentPath,
|
22
|
+
history: {
|
23
|
+
previous: backHitory.value,
|
24
|
+
forward: forwardHistory.value,
|
25
|
+
},
|
26
|
+
}));
|
27
|
+
return path;
|
28
|
+
};
|
29
|
+
const forward = (currentPath) => {
|
30
|
+
const path = forwardHistory.value.pop();
|
31
|
+
if (path)
|
32
|
+
backHitory.value.push(currentPath);
|
33
|
+
eventHandler.publish(createUpdateTabDataEvent({
|
34
|
+
path: path ?? currentPath,
|
35
|
+
history: {
|
36
|
+
previous: backHitory.value,
|
37
|
+
forward: forwardHistory.value,
|
38
|
+
},
|
39
|
+
}));
|
40
|
+
return path;
|
41
|
+
};
|
42
|
+
return {
|
43
|
+
push,
|
44
|
+
pop,
|
45
|
+
forward,
|
46
|
+
};
|
47
|
+
};
|