@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,103 @@
1
+ export const operationsManager = (fileManager, clipboard, pasteOperation, deleteNode, renameNode, createDir, executeCommandSync, executeCommandAsync, moveToTrash) => {
2
+ const paste = async (replace) => {
3
+ if (!clipboard.item.value)
4
+ return;
5
+ if (fileManager.files.value?.some((e) => e.type === 'DRIVE'))
6
+ throw new Error(`Can't past outside a drive`);
7
+ await pasteOperation(clipboard.item.value, fileManager.path.value, replace);
8
+ clipboard.removeItem();
9
+ fileManager.cleanNodeSelected();
10
+ fileManager.refresh();
11
+ };
12
+ const copy = (node) => {
13
+ if (node.type === 'DRIVE')
14
+ return;
15
+ clipboard.setItem({
16
+ items: fileManager.selections.value.length === 0
17
+ ? [node]
18
+ : fileManager.selections.value,
19
+ opeation: 'COPY',
20
+ });
21
+ };
22
+ const cut = (node) => {
23
+ if (node.type === 'DRIVE')
24
+ return;
25
+ clipboard.setItem({
26
+ items: fileManager.selections.value.length === 0
27
+ ? [node]
28
+ : fileManager.selections.value,
29
+ opeation: 'COPY',
30
+ });
31
+ };
32
+ const removeNode = async (node) => {
33
+ const clipboardItem = clipboard.item.value;
34
+ if (fileManager.selections.value.length > 0) {
35
+ if (clipboardItem?.items.some((e) => fileManager.selections.value.some((node) => e.path === node.path)))
36
+ throw new Error(`Can't delete the item that you set on clipboard`);
37
+ if (fileManager.selections.value.some((e) => e.type === 'DRIVE'))
38
+ throw new Error('you can not remove a drive');
39
+ await Promise.all(fileManager.selections.value.map((node) => deleteNode(node)));
40
+ fileManager.refresh();
41
+ return;
42
+ }
43
+ if (clipboardItem?.items.some((e) => e.path === node.path))
44
+ throw new Error(`Can't delete the item that you set on clipboard`);
45
+ if (node.type === 'DRIVE')
46
+ throw new Error('you can not remove a drive');
47
+ await deleteNode(node);
48
+ fileManager.cleanNodeSelected();
49
+ fileManager.refresh();
50
+ };
51
+ const moveNodeToTrash = async (node) => {
52
+ const clipboardItem = clipboard.item.value;
53
+ if (fileManager.selections.value.length > 0) {
54
+ if (clipboardItem?.items.some((e) => fileManager.selections.value.some((node) => e.path === node.path)))
55
+ throw new Error(`Can't delete the item that you set on clipboard`);
56
+ if (fileManager.selections.value.some((e) => e.type === 'DRIVE'))
57
+ throw new Error('you can not remove a drive');
58
+ await Promise.all(fileManager.selections.value.map((node) => moveNodeToTrash(node)));
59
+ fileManager.refresh();
60
+ return;
61
+ }
62
+ if (clipboardItem?.items.some((e) => e.path === node.path))
63
+ throw new Error(`Can't delete the item that you set on clipboard`);
64
+ if (node.type === 'DRIVE')
65
+ throw new Error('you can not remove a drive');
66
+ await moveToTrash(node);
67
+ fileManager.cleanNodeSelected();
68
+ fileManager.refresh();
69
+ };
70
+ const rename = async (node, newName) => {
71
+ const clipboardItem = clipboard.item.value;
72
+ if (clipboardItem?.items.some((e) => e.path === node.path))
73
+ throw new Error(`Can't rename item that you set on clipboard`);
74
+ if (node.type === 'DRIVE')
75
+ throw new Error('you can not rename a drive');
76
+ await renameNode(node, newName);
77
+ fileManager.cleanNodeSelected();
78
+ fileManager.refresh();
79
+ };
80
+ const createDirectory = async (name) => {
81
+ if (fileManager.files.value?.some((e) => e.type === 'DRIVE'))
82
+ throw new Error(`Can't create a folder outside a drive`);
83
+ await createDir(fileManager.path.value, name);
84
+ fileManager.refresh();
85
+ };
86
+ const runCommandAsync = async (command) => executeCommandAsync(fileManager.path.value, command);
87
+ const runCommandSync = async (command) => {
88
+ const data = await executeCommandSync(fileManager.path.value, command);
89
+ fileManager.refresh();
90
+ return data;
91
+ };
92
+ return {
93
+ paste,
94
+ copy,
95
+ rename,
96
+ removeNode,
97
+ createDirectory,
98
+ cut,
99
+ runCommandAsync,
100
+ runCommandSync,
101
+ moveNodeToTrash,
102
+ };
103
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,144 @@
1
+ const dirIcons = (node) => {
2
+ if (node.name === 'test' || node.name === 'tests')
3
+ return '󰙨 ';
4
+ if (node.name === 'node_modules')
5
+ return ' ';
6
+ if (node.name === '.git')
7
+ return ' ';
8
+ if (node.name === 'src')
9
+ return ' ';
10
+ if (node.name === '.vscode')
11
+ return ' ';
12
+ if (node.name === '.aws')
13
+ return ' ';
14
+ if (node.name === '.docker')
15
+ return ' ';
16
+ if (node.name === '.config')
17
+ return ' ';
18
+ if (node.name === 'Documents' || node.name === 'docs')
19
+ return ' ';
20
+ if (node.name === 'Downloads')
21
+ return '󰉍 ';
22
+ if (node.name === 'Desktop')
23
+ return '󰧨 ';
24
+ if (node.name.toLowerCase() === 'music')
25
+ return '󱍙 ';
26
+ if (node.name.toLowerCase() === 'pictures' ||
27
+ node.name.toLowerCase() === 'picture')
28
+ return '󰉏 ';
29
+ if (node.name.toLowerCase() === 'videos' ||
30
+ node.name.toLowerCase() === 'video')
31
+ return ' ';
32
+ if (node.name === 'bin')
33
+ return ' ';
34
+ return ' ';
35
+ };
36
+ const fileIcons = (node) => {
37
+ if (node.name === 'package.json' ||
38
+ node.name === '.nvmrc' ||
39
+ node.name === '.node-version')
40
+ return ' ';
41
+ if (node.name === 'Dockerfile' ||
42
+ node.name === '.dockerignore' ||
43
+ node.name === 'docker-compose.yml')
44
+ return ' ';
45
+ if (node.name === '.gitignore' || node.name === '.gitattributes')
46
+ return ' ';
47
+ if (node.name.toLowerCase() === 'readme.md')
48
+ return ' ';
49
+ if (node.name.endsWith('.key') || node.name.endsWith('.gpg'))
50
+ return ' ';
51
+ if (node.name.endsWith('.ts'))
52
+ return ' ';
53
+ if (node.name.endsWith('.c'))
54
+ return ' ';
55
+ if (node.name.endsWith('.exe'))
56
+ return 'ﬓ ';
57
+ if (node.name.endsWith('.sln'))
58
+ return ' ';
59
+ if (node.name.endsWith('.cs'))
60
+ return ' ';
61
+ if (node.name.endsWith('.js'))
62
+ return ' ';
63
+ if (node.name.endsWith('.txt'))
64
+ return ' ';
65
+ if (node.name.endsWith('.zip') ||
66
+ node.name.endsWith('.rar') ||
67
+ node.name.endsWith('.7z'))
68
+ return ' ';
69
+ if (node.name.endsWith('.cpp'))
70
+ return ' ';
71
+ if (node.name.endsWith('.html'))
72
+ return ' ';
73
+ if (node.name.endsWith('.png') ||
74
+ node.name.endsWith('.jpg') ||
75
+ node.name.endsWith('.jpeg') ||
76
+ node.name.endsWith('.gif') ||
77
+ node.name.endsWith('.bmp') ||
78
+ node.name.endsWith('.cur') ||
79
+ node.name.endsWith('.tiff') ||
80
+ node.name.endsWith('.svg'))
81
+ return ' ';
82
+ if (node.name.endsWith('.java'))
83
+ return ' ';
84
+ if (node.name.endsWith('.bat'))
85
+ return ' ';
86
+ if (node.name.endsWith('.md'))
87
+ return ' ';
88
+ if (node.name.endsWith('.kt'))
89
+ return ' ';
90
+ if (node.name.endsWith('.php'))
91
+ return ' ';
92
+ if (node.name.endsWith('.json'))
93
+ return ' ';
94
+ if (node.name.endsWith('.py'))
95
+ return ' ';
96
+ if (node.name.endsWith('.lock'))
97
+ return ' ';
98
+ if (node.name.endsWith('.toml') || node.name.endsWith('.ini'))
99
+ return ' ';
100
+ if (node.name.endsWith('.rs'))
101
+ return ' ';
102
+ if (node.name.endsWith('.sql'))
103
+ return '.sql';
104
+ if (node.name.endsWith('.zig'))
105
+ return ' ';
106
+ if (node.name.endsWith('.tsx') || node.name.endsWith('.jsx'))
107
+ return ' ';
108
+ if (node.name.endsWith('.pdf'))
109
+ return ' ';
110
+ if (node.name.endsWith('.docx'))
111
+ return ' ';
112
+ if (node.name.endsWith('.doc'))
113
+ return '󱎒 ';
114
+ if (node.name.endsWith('.xls') || node.name.endsWith('.xlsx'))
115
+ return '󱎒 ';
116
+ if (node.name.endsWith('.ppt') || node.name.endsWith('.pptx'))
117
+ return '󱎐 ';
118
+ if (node.name.endsWith('.psd'))
119
+ return ' ';
120
+ if (node.name.endsWith('.css'))
121
+ return ' ';
122
+ if (node.name.endsWith('.dll'))
123
+ return ' ';
124
+ if (node.name.endsWith('.mp3') ||
125
+ node.name.endsWith('.ma4') ||
126
+ node.name.endsWith('.wav') ||
127
+ node.name.endsWith('.mp3'))
128
+ return ' ';
129
+ if (node.name.endsWith('.mkv') ||
130
+ node.name.endsWith('.mp4') ||
131
+ node.name.endsWith('.avi'))
132
+ return ' ';
133
+ if (node.name.endsWith('.log'))
134
+ return ' ';
135
+ return ' ';
136
+ };
137
+ const driveIcon = (_node) => {
138
+ return ' ';
139
+ };
140
+ export const getNodeIcon = (node) => node.type === 'DIR'
141
+ ? dirIcons(node)
142
+ : node.type === 'FILE'
143
+ ? fileIcons(node)
144
+ : driveIcon(node);
@@ -0,0 +1,10 @@
1
+ import { Box, Text } from 'ink';
2
+ import React, { useContext } from 'react';
3
+ import { InputContextCapture } from './InputContextCapture.js';
4
+ export const InputCaptureWarpper = () => {
5
+ const inputCapture = useContext(InputContextCapture);
6
+ if (!inputCapture.isInputEnabled)
7
+ return React.createElement(React.Fragment, null);
8
+ return (React.createElement(Box, { borderStyle: "classic" },
9
+ React.createElement(Text, null, inputCapture.text)));
10
+ };
@@ -0,0 +1,26 @@
1
+ import { useInput } from 'ink';
2
+ import React, { createContext, useRef, useState } from 'react';
3
+ export const InputContextCapture = createContext(undefined);
4
+ export const InputContextCaptureProvider = (props) => {
5
+ const callback = useRef(undefined);
6
+ const [isInputEnabled, setInputEnable] = useState(false);
7
+ const [text, setText] = useState('');
8
+ const addCallback = (c, text) => {
9
+ callback.current = c;
10
+ setInputEnable(true);
11
+ setText(text);
12
+ };
13
+ useInput((input) => {
14
+ if (!isInputEnabled)
15
+ return;
16
+ callback.current?.(input);
17
+ callback.current = undefined;
18
+ setInputEnable(false);
19
+ setText('');
20
+ });
21
+ return (React.createElement(InputContextCapture.Provider, { value: {
22
+ addCallback,
23
+ text,
24
+ isInputEnabled,
25
+ } }, props.children));
26
+ };
@@ -0,0 +1,17 @@
1
+ import React, { createContext, useContext } from 'react';
2
+ import { EventContext } from '../core/infraestructure/event-handler/context/EventProvider.js';
3
+ import { useEffectOnInit } from '../core/infraestructure/on-init/useEffectOnInit.js';
4
+ import { useRefStateFactory } from '../core/infraestructure/state/useRefStateProvider.js';
5
+ import { useRefValueProvider } from '../core/infraestructure/value-provider/useRefValueProvider.js';
6
+ import { tabsManagerLogic } from './logic/tabsManagerLogic.js';
7
+ export const TabsContext = createContext(undefined);
8
+ export const TabsContextProvider = (props) => {
9
+ const data = tabsManagerLogic(useRefStateFactory(), useRefValueProvider(), () => ({
10
+ path: props.defaultPath,
11
+ history: {
12
+ previous: [],
13
+ forward: [],
14
+ },
15
+ }), useEffectOnInit, useContext(EventContext));
16
+ return (React.createElement(TabsContext.Provider, { value: data }, props.children));
17
+ };
@@ -0,0 +1,8 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ export const UPDATE_TAB_DATA = 'UPDATE_TAB_DATA';
3
+ export const createUpdateTabDataEvent = (data) => ({
4
+ ...data,
5
+ id: randomUUID(),
6
+ name: UPDATE_TAB_DATA,
7
+ timestamp: new Date(),
8
+ });
@@ -0,0 +1,8 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ export const createUpdateTabSelectionEvent = (selection) => ({
3
+ selection,
4
+ id: randomUUID(),
5
+ name: UPDATE_TAB_SELECTION,
6
+ timestamp: new Date(),
7
+ });
8
+ export const UPDATE_TAB_SELECTION = 'UPDATE_TAB_SELECTION';
@@ -0,0 +1,58 @@
1
+ import { UPDATE_TAB_DATA } from '../events/update.tab.data.js';
2
+ import { UPDATE_TAB_SELECTION, } from '../events/update.tab.selection.js';
3
+ export const tabsManagerLogic = (stateFactory, valueProvider, createTab, onInit, eventHandler) => {
4
+ const tabsState = stateFactory([createTab()]);
5
+ const selectionInTabs = valueProvider([]);
6
+ const indexState = stateFactory(0);
7
+ const updateTabData = (tabData) => {
8
+ tabsState.setState(tabsState.state.value.map((e, index) => indexState.state.value === index ? tabData : e));
9
+ };
10
+ const addTab = (tab) => {
11
+ selectionInTabs.value.push([]);
12
+ tabsState.setState([...tabsState.state.value, tab ?? createTab()]);
13
+ indexState.setState(tabsState.state.value.length - 1);
14
+ };
15
+ const removeTab = (index) => {
16
+ selectionInTabs.value = selectionInTabs.value.filter((_e, i) => index !== i);
17
+ tabsState.setState(tabsState.state.value.filter((_e, i) => index !== i));
18
+ if (index >= tabsState.state.value.length)
19
+ indexState.setState(tabsState.state.value.length - 1);
20
+ };
21
+ const moveToNext = () => {
22
+ if (tabsState.state.value.length === 1)
23
+ return;
24
+ if (indexState.state.value === tabsState.state.value.length - 1)
25
+ indexState.setState(0);
26
+ else
27
+ indexState.setState(indexState.state.value + 1);
28
+ };
29
+ const moveToPrev = () => {
30
+ if (tabsState.state.value.length === 1)
31
+ return;
32
+ if (indexState.state.value === 0)
33
+ indexState.setState(tabsState.state.value.length - 1);
34
+ else
35
+ indexState.setState(indexState.state.value - 1);
36
+ };
37
+ onInit(() => {
38
+ eventHandler.subscribe(UPDATE_TAB_DATA, (data) => {
39
+ updateTabData({
40
+ path: data.path,
41
+ history: data.history,
42
+ });
43
+ });
44
+ eventHandler.subscribe(UPDATE_TAB_SELECTION, (data) => {
45
+ selectionInTabs.value[indexState.state.value] = data.selection;
46
+ });
47
+ });
48
+ return {
49
+ moveToPrev,
50
+ moveToNext,
51
+ removeTab,
52
+ addTab,
53
+ updateTabData,
54
+ index: indexState.state,
55
+ tabs: tabsState.state,
56
+ selectionInTabs,
57
+ };
58
+ };
@@ -0,0 +1,38 @@
1
+ import { Box, Text, useInput } from 'ink';
2
+ import React, { useContext, useEffect, useState } from 'react';
3
+ import { Bookmark } from '../../bookmark/Bookmark.js';
4
+ import FileDisplay from '../../file-display/FileDisplay.js';
5
+ import { InputContextCapture } from '../../input-capture/InputContextCapture.js';
6
+ import { TextFieldContextCapture } from '../../text-field/TextFieldCaptureContext.js';
7
+ import { TabsContext } from '../TabsContext.js';
8
+ export default function TabsWrapper() {
9
+ const tabs = useContext(TabsContext);
10
+ const inputCapture = useContext(InputContextCapture);
11
+ const textField = useContext(TextFieldContextCapture);
12
+ const [childKey, setChildKey] = useState(0);
13
+ useInput((input, key) => {
14
+ if (inputCapture.isInputEnabled || textField.isInputEnabled)
15
+ return;
16
+ if (key.tab)
17
+ tabs.moveToNext();
18
+ if (key.tab && key.shift)
19
+ tabs.moveToPrev();
20
+ if (input === 't' && key.ctrl)
21
+ tabs.addTab();
22
+ if (input === 'w' && key.ctrl)
23
+ tabs.removeTab(tabs.index.value);
24
+ });
25
+ useEffect(() => {
26
+ setChildKey((prev) => ++prev);
27
+ }, [tabs.tabs.value, tabs.index.value]);
28
+ return (React.createElement(React.Fragment, null,
29
+ React.createElement(Box, { flexDirection: "row" },
30
+ React.createElement(Bookmark, null),
31
+ React.createElement(Box, { height: "100%", flexDirection: "column" },
32
+ tabs.tabs.value.length > 1 && (React.createElement(Box, { width: "100%", flexDirection: "row" }, tabs.tabs.value.map((_e, index) => (React.createElement(Box, { borderStyle: "round", borderColor: index === tabs.index.value
33
+ ? 'blue'
34
+ : 'white' },
35
+ React.createElement(Text, null, `Tab: ${index + 1}`)))))),
36
+ tabs.index.value < tabs.tabs.value.length &&
37
+ tabs.index.value !== -1 && (React.createElement(FileDisplay, { selection: tabs.selectionInTabs.value[tabs.index.value] ?? [], key: childKey, initialData: tabs.tabs.value[tabs.index.value] }))))));
38
+ }
@@ -0,0 +1,24 @@
1
+ import React, { createContext, useRef, useState } from 'react';
2
+ export const TextFieldContextCapture = createContext(undefined);
3
+ export const TextFieldContextCaptureProvider = (props) => {
4
+ const textFieldData = useRef(undefined);
5
+ const [isInputEnabled, setInputEnable] = useState(false);
6
+ const [error, setError] = useState('');
7
+ const addCallback = (data) => {
8
+ textFieldData.current = data;
9
+ setInputEnable(true);
10
+ };
11
+ const cleanUp = () => {
12
+ textFieldData.current = undefined;
13
+ setInputEnable(false);
14
+ setError('');
15
+ };
16
+ return (React.createElement(TextFieldContextCapture.Provider, { value: {
17
+ addCallback,
18
+ isInputEnabled,
19
+ error,
20
+ setError,
21
+ data: textFieldData.current,
22
+ cleanUp,
23
+ } }, props.children));
24
+ };
@@ -0,0 +1,21 @@
1
+ import { StatusMessage, TextInput } from '@inkjs/ui';
2
+ import { Box, useInput } from 'ink';
3
+ import React, { useContext } from 'react';
4
+ import { TextFieldContextCapture } from './TextFieldCaptureContext.js';
5
+ export const TextFieldWrapper = () => {
6
+ const textField = useContext(TextFieldContextCapture);
7
+ useInput((_input, key) => {
8
+ if (key.escape)
9
+ textField.cleanUp();
10
+ });
11
+ if (!textField.isInputEnabled)
12
+ return React.createElement(React.Fragment, null);
13
+ return (React.createElement(Box, { borderStyle: "classic", flexDirection: "column", gap: 1 },
14
+ React.createElement(TextInput, { defaultValue: textField.data?.defaultValue, onChange: textField.data?.callback.onChange, placeholder: textField.data?.placeHolder, onSubmit: (data) => {
15
+ textField.cleanUp();
16
+ if (!textField.error)
17
+ textField.data?.callback.onSuccess(data);
18
+ } }),
19
+ textField.data?.text && !textField.error && (React.createElement(StatusMessage, { variant: "info" }, textField.data.text)),
20
+ textField.error && (React.createElement(StatusMessage, { variant: "error" }, textField.error))));
21
+ };
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@danixl30/file-explorer-cli",
3
+ "version": "1.0.0",
4
+ "license": "MIT",
5
+ "bin": {
6
+ "files": "dist/cli.js"
7
+ },
8
+ "type": "module",
9
+ "engines": {
10
+ "node": ">=16"
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "dependencies": {
16
+ "@inkjs/ui": "^2.0.0",
17
+ "clipboardy": "^4.0.0",
18
+ "drivelist": "^12.0.2",
19
+ "fullscreen-ink": "^0.0.2",
20
+ "ink": "^4.1.0",
21
+ "lowdb": "^7.0.1",
22
+ "meow": "^11.0.0",
23
+ "minimatch": "^10.0.1",
24
+ "node-disk-info": "^1.3.0",
25
+ "react": "^18.2.0",
26
+ "trash": "^9.0.0"
27
+ },
28
+ "devDependencies": {
29
+ "@biomejs/biome": "^1.9.4",
30
+ "@sindresorhus/tsconfig": "^3.0.1",
31
+ "@types/node": "^22.9.0",
32
+ "@types/react": "^18.0.32",
33
+ "ava": "^5.2.0",
34
+ "chalk": "^5.2.0",
35
+ "ink-testing-library": "^3.0.0",
36
+ "ts-node": "^10.9.1",
37
+ "typescript": "^5.0.3",
38
+ "xo": "^0.53.1"
39
+ },
40
+ "ava": {
41
+ "extensions": {
42
+ "ts": "module",
43
+ "tsx": "module"
44
+ },
45
+ "nodeArguments": [
46
+ "--loader=ts-node/esm"
47
+ ]
48
+ },
49
+ "xo": {
50
+ "extends": "xo-react",
51
+ "prettier": true,
52
+ "rules": {
53
+ "react/prop-types": "off"
54
+ }
55
+ },
56
+ "scripts": {
57
+ "lint": "pnpm biome check --write src/",
58
+ "build": "tsc",
59
+ "start": "tsc && node dist/cli",
60
+ "dev": "tsc --watch"
61
+ }
62
+ }
package/readme.md ADDED
@@ -0,0 +1,57 @@
1
+ # CLI file explorer
2
+
3
+ Simple CLI file explorer with NodeJs
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ $ npm install -g @danixl30/cli-file-explorer
9
+ ```
10
+
11
+ ## CLI
12
+
13
+ ```
14
+ $ files --help
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ Use ```UP``` and ```DOWN``` key to move the cursor at the directory.
20
+
21
+ Use ```CTRL + t``` to create a new tab. If the cursor in on a directory press ```t``` to create a new tab with that directory.
22
+
23
+ User ```CRTL + w``` to close current tab.
24
+
25
+ Use ```TAB``` and ```SHIFT + TAB``` to change tabs.
26
+
27
+ Use ```RIGHT``` to select files or directories. ```SHIFT + RIGHT``` to unselect. ```SHIFT + i``` to invert selection. ```SHIFT + a``` to selcet all.
28
+
29
+ Use ```c``` to copy the item in the cursor or copy current selection.
30
+
31
+ Use ```SHIFT + c``` to cut the item in the cursor or cut current selection.
32
+
33
+ Use ```p``` to paste items copied or cut. ```SHIFT + p``` to past and override if exist.
34
+
35
+ Use ```d``` and confirm with ```y``` to move file or dir to trash. ```SHIFT + d``` to romeve item, confirm with ```SHIFT + y```.
36
+
37
+ Use ```r``` to refresh current tab.
38
+
39
+ Use ```SHIFT + r``` to rename file or dir that is on cursor.
40
+
41
+ Use ```g``` to select items in dir by glob pattern.
42
+
43
+ Use ```CTRL + l``` to set path o current tab.
44
+
45
+ Use ```i``` to see details of item that is on cursor.
46
+
47
+ Use ```CTRL + d``` to close details pane.
48
+
49
+ Use ```;``` to set not blocking command.
50
+
51
+ Use ```:``` to set blocking command.
52
+
53
+ Use ```SHITF + s``` to add dir that is on cursor to bookmarks.
54
+
55
+ Use ```s``` to switch to bookmarks pane.
56
+
57
+ Use ```a``` to create a directory in current tab.