@danixl30/file-explorer-cli 1.0.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.
Files changed (110) hide show
  1. package/LICENSE +21 -0
  2. package/dist/alerts/AlertContextProvider.js +13 -0
  3. package/dist/alerts/components/AlertDisplay.js +19 -0
  4. package/dist/alerts/logic/alert.logic.js +16 -0
  5. package/dist/app.js +27 -0
  6. package/dist/bookmark/Bookmark.js +123 -0
  7. package/dist/bookmark/components/BookmarkList.js +47 -0
  8. package/dist/bookmark/context/BookmarkPaneContext.js +9 -0
  9. package/dist/bookmark/events/add.item.js +8 -0
  10. package/dist/bookmark/implementations/clean.bookmarks.js +6 -0
  11. package/dist/bookmark/implementations/get.all.bookmarks.js +2 -0
  12. package/dist/bookmark/implementations/save.bookmarks.js +5 -0
  13. package/dist/bookmark/logic/bookmark.manager.js +25 -0
  14. package/dist/bookmark/services/clean.bookmarks.js +1 -0
  15. package/dist/bookmark/services/get.all.bookmarks.js +1 -0
  16. package/dist/bookmark/services/write.bookmarks.js +1 -0
  17. package/dist/bookmark/types/bookmark.js +1 -0
  18. package/dist/cli.js +31 -0
  19. package/dist/clipboard/ClipboardProvider.js +8 -0
  20. package/dist/clipboard/logic/clipboard-logic.js +10 -0
  21. package/dist/core/application/event-handler/event-handler.js +1 -0
  22. package/dist/core/application/event-handler/listener/event-listener.js +1 -0
  23. package/dist/core/application/event-handler/types/event.js +1 -0
  24. package/dist/core/application/event-handler/types/subscription.js +1 -0
  25. package/dist/core/application/id-generator/id.generator.js +1 -0
  26. package/dist/core/application/init-layout/init-layout.js +1 -0
  27. package/dist/core/application/input-manager/input-manager.js +1 -0
  28. package/dist/core/application/input-manager/types/input-manager-result.js +1 -0
  29. package/dist/core/application/on-init/on-init.js +1 -0
  30. package/dist/core/application/on-init-job/lazy/on-init-job-lazy.js +1 -0
  31. package/dist/core/application/on-init-job/on-init-job.js +1 -0
  32. package/dist/core/application/pagination-manager/pagination-manager.js +1 -0
  33. package/dist/core/application/pagination-manager/types/pagination-result.js +1 -0
  34. package/dist/core/application/service/application-service.js +1 -0
  35. package/dist/core/application/state/state-factory.js +1 -0
  36. package/dist/core/application/state/state-provider.js +1 -0
  37. package/dist/core/application/state-observers/state-observer.js +1 -0
  38. package/dist/core/application/timer/sleep/sleep.js +1 -0
  39. package/dist/core/application/timer/timer.js +19 -0
  40. package/dist/core/application/value-provider/value-provider.js +1 -0
  41. package/dist/core/infraestructure/db/bookmark.db.js +4 -0
  42. package/dist/core/infraestructure/event-handler/context/EventProvider.js +9 -0
  43. package/dist/core/infraestructure/event-handler/listener/event-listener-factory.js +8 -0
  44. package/dist/core/infraestructure/event-handler/useEventHadler.js +27 -0
  45. package/dist/core/infraestructure/initLayout/useLayoutEffectOnInit.js +4 -0
  46. package/dist/core/infraestructure/input-manager/useInputManager.js +14 -0
  47. package/dist/core/infraestructure/on-init/useEffectOnInit.js +9 -0
  48. package/dist/core/infraestructure/on-init-job/nativeOnInitJob.js +37 -0
  49. package/dist/core/infraestructure/on-init-job/nativeOnInitJobLazy.js +32 -0
  50. package/dist/core/infraestructure/pagination-manager/data-transforms/infinite.js +1 -0
  51. package/dist/core/infraestructure/pagination-manager/data-transforms/normal.js +1 -0
  52. package/dist/core/infraestructure/pagination-manager/usePaginationManager.js +59 -0
  53. package/dist/core/infraestructure/state/useRefStateProvider.js +39 -0
  54. package/dist/core/infraestructure/state/useStateProvider.js +35 -0
  55. package/dist/core/infraestructure/state-observer/useEffectStateObserver.js +16 -0
  56. package/dist/core/infraestructure/timer/sleep.js +2 -0
  57. package/dist/core/infraestructure/uuid/uuid.generator.js +2 -0
  58. package/dist/core/infraestructure/value-provider/useRefValueProvider.js +12 -0
  59. package/dist/core/utils/argument.type.js +1 -0
  60. package/dist/core/utils/optional.js +1 -0
  61. package/dist/file-display/FileDisplay.js +456 -0
  62. package/dist/file-display/components/Loading.js +4 -0
  63. package/dist/file-display/components/NodeDetails.js +50 -0
  64. package/dist/file-display/components/NodeList.js +61 -0
  65. package/dist/file-display/events/node.written.js +7 -0
  66. package/dist/file-display/hooks/useResize.js +24 -0
  67. package/dist/file-display/implementation/create.dir.js +7 -0
  68. package/dist/file-display/implementation/delete.js +9 -0
  69. package/dist/file-display/implementation/execute.command.async.js +6 -0
  70. package/dist/file-display/implementation/execute.command.sync.js +9 -0
  71. package/dist/file-display/implementation/execute.file.js +4 -0
  72. package/dist/file-display/implementation/filter.glob.js +4 -0
  73. package/dist/file-display/implementation/get.all.files.js +44 -0
  74. package/dist/file-display/implementation/get.directory.js +8 -0
  75. package/dist/file-display/implementation/get.upper.directory.js +17 -0
  76. package/dist/file-display/implementation/move.trash.js +9 -0
  77. package/dist/file-display/implementation/node.details.js +79 -0
  78. package/dist/file-display/implementation/paste.operation.js +40 -0
  79. package/dist/file-display/implementation/rename.js +6 -0
  80. package/dist/file-display/logic/file.manager.js +107 -0
  81. package/dist/file-display/logic/history.manager.js +47 -0
  82. package/dist/file-display/logic/node.detail.js +8 -0
  83. package/dist/file-display/logic/operations.manager.js +103 -0
  84. package/dist/file-display/services/create.dir.js +1 -0
  85. package/dist/file-display/services/delete.js +1 -0
  86. package/dist/file-display/services/execute.command.async.js +1 -0
  87. package/dist/file-display/services/execute.command.sync.js +1 -0
  88. package/dist/file-display/services/execute.file.js +1 -0
  89. package/dist/file-display/services/filter.glob.js +1 -0
  90. package/dist/file-display/services/get.all.files.js +1 -0
  91. package/dist/file-display/services/get.directory.js +1 -0
  92. package/dist/file-display/services/get.upper.directory.js +1 -0
  93. package/dist/file-display/services/move.trash.js +1 -0
  94. package/dist/file-display/services/node.details.js +1 -0
  95. package/dist/file-display/services/paste.operation.js +1 -0
  96. package/dist/file-display/services/remane.js +1 -0
  97. package/dist/file-display/types/node.details.js +1 -0
  98. package/dist/file-display/types/node.js +1 -0
  99. package/dist/file-display/utils/icon.parser.js +144 -0
  100. package/dist/input-capture/InputCapureWrapper.js +10 -0
  101. package/dist/input-capture/InputContextCapture.js +26 -0
  102. package/dist/tabs/TabsContext.js +17 -0
  103. package/dist/tabs/events/update.tab.data.js +8 -0
  104. package/dist/tabs/events/update.tab.selection.js +8 -0
  105. package/dist/tabs/logic/tabsManagerLogic.js +58 -0
  106. package/dist/tabs/wrapper/tabs.wrapper.js +38 -0
  107. package/dist/text-field/TextFieldCaptureContext.js +24 -0
  108. package/dist/text-field/TextFieldWrapper.js +21 -0
  109. package/package.json +62 -0
  110. 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,7 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ export const createNodeWrittenEvent = () => ({
3
+ id: randomUUID(),
4
+ name: NODE_WRITTEN,
5
+ timestamp: new Date(),
6
+ });
7
+ export const NODE_WRITTEN = 'NODE_WRITTEN';
@@ -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,7 @@
1
+ import { mkdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ export const createDirectory = async (path, dir) => {
4
+ mkdirSync(join(path, '/' + dir), {
5
+ recursive: true,
6
+ });
7
+ };
@@ -0,0 +1,9 @@
1
+ import { rmSync, unlinkSync } from 'node:fs';
2
+ export const deleteNode = async (node) => {
3
+ if (node.type === 'FILE')
4
+ unlinkSync(node.path);
5
+ if (node.type === 'DIR')
6
+ rmSync(node.path, {
7
+ recursive: true,
8
+ });
9
+ };
@@ -0,0 +1,6 @@
1
+ import { exec } from 'node:child_process';
2
+ export const executeCommandAsync = async (path, command) => {
3
+ exec(command, {
4
+ cwd: path,
5
+ });
6
+ };
@@ -0,0 +1,9 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ export const executeCommandSync = async (path, command) => {
3
+ spawnSync(command, {
4
+ cwd: path,
5
+ stdio: 'inherit',
6
+ shell: true,
7
+ });
8
+ return 'Command executed';
9
+ };
@@ -0,0 +1,4 @@
1
+ import { exec } from 'node:child_process';
2
+ export const executeFile = async (node) => {
3
+ exec(node.path);
4
+ };
@@ -0,0 +1,4 @@
1
+ import { minimatch } from 'minimatch';
2
+ export const filterByGlobPattern = (nodes, pattern) => nodes.filter((e) => minimatch(e.path, pattern, {
3
+ matchBase: true,
4
+ }));
@@ -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,8 @@
1
+ import { existsSync } from 'node:fs';
2
+ export const getNextDir = async (node) => {
3
+ if (node.type === 'DRIVE')
4
+ return node.path;
5
+ if (!existsSync(node.path))
6
+ throw new Error('Directory not exist');
7
+ return node.path;
8
+ };
@@ -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,6 @@
1
+ import { renameSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ export const renameNode = async (node, newName) => {
4
+ const basePath = node.path.split('/').toSpliced(-1, 1).join('/');
5
+ renameSync(node.path, join(basePath, `/${newName}`));
6
+ };
@@ -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
+ };
@@ -0,0 +1,8 @@
1
+ export const nodeDetailLogic = (node, onInitJob, getNodeDetails) => {
2
+ const detailsJob = onInitJob(() => getNodeDetails(node));
3
+ return {
4
+ data: detailsJob.data,
5
+ isLoading: detailsJob.isLoading,
6
+ error: detailsJob.error,
7
+ };
8
+ };