@danixl30/file-explorer-cli 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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.