git0 0.2.11 → 0.2.13

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 (243) hide show
  1. package/docs/assets/js/c3a618e1.965a31da.js +1 -1
  2. package/docs/functions/index.html +2 -2
  3. package/docs-config/src/functions/index.md +1 -1
  4. package/package.json +2 -1
  5. package/readme.md +18 -30
  6. package/src/fm.js +1130 -0
  7. package/src/git0.js +3 -4
  8. package/src/github-api.js +14 -15
  9. package/TypeScript-React-Starter/LICENSE +0 -21
  10. package/TypeScript-React-Starter/README-CRA.md +0 -1281
  11. package/TypeScript-React-Starter/README.md +0 -810
  12. package/TypeScript-React-Starter/package.json +0 -28
  13. package/TypeScript-React-Starter/public/favicon.ico +0 -0
  14. package/TypeScript-React-Starter/public/index.html +0 -31
  15. package/TypeScript-React-Starter/src/App.css +0 -24
  16. package/TypeScript-React-Starter/src/App.test.tsx +0 -8
  17. package/TypeScript-React-Starter/src/App.tsx +0 -23
  18. package/TypeScript-React-Starter/src/actions/index.tsx +0 -24
  19. package/TypeScript-React-Starter/src/components/Hello.css +0 -13
  20. package/TypeScript-React-Starter/src/components/Hello.test.tsx +0 -30
  21. package/TypeScript-React-Starter/src/components/Hello.tsx +0 -35
  22. package/TypeScript-React-Starter/src/constants/index.tsx +0 -6
  23. package/TypeScript-React-Starter/src/containers/Hello.tsx +0 -20
  24. package/TypeScript-React-Starter/src/index.css +0 -5
  25. package/TypeScript-React-Starter/src/index.tsx +0 -22
  26. package/TypeScript-React-Starter/src/logo.svg +0 -7
  27. package/TypeScript-React-Starter/src/reducers/index.tsx +0 -15
  28. package/TypeScript-React-Starter/src/types/index.tsx +0 -5
  29. package/TypeScript-React-Starter/tsconfig.json +0 -29
  30. package/TypeScript-React-Starter/tslint.json +0 -11
  31. package/TypeScript-React-Starter/yarn.lock +0 -4785
  32. package/svelte-nodegui/.all-contributorsrc +0 -60
  33. package/svelte-nodegui/.vscode/launch.json +0 -29
  34. package/svelte-nodegui/CHANGELOG.md +0 -153
  35. package/svelte-nodegui/LICENSE +0 -22
  36. package/svelte-nodegui/README.md +0 -151
  37. package/svelte-nodegui/demo/.editorconfig +0 -15
  38. package/svelte-nodegui/demo/.vscode/extensions.json +0 -3
  39. package/svelte-nodegui/demo/livereload.js +0 -29
  40. package/svelte-nodegui/demo/package-lock.json +0 -3514
  41. package/svelte-nodegui/demo/package.json +0 -35
  42. package/svelte-nodegui/demo/src/App.svelte +0 -29
  43. package/svelte-nodegui/demo/src/app.ts +0 -31
  44. package/svelte-nodegui/demo/tsconfig.json +0 -40
  45. package/svelte-nodegui/demo/webpack.config.js +0 -110
  46. package/svelte-nodegui/extras/assets/kitchen.png +0 -0
  47. package/svelte-nodegui/extras/assets/nodegui.png +0 -0
  48. package/svelte-nodegui/extras/assets/nodegui_white.png +0 -0
  49. package/svelte-nodegui/extras/assets/start_icon.png +0 -0
  50. package/svelte-nodegui/extras/legal/logo/thanks.md +0 -13
  51. package/svelte-nodegui/extras/legal/yode/LICENSE +0 -21
  52. package/svelte-nodegui/extras/legal/yoga/LICENSE +0 -0
  53. package/svelte-nodegui/extras/logo/nodegui-circle.png +0 -0
  54. package/svelte-nodegui/extras/logo/nodegui.png +0 -0
  55. package/svelte-nodegui/extras/logo/nodegui.svg +0 -4
  56. package/svelte-nodegui/nativescript-svelte-todo.gif +0 -0
  57. package/svelte-nodegui/package-lock.json +0 -1749
  58. package/svelte-nodegui/package.json +0 -44
  59. package/svelte-nodegui/repl-workers/bundler/commonjs.js +0 -58
  60. package/svelte-nodegui/repl-workers/bundler/index.js +0 -353
  61. package/svelte-nodegui/repl-workers/package-lock.json +0 -484
  62. package/svelte-nodegui/repl-workers/package.json +0 -21
  63. package/svelte-nodegui/repl-workers/rollup.config.js +0 -21
  64. package/svelte-nodegui/rollup.config.js +0 -81
  65. package/svelte-nodegui/scripts/create-pkg.js +0 -42
  66. package/svelte-nodegui/src/components/AsComponent.svelte +0 -16
  67. package/svelte-nodegui/src/components/SlotComponent.svelte +0 -4
  68. package/svelte-nodegui/src/components/Template.svelte +0 -10
  69. package/svelte-nodegui/src/components/Template.svelte.d.ts +0 -6
  70. package/svelte-nodegui/src/components/index.ts +0 -2
  71. package/svelte-nodegui/src/dom/index.ts +0 -132
  72. package/svelte-nodegui/src/dom/nativescript-vue-next/LICENCE +0 -21
  73. package/svelte-nodegui/src/dom/nativescript-vue-next/index.ts +0 -19
  74. package/svelte-nodegui/src/dom/nativescript-vue-next/runtime/index.ts +0 -21
  75. package/svelte-nodegui/src/dom/nativescript-vue-next/runtime/nodes.ts +0 -890
  76. package/svelte-nodegui/src/dom/nativescript-vue-next/runtime/registry.ts +0 -262
  77. package/svelte-nodegui/src/dom/nativescript-vue-next/runtime/runtimeHelpers.ts +0 -20
  78. package/svelte-nodegui/src/dom/react-nodegui/LICENSE +0 -21
  79. package/svelte-nodegui/src/dom/react-nodegui/src/components/AbstractComponents/RNAbstractButton.ts +0 -67
  80. package/svelte-nodegui/src/dom/react-nodegui/src/components/Action/RNAction.ts +0 -146
  81. package/svelte-nodegui/src/dom/react-nodegui/src/components/Action/index.ts +0 -37
  82. package/svelte-nodegui/src/dom/react-nodegui/src/components/AnimatedImage/RNAnimatedImage.ts +0 -76
  83. package/svelte-nodegui/src/dom/react-nodegui/src/components/AnimatedImage/index.ts +0 -45
  84. package/svelte-nodegui/src/dom/react-nodegui/src/components/BoxView/RNBoxView.ts +0 -99
  85. package/svelte-nodegui/src/dom/react-nodegui/src/components/BoxView/index.ts +0 -49
  86. package/svelte-nodegui/src/dom/react-nodegui/src/components/Button/RNButton.ts +0 -71
  87. package/svelte-nodegui/src/dom/react-nodegui/src/components/Button/index.ts +0 -49
  88. package/svelte-nodegui/src/dom/react-nodegui/src/components/CheckBox/RNCheckBox.ts +0 -71
  89. package/svelte-nodegui/src/dom/react-nodegui/src/components/CheckBox/index.ts +0 -50
  90. package/svelte-nodegui/src/dom/react-nodegui/src/components/ComboBox/RNComboBox.ts +0 -118
  91. package/svelte-nodegui/src/dom/react-nodegui/src/components/ComboBox/index.ts +0 -50
  92. package/svelte-nodegui/src/dom/react-nodegui/src/components/Dial/RNDial.ts +0 -69
  93. package/svelte-nodegui/src/dom/react-nodegui/src/components/Dial/index.ts +0 -41
  94. package/svelte-nodegui/src/dom/react-nodegui/src/components/GridView/GridColumn/RNGridColumn.ts +0 -95
  95. package/svelte-nodegui/src/dom/react-nodegui/src/components/GridView/GridColumn/index.ts +0 -49
  96. package/svelte-nodegui/src/dom/react-nodegui/src/components/GridView/GridRow/RNGridRow.ts +0 -141
  97. package/svelte-nodegui/src/dom/react-nodegui/src/components/GridView/GridRow/index.ts +0 -47
  98. package/svelte-nodegui/src/dom/react-nodegui/src/components/GridView/RNGridView.ts +0 -185
  99. package/svelte-nodegui/src/dom/react-nodegui/src/components/GridView/index.ts +0 -51
  100. package/svelte-nodegui/src/dom/react-nodegui/src/components/GridView/utils.ts +0 -59
  101. package/svelte-nodegui/src/dom/react-nodegui/src/components/Image/RNImage.ts +0 -135
  102. package/svelte-nodegui/src/dom/react-nodegui/src/components/Image/index.ts +0 -46
  103. package/svelte-nodegui/src/dom/react-nodegui/src/components/LineEdit/RNLineEdit.ts +0 -82
  104. package/svelte-nodegui/src/dom/react-nodegui/src/components/LineEdit/index.ts +0 -41
  105. package/svelte-nodegui/src/dom/react-nodegui/src/components/Menu/RNMenu.ts +0 -49
  106. package/svelte-nodegui/src/dom/react-nodegui/src/components/Menu/index.ts +0 -44
  107. package/svelte-nodegui/src/dom/react-nodegui/src/components/MenuBar/RNMenuBar.ts +0 -51
  108. package/svelte-nodegui/src/dom/react-nodegui/src/components/MenuBar/index.ts +0 -42
  109. package/svelte-nodegui/src/dom/react-nodegui/src/components/PlainTextEdit/RNPlainTextEdit.ts +0 -82
  110. package/svelte-nodegui/src/dom/react-nodegui/src/components/PlainTextEdit/index.ts +0 -44
  111. package/svelte-nodegui/src/dom/react-nodegui/src/components/ProgressBar/RNProgressBar.ts +0 -78
  112. package/svelte-nodegui/src/dom/react-nodegui/src/components/ProgressBar/index.ts +0 -43
  113. package/svelte-nodegui/src/dom/react-nodegui/src/components/RadioButton/RNRadioButton.ts +0 -50
  114. package/svelte-nodegui/src/dom/react-nodegui/src/components/RadioButton/index.ts +0 -43
  115. package/svelte-nodegui/src/dom/react-nodegui/src/components/ScrollArea/RNScrollArea.ts +0 -51
  116. package/svelte-nodegui/src/dom/react-nodegui/src/components/ScrollArea/index.ts +0 -44
  117. package/svelte-nodegui/src/dom/react-nodegui/src/components/Slider/RNSlider.ts +0 -97
  118. package/svelte-nodegui/src/dom/react-nodegui/src/components/Slider/index.ts +0 -46
  119. package/svelte-nodegui/src/dom/react-nodegui/src/components/SpinBox/RNSpinBox.ts +0 -88
  120. package/svelte-nodegui/src/dom/react-nodegui/src/components/SpinBox/index.ts +0 -49
  121. package/svelte-nodegui/src/dom/react-nodegui/src/components/Svg/RNSvg.ts +0 -67
  122. package/svelte-nodegui/src/dom/react-nodegui/src/components/Svg/index.ts +0 -43
  123. package/svelte-nodegui/src/dom/react-nodegui/src/components/SystemTrayIcon/RNSystemTrayIcon.ts +0 -141
  124. package/svelte-nodegui/src/dom/react-nodegui/src/components/SystemTrayIcon/index.ts +0 -43
  125. package/svelte-nodegui/src/dom/react-nodegui/src/components/Tab/RNTab.ts +0 -69
  126. package/svelte-nodegui/src/dom/react-nodegui/src/components/Tab/index.ts +0 -48
  127. package/svelte-nodegui/src/dom/react-nodegui/src/components/TabItem/RNTabItem.ts +0 -72
  128. package/svelte-nodegui/src/dom/react-nodegui/src/components/TabItem/index.ts +0 -40
  129. package/svelte-nodegui/src/dom/react-nodegui/src/components/Text/RNText.ts +0 -65
  130. package/svelte-nodegui/src/dom/react-nodegui/src/components/Text/index.ts +0 -49
  131. package/svelte-nodegui/src/dom/react-nodegui/src/components/View/RNView.ts +0 -306
  132. package/svelte-nodegui/src/dom/react-nodegui/src/components/View/index.ts +0 -44
  133. package/svelte-nodegui/src/dom/react-nodegui/src/components/Window/RNWindow.ts +0 -66
  134. package/svelte-nodegui/src/dom/react-nodegui/src/components/Window/index.ts +0 -50
  135. package/svelte-nodegui/src/dom/react-nodegui/src/components/config.ts +0 -95
  136. package/svelte-nodegui/src/dom/react-nodegui/src/index.ts +0 -52
  137. package/svelte-nodegui/src/dom/react-nodegui/src/reconciler/index.ts +0 -226
  138. package/svelte-nodegui/src/dom/react-nodegui/src/utils/decoupleFromReact.ts +0 -2
  139. package/svelte-nodegui/src/dom/react-nodegui/src/utils/helpers.ts +0 -14
  140. package/svelte-nodegui/src/dom/shared/Logger.ts +0 -16
  141. package/svelte-nodegui/src/dom/shared/index.ts +0 -1
  142. package/svelte-nodegui/src/dom/svelte/HeadElement.ts +0 -41
  143. package/svelte-nodegui/src/dom/svelte/RNObject.ts +0 -95
  144. package/svelte-nodegui/src/dom/svelte/StyleElement.ts +0 -92
  145. package/svelte-nodegui/src/dom/svelte/SvelteNodeGUIDocument.ts +0 -124
  146. package/svelte-nodegui/src/dom/svelte/TemplateElement.ts +0 -16
  147. package/svelte-nodegui/src/dom/svelte-elements.ts +0 -111
  148. package/svelte-nodegui/src/index.ts +0 -62
  149. package/svelte-nodegui/src/svelte-nodegui.ts +0 -159
  150. package/svelte-nodegui/src/transitions/bezier.ts +0 -112
  151. package/svelte-nodegui/src/transitions/index.ts +0 -227
  152. package/svelte-nodegui/tsconfig.json +0 -19
  153. package/svelte-nodegui/website/README.md +0 -33
  154. package/svelte-nodegui/website/blog/2019-05-30-welcome.md +0 -12
  155. package/svelte-nodegui/website/docs/api/classes/renderer.md +0 -49
  156. package/svelte-nodegui/website/docs/api/classes/rnaction.md +0 -608
  157. package/svelte-nodegui/website/docs/api/classes/rngridcolumn.md +0 -232
  158. package/svelte-nodegui/website/docs/api/classes/rngridrow.md +0 -247
  159. package/svelte-nodegui/website/docs/api/classes/rnmenu.md +0 -1337
  160. package/svelte-nodegui/website/docs/api/classes/rnmenubar.md +0 -1341
  161. package/svelte-nodegui/website/docs/api/globals.md +0 -832
  162. package/svelte-nodegui/website/docs/api/index.md +0 -0
  163. package/svelte-nodegui/website/docs/api/interfaces/_react_proxy_.reactproxycomponent.md +0 -38
  164. package/svelte-nodegui/website/docs/api/interfaces/abstractbuttonprops.md +0 -306
  165. package/svelte-nodegui/website/docs/api/interfaces/actionprops.md +0 -115
  166. package/svelte-nodegui/website/docs/api/interfaces/animatedimageprops.md +0 -297
  167. package/svelte-nodegui/website/docs/api/interfaces/boxviewprops.md +0 -245
  168. package/svelte-nodegui/website/docs/api/interfaces/buttonprops.md +0 -313
  169. package/svelte-nodegui/website/docs/api/interfaces/checkboxprops.md +0 -313
  170. package/svelte-nodegui/website/docs/api/interfaces/comboboxprops.md +0 -343
  171. package/svelte-nodegui/website/docs/api/interfaces/datawithoffset.md +0 -32
  172. package/svelte-nodegui/website/docs/api/interfaces/dialprops.md +0 -259
  173. package/svelte-nodegui/website/docs/api/interfaces/gridviewprops.md +0 -273
  174. package/svelte-nodegui/website/docs/api/interfaces/imageprops.md +0 -311
  175. package/svelte-nodegui/website/docs/api/interfaces/lineeditprops.md +0 -266
  176. package/svelte-nodegui/website/docs/api/interfaces/menubarprops.md +0 -245
  177. package/svelte-nodegui/website/docs/api/interfaces/menuprops.md +0 -245
  178. package/svelte-nodegui/website/docs/api/interfaces/plaintexteditprops.md +0 -259
  179. package/svelte-nodegui/website/docs/api/interfaces/progressbarprops.md +0 -266
  180. package/svelte-nodegui/website/docs/api/interfaces/radiobuttonprops.md +0 -282
  181. package/svelte-nodegui/website/docs/api/interfaces/scrollareaprops.md +0 -245
  182. package/svelte-nodegui/website/docs/api/interfaces/sliderprops.md +0 -329
  183. package/svelte-nodegui/website/docs/api/interfaces/spinboxprops.md +0 -273
  184. package/svelte-nodegui/website/docs/api/interfaces/systemtrayiconprops.md +0 -97
  185. package/svelte-nodegui/website/docs/api/interfaces/tabitemprops.md +0 -28
  186. package/svelte-nodegui/website/docs/api/interfaces/tabprops.md +0 -245
  187. package/svelte-nodegui/website/docs/api/interfaces/textprops.md +0 -277
  188. package/svelte-nodegui/website/docs/api/interfaces/viewprops.md +0 -261
  189. package/svelte-nodegui/website/docs/api/interfaces/windowprops.md +0 -245
  190. package/svelte-nodegui/website/docs/api/modules/_react_proxy_.md +0 -29
  191. package/svelte-nodegui/website/docs/doc1.md +0 -162
  192. package/svelte-nodegui/website/docs/guides/custom-nodegui-native-plugin.md +0 -6
  193. package/svelte-nodegui/website/docs/guides/debugging-in-vscode.md +0 -39
  194. package/svelte-nodegui/website/docs/guides/debugging.md +0 -59
  195. package/svelte-nodegui/website/docs/guides/getting-started.md +0 -141
  196. package/svelte-nodegui/website/docs/guides/handle-events.md +0 -188
  197. package/svelte-nodegui/website/docs/guides/images.md +0 -62
  198. package/svelte-nodegui/website/docs/guides/layout.md +0 -281
  199. package/svelte-nodegui/website/docs/guides/networking.md +0 -31
  200. package/svelte-nodegui/website/docs/guides/packaging.md +0 -32
  201. package/svelte-nodegui/website/docs/guides/scroll-view.md +0 -69
  202. package/svelte-nodegui/website/docs/guides/styling.md +0 -208
  203. package/svelte-nodegui/website/docs/guides/tutorial.md +0 -82
  204. package/svelte-nodegui/website/docs/guides/using-native-node-modules.md +0 -55
  205. package/svelte-nodegui/website/docs/mdx.md +0 -22
  206. package/svelte-nodegui/website/docs/scripts/fixdocs.js +0 -21
  207. package/svelte-nodegui/website/docusaurus.config.js +0 -131
  208. package/svelte-nodegui/website/package.json +0 -33
  209. package/svelte-nodegui/website/sidebars.js +0 -77
  210. package/svelte-nodegui/website/src/components/CodeExample.js +0 -42
  211. package/svelte-nodegui/website/src/components/CreateNativeApps.js +0 -46
  212. package/svelte-nodegui/website/src/components/Features.js +0 -62
  213. package/svelte-nodegui/website/src/components/Hero.js +0 -85
  214. package/svelte-nodegui/website/src/components/SplitView.js +0 -18
  215. package/svelte-nodegui/website/src/components/Talks.js +0 -68
  216. package/svelte-nodegui/website/src/components/Try.js +0 -83
  217. package/svelte-nodegui/website/src/components/common.js +0 -41
  218. package/svelte-nodegui/website/src/components/styles.css +0 -61
  219. package/svelte-nodegui/website/src/css/custom.css +0 -34
  220. package/svelte-nodegui/website/src/pages/index.js +0 -32
  221. package/svelte-nodegui/website/src/pages/styles.module.css +0 -35
  222. package/svelte-nodegui/website/static/CNAME +0 -1
  223. package/svelte-nodegui/website/static/img/box-layout-1.png +0 -0
  224. package/svelte-nodegui/website/static/img/box-layout-2.png +0 -0
  225. package/svelte-nodegui/website/static/img/code-sample.png +0 -0
  226. package/svelte-nodegui/website/static/img/demo.png +0 -0
  227. package/svelte-nodegui/website/static/img/favicon.ico +0 -0
  228. package/svelte-nodegui/website/static/img/flex-layout-1.png +0 -0
  229. package/svelte-nodegui/website/static/img/grid-layout-1.png +0 -0
  230. package/svelte-nodegui/website/static/img/logo-circle.png +0 -0
  231. package/svelte-nodegui/website/static/img/logo.png +0 -0
  232. package/svelte-nodegui/website/static/img/logo.svg +0 -5
  233. package/svelte-nodegui/website/static/img/logox200.png +0 -0
  234. package/svelte-nodegui/website/static/img/undraw_building_websites.svg +0 -1
  235. package/svelte-nodegui/website/static/img/undraw_code_review.svg +0 -1
  236. package/svelte-nodegui/website/static/img/undraw_docusaurus_mountain.svg +0 -170
  237. package/svelte-nodegui/website/static/img/undraw_docusaurus_react.svg +0 -169
  238. package/svelte-nodegui/website/static/img/undraw_docusaurus_tree.svg +0 -1
  239. package/svelte-nodegui/website/static/img/undraw_react.svg +0 -1
  240. package/svelte-nodegui/website/static/img/undraw_website_setup.svg +0 -1
  241. package/svelte-nodegui/website/static/img/undraw_windows.svg +0 -11
  242. package/svelte-nodegui/website/website/sidebars.js +0 -32
  243. package/svelte-nodegui/website/yarn.lock +0 -10533
package/src/fm.js ADDED
@@ -0,0 +1,1130 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import readline from 'readline';
5
+ import { execSync } from 'child_process';
6
+ import os from 'os';
7
+
8
+
9
+ class FileManager {
10
+ constructor() {
11
+ this.currentPath = process.cwd();
12
+ this.selectedIndex = 0;
13
+ this.items = [];
14
+ this.showHidden = false;
15
+ this.sortBy = 'name'; // 'name', 'size', 'date', 'type'
16
+ this.sortOrder = 'asc'; // 'asc', 'desc'
17
+ this.clipboard = [];
18
+ this.clipboardAction = null; // 'copy' or 'cut'
19
+ this.viewMode = 'detailed'; // 'detailed', 'simple', 'grid'
20
+ this.filterText = '';
21
+ this.bookmarks = this.loadBookmarks();
22
+ this.history = [process.cwd()];
23
+ this.historyIndex = 0;
24
+ this.selectedItems = new Set();
25
+ this.multiSelectMode = false;
26
+ this.searchMode = false;
27
+ this.previewMode = false;
28
+ this.splitView = false;
29
+ this.rightPanePath = os.homedir();
30
+ this.activePane = 'left'; // 'left', 'right'
31
+
32
+ // File type icons
33
+ this.fileIcons = {
34
+ // Documents
35
+ '.pdf': '📄', '.doc': '📝', '.docx': '📝', '.txt': '📄', '.md': '📋',
36
+ '.rtf': '📝', '.odt': '📝', '.pages': '📝',
37
+ // Images
38
+ '.jpg': '🖼️', '.jpeg': '🖼️', '.png': '🖼️', '.gif': '🎞️', '.bmp': '🖼️',
39
+ '.svg': '🎨', '.ico': '🖼️', '.tiff': '🖼️', '.webp': '🖼️',
40
+ // Videos
41
+ '.mp4': '🎬', '.avi': '🎬', '.mov': '🎬', '.wmv': '🎬', '.flv': '🎬',
42
+ '.mkv': '🎬', '.webm': '🎬', '.m4v': '🎬',
43
+ // Audio
44
+ '.mp3': '🎵', '.wav': '🎵', '.flac': '🎵', '.aac': '🎵', '.ogg': '🎵',
45
+ '.wma': '🎵', '.m4a': '🎵',
46
+ // Archives
47
+ '.zip': '🗜️', '.rar': '🗜️', '.7z': '🗜️', '.tar': '🗜️', '.gz': '🗜️',
48
+ '.bz2': '🗜️', '.xz': '🗜️',
49
+ // Code
50
+ '.js': '📜', '.html': '🌐', '.css': '🎨', '.json': '📋', '.xml': '📋',
51
+ '.py': '🐍', '.java': '☕', '.cpp': '⚙️', '.c': '⚙️', '.h': '⚙️',
52
+ '.php': '🐘', '.rb': '💎', '.go': '🐹', '.rs': '🦀', '.swift': '🦉',
53
+ '.kt': '🎯', '.ts': '📘', '.jsx': '⚛️', '.tsx': '⚛️', '.vue': '💚',
54
+ // Config
55
+ '.ini': '⚙️', '.conf': '⚙️', '.cfg': '⚙️', '.yaml': '📋', '.yml': '📋',
56
+ '.toml': '📋', '.env': '🔧',
57
+ // Executables
58
+ '.exe': '⚙️', '.msi': '📦', '.deb': '📦', '.rpm': '📦', '.dmg': '💿',
59
+ '.app': '📱', '.AppImage': '📱',
60
+ // Default
61
+ 'default': '📄'
62
+ };
63
+
64
+ // Toolbar icons
65
+ this.toolbarIcons = [
66
+ { icon: '🏠', action: 'home', tooltip: 'Go Home' },
67
+ { icon: '⬆️', action: 'up', tooltip: 'Parent Directory' },
68
+ { icon: '⬅️', action: 'back', tooltip: 'Back' },
69
+ { icon: '➡️', action: 'forward', tooltip: 'Forward' },
70
+ { icon: '🔄', action: 'refresh', tooltip: 'Refresh' },
71
+ { icon: '📋', action: 'copy', tooltip: 'Copy' },
72
+ { icon: '✂️', action: 'cut', tooltip: 'Cut' },
73
+ { icon: '📄', action: 'paste', tooltip: 'Paste' },
74
+ { icon: '🗑️', action: 'delete', tooltip: 'Delete' },
75
+ { icon: '📁', action: 'newdir', tooltip: 'New Folder' },
76
+ { icon: '📄', action: 'newfile', tooltip: 'New File' },
77
+ { icon: '🔍', action: 'search', tooltip: 'Search' },
78
+ { icon: '👁️', action: 'preview', tooltip: 'Preview' },
79
+ { icon: '📊', action: 'view', tooltip: 'View Mode' },
80
+ { icon: '📖', action: 'bookmark', tooltip: 'Bookmarks' },
81
+ { icon: '🔧', action: 'settings', tooltip: 'Settings' }
82
+ ];
83
+
84
+ // Setup readline interface
85
+ this.rl = readline.createInterface({
86
+ input: process.stdin,
87
+ output: process.stdout
88
+ });
89
+
90
+ // Enable mouse support
91
+ process.stdout.write('\x1b[?1000h'); // Enable mouse tracking
92
+ process.stdout.write('\x1b[?1006h'); // Enable SGR mouse mode
93
+
94
+ // Enable raw mode for immediate key detection
95
+ process.stdin.setRawMode(true);
96
+ process.stdin.resume();
97
+ process.stdin.setEncoding('utf8');
98
+
99
+ // Hide cursor
100
+ process.stdout.write('\x1B[?25l');
101
+
102
+ // Handle cleanup on exit
103
+ process.on('SIGINT', () => this.cleanup());
104
+ process.on('SIGTERM', () => this.cleanup());
105
+ process.on('exit', () => this.cleanup());
106
+ }
107
+
108
+ cleanup() {
109
+ // Show cursor
110
+ process.stdout.write('\x1B[?25h');
111
+ // Disable mouse tracking
112
+ process.stdout.write('\x1b[?1000l');
113
+ process.stdout.write('\x1b[?1006l');
114
+ // Clear screen
115
+ console.clear();
116
+ this.saveBookmarks();
117
+ process.exit(0);
118
+ }
119
+
120
+ loadBookmarks() {
121
+ try {
122
+ const bookmarksPath = path.join(os.homedir(), '.fileman-bookmarks.json');
123
+ if (fs.existsSync(bookmarksPath)) {
124
+ return JSON.parse(fs.readFileSync(bookmarksPath, 'utf8'));
125
+ }
126
+ } catch (error) {
127
+ // Ignore errors, return default bookmarks
128
+ }
129
+ return {
130
+ 'Home': os.homedir(),
131
+ 'Documents': path.join(os.homedir(), 'Documents'),
132
+ 'Downloads': path.join(os.homedir(), 'Downloads'),
133
+ 'Desktop': path.join(os.homedir(), 'Desktop')
134
+ };
135
+ }
136
+
137
+ saveBookmarks() {
138
+ try {
139
+ const bookmarksPath = path.join(os.homedir(), '.fileman-bookmarks.json');
140
+ fs.writeFileSync(bookmarksPath, JSON.stringify(this.bookmarks, null, 2));
141
+ } catch (error) {
142
+ // Ignore save errors
143
+ }
144
+ }
145
+
146
+ getFileIcon(item) {
147
+ if (item.isDirectory) {
148
+ if (item.name.startsWith('.')) return '📂';
149
+ return '📁';
150
+ }
151
+
152
+ const ext = path.extname(item.name).toLowerCase();
153
+ return this.fileIcons[ext] || this.fileIcons.default;
154
+ }
155
+
156
+ async loadDirectory(dirPath = this.currentPath) {
157
+ try {
158
+ const files = await fs.promises.readdir(dirPath, { withFileTypes: true });
159
+
160
+ let items = files
161
+ .filter(item => this.showHidden || !item.name.startsWith('.'))
162
+ .filter(item => !this.filterText || item.name.toLowerCase().includes(this.filterText.toLowerCase()))
163
+ .map(item => {
164
+ const fullPath = path.join(dirPath, item.name);
165
+ let stats;
166
+ try {
167
+ stats = fs.statSync(fullPath);
168
+ } catch (e) {
169
+ stats = { size: 0, mtime: new Date(), mode: 0 };
170
+ }
171
+
172
+ return {
173
+ name: item.name,
174
+ isDirectory: item.isDirectory(),
175
+ isFile: item.isFile(),
176
+ isSymbolicLink: item.isSymbolicLink(),
177
+ size: stats.size,
178
+ mtime: stats.mtime,
179
+ mode: stats.mode,
180
+ fullPath,
181
+ permissions: this.getPermissions(stats.mode),
182
+ type: item.isDirectory() ? 'directory' : path.extname(item.name).toLowerCase()
183
+ };
184
+ });
185
+
186
+ // Sort items
187
+ this.sortItems(items);
188
+
189
+ if (dirPath === this.currentPath) {
190
+ this.items = items;
191
+ // Adjust selected index if necessary
192
+ if (this.selectedIndex >= this.items.length) {
193
+ this.selectedIndex = Math.max(0, this.items.length - 1);
194
+ }
195
+ }
196
+
197
+ return items;
198
+ } catch (error) {
199
+ if (dirPath === this.currentPath) {
200
+ this.items = [];
201
+ this.selectedIndex = 0;
202
+ }
203
+ return [];
204
+ }
205
+ }
206
+
207
+ getPermissions(mode) {
208
+ const perms = ['---', '--x', '-w-', '-wx', 'r--', 'r-x', 'rw-', 'rwx'];
209
+ return [
210
+ perms[(mode >> 6) & 7], // Owner
211
+ perms[(mode >> 3) & 7], // Group
212
+ perms[mode & 7] // Others
213
+ ].join('');
214
+ }
215
+
216
+ sortItems(items) {
217
+ items.sort((a, b) => {
218
+ // Directories first
219
+ if (a.isDirectory && !b.isDirectory) return -1;
220
+ if (!a.isDirectory && b.isDirectory) return 1;
221
+
222
+ let result = 0;
223
+ switch (this.sortBy) {
224
+ case 'size':
225
+ result = a.size - b.size;
226
+ break;
227
+ case 'date':
228
+ result = a.mtime - b.mtime;
229
+ break;
230
+ case 'type':
231
+ result = a.type.localeCompare(b.type);
232
+ break;
233
+ default:
234
+ result = a.name.localeCompare(b.name, undefined, { numeric: true });
235
+ }
236
+
237
+ return this.sortOrder === 'desc' ? -result : result;
238
+ });
239
+ }
240
+
241
+ formatSize(bytes) {
242
+ if (bytes === 0) return '0 B';
243
+ const k = 1024;
244
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
245
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
246
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
247
+ }
248
+
249
+ formatDate(date) {
250
+ return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
251
+ }
252
+
253
+ renderToolbar() {
254
+ const terminalWidth = process.stdout.columns;
255
+ let toolbar = '\x1b[44m\x1b[37m'; // Blue background, white text
256
+
257
+ // Render clickable icons
258
+ for (let i = 0; i < this.toolbarIcons.length; i++) {
259
+ const icon = this.toolbarIcons[i];
260
+ toolbar += ` ${icon.icon} `;
261
+ }
262
+
263
+ // Fill remaining space
264
+ const usedSpace = this.toolbarIcons.length * 3;
265
+ const remainingSpace = Math.max(0, terminalWidth - usedSpace);
266
+ toolbar += ' '.repeat(remainingSpace);
267
+
268
+ toolbar += '\x1b[0m'; // Reset colors
269
+ console.log(toolbar);
270
+ }
271
+
272
+ renderStatusBar() {
273
+ const terminalWidth = process.stdout.columns;
274
+ let statusBar = '\x1b[42m\x1b[30m'; // Green background, black text
275
+
276
+ // Left side - current info
277
+ const selectedItem = this.items[this.selectedIndex];
278
+ let leftInfo = '';
279
+
280
+ if (selectedItem) {
281
+ leftInfo = `${selectedItem.name} | ${selectedItem.isDirectory ? '<DIR>' : this.formatSize(selectedItem.size)}`;
282
+ if (this.selectedItems.size > 0) {
283
+ leftInfo += ` | Selected: ${this.selectedItems.size}`;
284
+ }
285
+ }
286
+
287
+ // Right side - stats
288
+ const rightInfo = `${this.items.length} items | Sort: ${this.sortBy} ${this.sortOrder} | View: ${this.viewMode}`;
289
+
290
+ // Calculate spacing
291
+ const totalInfo = leftInfo.length + rightInfo.length;
292
+ const spacing = Math.max(1, terminalWidth - totalInfo);
293
+
294
+ statusBar += leftInfo + ' '.repeat(spacing) + rightInfo;
295
+
296
+ // Trim if too long
297
+ if (statusBar.length > terminalWidth + 10) { // +10 for color codes
298
+ statusBar = statusBar.substring(0, terminalWidth + 10);
299
+ }
300
+
301
+ statusBar += '\x1b[0m'; // Reset colors
302
+ console.log(statusBar);
303
+ }
304
+
305
+ renderFileList() {
306
+ const terminalHeight = process.stdout.rows - 7; // Account for toolbar, header, status
307
+ const terminalWidth = process.stdout.columns;
308
+
309
+ if (this.splitView) {
310
+ this.renderSplitView(terminalHeight, terminalWidth);
311
+ return;
312
+ }
313
+
314
+ if (this.items.length === 0) {
315
+ console.log('\x1b[2m Empty directory\x1b[0m');
316
+ return;
317
+ }
318
+
319
+ // Calculate start index for scrolling
320
+ const startIndex = Math.max(0, this.selectedIndex - Math.floor(terminalHeight / 2));
321
+ const endIndex = Math.min(this.items.length, startIndex + terminalHeight);
322
+
323
+ for (let i = startIndex; i < endIndex; i++) {
324
+ this.renderFileItem(i, terminalWidth);
325
+ }
326
+
327
+ // Show scroll indicator
328
+ if (this.items.length > terminalHeight) {
329
+ const scrollPercent = Math.round((this.selectedIndex / (this.items.length - 1)) * 100);
330
+ console.log(`\x1b[2m[${scrollPercent}%] ${this.selectedIndex + 1}/${this.items.length}\x1b[0m`);
331
+ }
332
+ }
333
+
334
+ renderFileItem(index, terminalWidth) {
335
+ const item = this.items[index];
336
+ const isSelected = index === this.selectedIndex;
337
+ const isMultiSelected = this.selectedItems.has(index);
338
+
339
+ let prefix = ' ';
340
+ let suffix = '\x1b[0m';
341
+
342
+ if (isSelected && isMultiSelected) {
343
+ prefix = '\x1b[43m\x1b[30m> '; // Yellow background for selected + multi-selected
344
+ } else if (isSelected) {
345
+ prefix = '\x1b[47m\x1b[30m> '; // White background for selected
346
+ } else if (isMultiSelected) {
347
+ prefix = '\x1b[46m\x1b[30m* '; // Cyan background for multi-selected
348
+ }
349
+
350
+ const icon = this.getFileIcon(item);
351
+ let nameColor = this.getFileColor(item);
352
+
353
+ if (this.viewMode === 'simple') {
354
+ const displayName = this.truncateName(item.name, terminalWidth - 10);
355
+ console.log(prefix + icon + ' ' + nameColor + displayName + suffix);
356
+ } else if (this.viewMode === 'detailed') {
357
+ const sizeStr = item.isDirectory ? '<DIR>' : this.formatSize(item.size);
358
+ const dateStr = this.formatDate(item.mtime);
359
+ const permStr = item.permissions;
360
+
361
+ const maxNameLength = Math.max(20, terminalWidth - 45);
362
+ const displayName = this.truncateName(item.name, maxNameLength);
363
+
364
+ console.log(prefix + icon + ' ' + nameColor + displayName.padEnd(maxNameLength) +
365
+ '\x1b[0m ' + permStr + ' ' + sizeStr.padStart(8) + ' ' + dateStr + suffix);
366
+ } else if (this.viewMode === 'grid') {
367
+ // Grid view implementation would go here
368
+ const displayName = this.truncateName(item.name, 15);
369
+ process.stdout.write(prefix + icon + ' ' + nameColor + displayName.padEnd(15) + suffix + ' ');
370
+ if ((index - (Math.max(0, this.selectedIndex - Math.floor((process.stdout.rows - 7) / 2)))) % 4 === 3) {
371
+ console.log('');
372
+ }
373
+ }
374
+ }
375
+
376
+ renderSplitView(terminalHeight, terminalWidth) {
377
+ const halfWidth = Math.floor(terminalWidth / 2) - 1;
378
+
379
+ // Render left pane (current directory)
380
+ console.log('\x1b[1m\x1b[34mLeft Pane: ' + this.currentPath + '\x1b[0m');
381
+ // Left pane file list would go here
382
+
383
+ // Render right pane
384
+ console.log('\x1b[1m\x1b[35mRight Pane: ' + this.rightPanePath + '\x1b[0m');
385
+ // Right pane file list would go here
386
+ }
387
+
388
+ getFileColor(item) {
389
+ if (item.isSymbolicLink) return '\x1b[36m'; // Cyan for symlinks
390
+ if (item.isDirectory) return '\x1b[34m'; // Blue for directories
391
+ if (item.name.startsWith('.')) return '\x1b[2m'; // Dim for hidden files
392
+ if (item.mode & 0o111) return '\x1b[32m'; // Green for executables
393
+
394
+ // Color by file type
395
+ const ext = path.extname(item.name).toLowerCase();
396
+ if (['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.svg'].includes(ext)) return '\x1b[35m'; // Magenta for images
397
+ if (['.mp3', '.wav', '.flac', '.aac', '.ogg'].includes(ext)) return '\x1b[33m'; // Yellow for audio
398
+ if (['.mp4', '.avi', '.mov', '.wmv', '.mkv'].includes(ext)) return '\x1b[31m'; // Red for video
399
+ if (['.zip', '.rar', '.7z', '.tar', '.gz'].includes(ext)) return '\x1b[91m'; // Bright red for archives
400
+
401
+ return '\x1b[37m'; // White for regular files
402
+ }
403
+
404
+ truncateName(name, maxLength) {
405
+ if (name.length <= maxLength) return name;
406
+ return name.substring(0, maxLength - 3) + '...';
407
+ }
408
+
409
+ render() {
410
+ console.clear();
411
+
412
+ const terminalWidth = process.stdout.columns;
413
+
414
+ // Header with path
415
+ console.log('\x1b[1m\x1b[34m' + '='.repeat(terminalWidth) + '\x1b[0m');
416
+ let pathDisplay = this.currentPath;
417
+ if (pathDisplay.length > terminalWidth - 20) {
418
+ pathDisplay = '...' + pathDisplay.substring(pathDisplay.length - (terminalWidth - 23));
419
+ }
420
+ console.log('\x1b[1m\x1b[36mFile Manager\x1b[0m - \x1b[33m' + pathDisplay + '\x1b[0m');
421
+
422
+ // Render toolbar
423
+ this.renderToolbar();
424
+
425
+ // Filter/Search bar
426
+ if (this.searchMode || this.filterText) {
427
+ console.log('\x1b[43m\x1b[30mSearch: ' + this.filterText + '_\x1b[0m');
428
+ }
429
+
430
+ console.log('\x1b[1m\x1b[34m' + '='.repeat(terminalWidth) + '\x1b[0m');
431
+
432
+ // File list
433
+ this.renderFileList();
434
+
435
+ // Preview pane
436
+ if (this.previewMode && this.items.length > 0) {
437
+ this.renderPreview();
438
+ }
439
+
440
+ // Status bar
441
+ this.renderStatusBar();
442
+
443
+ // Show clipboard status
444
+ if (this.clipboard.length > 0) {
445
+ const action = this.clipboardAction === 'copy' ? 'Copied' : 'Cut';
446
+ const items = this.clipboard.length === 1 ?
447
+ path.basename(this.clipboard[0]) :
448
+ `${this.clipboard.length} items`;
449
+ console.log(`\x1b[33m${action}: ${items}\x1b[0m`);
450
+ }
451
+ }
452
+
453
+ renderPreview() {
454
+ const selectedItem = this.items[this.selectedIndex];
455
+ if (!selectedItem) return;
456
+
457
+ console.log('\x1b[1m\x1b[36m--- Preview ---\x1b[0m');
458
+
459
+ if (selectedItem.isDirectory) {
460
+ // Show directory contents
461
+ this.loadDirectory(selectedItem.fullPath).then(items => {
462
+ console.log(`\x1b[2mDirectory contains ${items.length} items\x1b[0m`);
463
+ items.slice(0, 5).forEach(item => {
464
+ console.log(`\x1b[2m ${this.getFileIcon(item)} ${item.name}\x1b[0m`);
465
+ });
466
+ if (items.length > 5) {
467
+ console.log(`\x1b[2m ... and ${items.length - 5} more\x1b[0m`);
468
+ }
469
+ });
470
+ } else {
471
+ // Show file info and content preview
472
+ console.log(`\x1b[2mSize: ${this.formatSize(selectedItem.size)}\x1b[0m`);
473
+ console.log(`\x1b[2mModified: ${this.formatDate(selectedItem.mtime)}\x1b[0m`);
474
+ console.log(`\x1b[2mPermissions: ${selectedItem.permissions}\x1b[0m`);
475
+
476
+ // Try to show file content preview for text files
477
+ const ext = path.extname(selectedItem.name).toLowerCase();
478
+ if (['.txt', '.md', '.js', '.json', '.html', '.css', '.py', '.java'].includes(ext)) {
479
+ try {
480
+ const content = fs.readFileSync(selectedItem.fullPath, 'utf8');
481
+ const lines = content.split('\n').slice(0, 3);
482
+ console.log('\x1b[2mContent preview:\x1b[0m');
483
+ lines.forEach(line => {
484
+ console.log(`\x1b[2m ${line.substring(0, 50)}${line.length > 50 ? '...' : ''}\x1b[0m`);
485
+ });
486
+ } catch (error) {
487
+ console.log('\x1b[2mCannot preview file content\x1b[0m');
488
+ }
489
+ }
490
+ }
491
+ }
492
+
493
+ async handleKeyPress(key) {
494
+ // Handle mouse input
495
+ if (key.startsWith('\x1b[M') || key.startsWith('\x1b[<')) {
496
+ this.handleMouseInput(key);
497
+ return;
498
+ }
499
+
500
+ // Handle search mode
501
+ if (this.searchMode) {
502
+ if (key === '\x1b' || key === '\r' || key === '\n') { // Escape or Enter
503
+ this.searchMode = false;
504
+ if (key === '\r' || key === '\n') {
505
+ await this.loadDirectory();
506
+ }
507
+ } else if (key === '\x7f' || key === '\b') { // Backspace
508
+ this.filterText = this.filterText.slice(0, -1);
509
+ await this.loadDirectory();
510
+ } else if (key.length === 1 && key >= ' ') {
511
+ this.filterText += key;
512
+ await this.loadDirectory();
513
+ }
514
+ return;
515
+ }
516
+
517
+ switch (key) {
518
+ case '\u001b[A': // Up arrow
519
+ case 'k':
520
+ this.moveSelection(-1);
521
+ break;
522
+
523
+ case '\u001b[B': // Down arrow
524
+ case 'j':
525
+ this.moveSelection(1);
526
+ break;
527
+
528
+ case '\r': // Enter
529
+ case '\n':
530
+ await this.openSelected();
531
+ break;
532
+
533
+ case '\u001b[D': // Left arrow
534
+ case 'h':
535
+ await this.goUp();
536
+ break;
537
+
538
+ case '\u001b[C': // Right arrow
539
+ case 'l':
540
+ if (this.items.length > 0 && this.items[this.selectedIndex].isDirectory) {
541
+ await this.openSelected();
542
+ }
543
+ break;
544
+
545
+ case ' ': // Space for multi-select
546
+ this.toggleMultiSelect();
547
+ break;
548
+
549
+ case 'a': // Select all
550
+ this.selectAll();
551
+ break;
552
+
553
+ case 'A': // Deselect all
554
+ this.selectedItems.clear();
555
+ break;
556
+
557
+ case 's': // Sort
558
+ this.cycleSortMode();
559
+ await this.loadDirectory();
560
+ break;
561
+
562
+ case 'S': // Sort order
563
+ this.sortOrder = this.sortOrder === 'asc' ? 'desc' : 'asc';
564
+ await this.loadDirectory();
565
+ break;
566
+
567
+ case 'v': // View mode
568
+ this.cycleViewMode();
569
+ break;
570
+
571
+ case 'H': // Toggle hidden files
572
+ this.showHidden = !this.showHidden;
573
+ await this.loadDirectory();
574
+ break;
575
+
576
+ case 'c': // Copy
577
+ this.copySelected();
578
+ break;
579
+
580
+ case 'x': // Cut
581
+ this.cutSelected();
582
+ break;
583
+
584
+ case 'p': // Paste
585
+ await this.paste();
586
+ break;
587
+
588
+ case 'd': // Delete
589
+ await this.deleteSelected();
590
+ break;
591
+
592
+ case 'n': // New file
593
+ await this.createNew();
594
+ break;
595
+
596
+ case 'N': // New directory
597
+ await this.createNew('directory');
598
+ break;
599
+
600
+ case 'r': // Rename
601
+ await this.renameSelected();
602
+ break;
603
+
604
+ case 'f': // Search
605
+ case '/':
606
+ this.searchMode = true;
607
+ this.filterText = '';
608
+ break;
609
+
610
+ case 'g': // Go to top
611
+ this.selectedIndex = 0;
612
+ break;
613
+
614
+ case 'G': // Go to bottom
615
+ this.selectedIndex = Math.max(0, this.items.length - 1);
616
+ break;
617
+
618
+ case 'b': // Bookmarks
619
+ await this.showBookmarks();
620
+ break;
621
+
622
+ case 'B': // Add bookmark
623
+ await this.addBookmark();
624
+ break;
625
+
626
+ case 'P': // Toggle preview
627
+ this.previewMode = !this.previewMode;
628
+ break;
629
+
630
+ case 'T': // Toggle split view
631
+ this.splitView = !this.splitView;
632
+ break;
633
+
634
+ case 'R': // Refresh
635
+ await this.loadDirectory();
636
+ break;
637
+
638
+ case 'u': // Go back in history
639
+ this.goBack();
640
+ break;
641
+
642
+ case 'U': // Go forward in history
643
+ this.goForward();
644
+ break;
645
+
646
+ case 'q': // Quit
647
+ case '\u0003': // Ctrl+C
648
+ this.cleanup();
649
+ break;
650
+
651
+ case '?': // Help
652
+ await this.showHelp();
653
+ break;
654
+ }
655
+ }
656
+
657
+ handleMouseInput(sequence) {
658
+ // Parse mouse input for toolbar clicks
659
+ // This is a simplified implementation
660
+ if (sequence.includes('0;')) {
661
+ // Mouse click on first row (toolbar)
662
+ const clickX = parseInt(sequence.match(/;(\d+)/)?.[1] || '0');
663
+ const iconIndex = Math.floor(clickX / 3);
664
+
665
+ if (iconIndex >= 0 && iconIndex < this.toolbarIcons.length) {
666
+ this.executeToolbarAction(this.toolbarIcons[iconIndex].action);
667
+ }
668
+ }
669
+ }
670
+
671
+ async executeToolbarAction(action) {
672
+ switch (action) {
673
+ case 'home':
674
+ this.currentPath = os.homedir();
675
+ this.selectedIndex = 0;
676
+ await this.loadDirectory();
677
+ break;
678
+ case 'up':
679
+ await this.goUp();
680
+ break;
681
+ case 'back':
682
+ this.goBack();
683
+ break;
684
+ case 'forward':
685
+ this.goForward();
686
+ break;
687
+ case 'refresh':
688
+ await this.loadDirectory();
689
+ break;
690
+ case 'copy':
691
+ this.copySelected();
692
+ break;
693
+ case 'cut':
694
+ this.cutSelected();
695
+ break;
696
+ case 'paste':
697
+ await this.paste();
698
+ break;
699
+ case 'delete':
700
+ await this.deleteSelected();
701
+ break;
702
+ case 'newdir':
703
+ await this.createNew('directory');
704
+ break;
705
+ case 'newfile':
706
+ await this.createNew('file');
707
+ break;
708
+ case 'search':
709
+ this.searchMode = true;
710
+ this.filterText = '';
711
+ break;
712
+ case 'preview':
713
+ this.previewMode = !this.previewMode;
714
+ break;
715
+ case 'view':
716
+ this.cycleViewMode();
717
+ break;
718
+ case 'bookmark':
719
+ await this.showBookmarks();
720
+ break;
721
+ case 'settings':
722
+ await this.showSettings();
723
+ break;
724
+ }
725
+ }
726
+
727
+ moveSelection(direction) {
728
+ const newIndex = this.selectedIndex + direction;
729
+ if (newIndex >= 0 && newIndex < this.items.length) {
730
+ this.selectedIndex = newIndex;
731
+ }
732
+ }
733
+
734
+ toggleMultiSelect() {
735
+ if (this.selectedItems.has(this.selectedIndex)) {
736
+ this.selectedItems.delete(this.selectedIndex);
737
+ } else {
738
+ this.selectedItems.add(this.selectedIndex);
739
+ }
740
+ this.multiSelectMode = this.selectedItems.size > 0;
741
+ }
742
+
743
+ selectAll() {
744
+ for (let i = 0; i < this.items.length; i++) {
745
+ this.selectedItems.add(i);
746
+ }
747
+ this.multiSelectMode = true;
748
+ }
749
+
750
+ cycleSortMode() {
751
+ const modes = ['name', 'size', 'date', 'type'];
752
+ const currentIndex = modes.indexOf(this.sortBy);
753
+ this.sortBy = modes[(currentIndex + 1) % modes.length];
754
+ }
755
+
756
+ cycleViewMode() {
757
+ const modes = ['detailed', 'simple', 'grid'];
758
+ const currentIndex = modes.indexOf(this.viewMode);
759
+ this.viewMode = modes[(currentIndex + 1) % modes.length];
760
+ }
761
+
762
+ async openSelected() {
763
+ if (this.items.length === 0) return;
764
+
765
+ const selected = this.items[this.selectedIndex];
766
+
767
+ if (selected.isDirectory) {
768
+ this.addToHistory(this.currentPath);
769
+ this.currentPath = selected.fullPath;
770
+ this.selectedIndex = 0;
771
+ this.selectedItems.clear();
772
+ await this.loadDirectory();
773
+ } else {
774
+ // Try to open file with system default application
775
+ try {
776
+ const command = process.platform === 'win32' ? 'start' :
777
+ process.platform === 'darwin' ? 'open' : 'xdg-open';
778
+ execSync(`${command} "${selected.fullPath}"`, { stdio: 'ignore' });
779
+ } catch (error) {
780
+ console.log(`\x1b[31mCannot open file: ${selected.name}\x1b[0m`);
781
+ await this.waitForKey();
782
+ }
783
+ }
784
+ }
785
+
786
+ async goUp() {
787
+ const parentPath = path.dirname(this.currentPath);
788
+ if (parentPath !== this.currentPath) {
789
+ this.addToHistory(this.currentPath);
790
+ this.currentPath = parentPath;
791
+ this.selectedIndex = 0;
792
+ this.selectedItems.clear();
793
+ await this.loadDirectory();
794
+ }
795
+ }
796
+
797
+ addToHistory(path) {
798
+ // Remove forward history when adding new path
799
+ this.history = this.history.slice(0, this.historyIndex + 1);
800
+ this.history.push(path);
801
+ this.historyIndex = this.history.length - 1;
802
+
803
+ // Limit history size
804
+ if (this.history.length > 50) {
805
+ this.history.shift();
806
+ this.historyIndex--;
807
+ }
808
+ }
809
+
810
+ goBack() {
811
+ if (this.historyIndex > 0) {
812
+ this.historyIndex--;
813
+ this.currentPath = this.history[this.historyIndex];
814
+ this.selectedIndex = 0;
815
+ this.selectedItems.clear();
816
+ this.loadDirectory();
817
+ }
818
+ }
819
+
820
+ goForward() {
821
+ if (this.historyIndex < this.history.length - 1) {
822
+ this.historyIndex++;
823
+ this.currentPath = this.history[this.historyIndex];
824
+ this.selectedIndex = 0;
825
+ this.selectedItems.clear();
826
+ this.loadDirectory();
827
+ }
828
+ }
829
+
830
+ copySelected() {
831
+ if (this.selectedItems.size > 0) {
832
+ this.clipboard = Array.from(this.selectedItems).map(i => this.items[i].fullPath);
833
+ } else if (this.items.length > 0) {
834
+ this.clipboard = [this.items[this.selectedIndex].fullPath];
835
+ }
836
+ this.clipboardAction = 'copy';
837
+ }
838
+
839
+ cutSelected() {
840
+ if (this.selectedItems.size > 0) {
841
+ this.clipboard = Array.from(this.selectedItems).map(i => this.items[i].fullPath);
842
+ } else if (this.items.length > 0) {
843
+ this.clipboard = [this.items[this.selectedIndex].fullPath];
844
+ }
845
+ this.clipboardAction = 'cut';
846
+ }
847
+
848
+ async paste() {
849
+ if (this.clipboard.length === 0) return;
850
+
851
+ try {
852
+ for (const sourcePath of this.clipboard) {
853
+ const fileName = path.basename(sourcePath);
854
+ const destPath = path.join(this.currentPath, fileName);
855
+
856
+ if (this.clipboardAction === 'copy') {
857
+ await this.copyFile(sourcePath, destPath);
858
+ } else {
859
+ await fs.promises.rename(sourcePath, destPath);
860
+ }
861
+ }
862
+
863
+ if (this.clipboardAction === 'cut') {
864
+ this.clipboard = [];
865
+ this.clipboardAction = null;
866
+ }
867
+
868
+ this.selectedItems.clear();
869
+ await this.loadDirectory();
870
+ } catch (error) {
871
+ console.log(`\x1b[31mPaste failed: ${error.message}\x1b[0m`);
872
+ await this.waitForKey();
873
+ }
874
+ }
875
+
876
+ async copyFile(src, dest) {
877
+ const stat = await fs.promises.stat(src);
878
+
879
+ if (stat.isDirectory()) {
880
+ await fs.promises.mkdir(dest, { recursive: true });
881
+ const files = await fs.promises.readdir(src);
882
+
883
+ for (const file of files) {
884
+ await this.copyFile(path.join(src, file), path.join(dest, file));
885
+ }
886
+ } else {
887
+ await fs.promises.copyFile(src, dest);
888
+ }
889
+ }
890
+
891
+ async deleteSelected() {
892
+ const itemsToDelete = this.selectedItems.size > 0 ?
893
+ Array.from(this.selectedItems).map(i => this.items[i]) :
894
+ this.items.length > 0 ? [this.items[this.selectedIndex]] : [];
895
+
896
+ if (itemsToDelete.length === 0) return;
897
+
898
+ const names = itemsToDelete.map(item => item.name).join(', ');
899
+ console.log(`\x1b[31mDelete ${itemsToDelete.length} item(s): ${names}? (y/N)\x1b[0m`);
900
+
901
+ const confirmation = await this.getInput();
902
+ if (confirmation.toLowerCase() === 'y') {
903
+ try {
904
+ for (const item of itemsToDelete) {
905
+ if (item.isDirectory) {
906
+ await fs.promises.rmdir(item.fullPath, { recursive: true });
907
+ } else {
908
+ await fs.promises.unlink(item.fullPath);
909
+ }
910
+ }
911
+
912
+ this.selectedItems.clear();
913
+ if (this.selectedIndex >= this.items.length - itemsToDelete.length) {
914
+ this.selectedIndex = Math.max(0, this.selectedIndex - itemsToDelete.length);
915
+ }
916
+
917
+ await this.loadDirectory();
918
+ } catch (error) {
919
+ console.log(`\x1b[31mDelete failed: ${error.message}\x1b[0m`);
920
+ await this.waitForKey();
921
+ }
922
+ }
923
+ }
924
+
925
+ async renameSelected() {
926
+ if (this.items.length === 0) return;
927
+
928
+ const selected = this.items[this.selectedIndex];
929
+ console.log(`\x1b[33mRename "${selected.name}" to:\x1b[0m`);
930
+
931
+ const newName = await this.getInput();
932
+ if (newName.trim() && newName.trim() !== selected.name) {
933
+ try {
934
+ const newPath = path.join(this.currentPath, newName.trim());
935
+ await fs.promises.rename(selected.fullPath, newPath);
936
+ await this.loadDirectory();
937
+ } catch (error) {
938
+ console.log(`\x1b[31mRename failed: ${error.message}\x1b[0m`);
939
+ await this.waitForKey();
940
+ }
941
+ }
942
+ }
943
+
944
+ async createNew(type = null) {
945
+ if (!type) {
946
+ console.log('\x1b[33mCreate (f)ile or (d)irectory? (f/d)\x1b[0m');
947
+ const typeInput = await this.getInput();
948
+ type = typeInput.toLowerCase() === 'd' ? 'directory' : 'file';
949
+ }
950
+
951
+ const itemType = type === 'directory' ? 'directory' : 'file';
952
+ console.log(`\x1b[33mEnter ${itemType} name:\x1b[0m`);
953
+ const name = await this.getInput();
954
+
955
+ if (name.trim()) {
956
+ try {
957
+ const fullPath = path.join(this.currentPath, name.trim());
958
+
959
+ if (type === 'directory') {
960
+ await fs.promises.mkdir(fullPath);
961
+ } else {
962
+ await fs.promises.writeFile(fullPath, '');
963
+ }
964
+
965
+ await this.loadDirectory();
966
+ } catch (error) {
967
+ console.log(`\x1b[31mCreate failed: ${error.message}\x1b[0m`);
968
+ await this.waitForKey();
969
+ }
970
+ }
971
+ }
972
+
973
+ async showBookmarks() {
974
+ console.clear();
975
+ console.log('\x1b[1m\x1b[36m=== Bookmarks ===\x1b[0m');
976
+
977
+ const bookmarkNames = Object.keys(this.bookmarks);
978
+ for (let i = 0; i < bookmarkNames.length; i++) {
979
+ const name = bookmarkNames[i];
980
+ const path = this.bookmarks[name];
981
+ console.log(`${i + 1}. ${name} -> ${path}`);
982
+ }
983
+
984
+ console.log('\nEnter bookmark number to navigate, or press Enter to cancel:');
985
+ const input = await this.getInput();
986
+ const num = parseInt(input);
987
+
988
+ if (num > 0 && num <= bookmarkNames.length) {
989
+ const bookmarkName = bookmarkNames[num - 1];
990
+ const bookmarkPath = this.bookmarks[bookmarkName];
991
+
992
+ if (fs.existsSync(bookmarkPath)) {
993
+ this.addToHistory(this.currentPath);
994
+ this.currentPath = bookmarkPath;
995
+ this.selectedIndex = 0;
996
+ this.selectedItems.clear();
997
+ await this.loadDirectory();
998
+ } else {
999
+ console.log(`\x1b[31mBookmark path does not exist: ${bookmarkPath}\x1b[0m`);
1000
+ await this.waitForKey();
1001
+ }
1002
+ }
1003
+ }
1004
+
1005
+ async addBookmark() {
1006
+ console.log(`\x1b[33mAdd bookmark for "${this.currentPath}".\nEnter bookmark name:\x1b[0m`);
1007
+ const name = await this.getInput();
1008
+
1009
+ if (name.trim()) {
1010
+ this.bookmarks[name.trim()] = this.currentPath;
1011
+ this.saveBookmarks();
1012
+ console.log(`\x1b[32mBookmark "${name.trim()}" added!\x1b[0m`);
1013
+ await this.waitForKey();
1014
+ }
1015
+ }
1016
+
1017
+ async showSettings() {
1018
+ console.clear();
1019
+ console.log('\x1b[1m\x1b[36m=== Settings ===\x1b[0m');
1020
+ console.log(`1. Show hidden files: ${this.showHidden ? 'ON' : 'OFF'}`);
1021
+ console.log(`2. Sort by: ${this.sortBy}`);
1022
+ console.log(`3. Sort order: ${this.sortOrder}`);
1023
+ console.log(`4. View mode: ${this.viewMode}`);
1024
+ console.log(`5. Preview mode: ${this.previewMode ? 'ON' : 'OFF'}`);
1025
+ console.log(`6. Split view: ${this.splitView ? 'ON' : 'OFF'}`);
1026
+ console.log('\nEnter setting number to toggle, or press Enter to cancel:');
1027
+
1028
+ const input = await this.getInput();
1029
+ const num = parseInt(input);
1030
+
1031
+ switch (num) {
1032
+ case 1:
1033
+ this.showHidden = !this.showHidden;
1034
+ await this.loadDirectory();
1035
+ break;
1036
+ case 2:
1037
+ this.cycleSortMode();
1038
+ await this.loadDirectory();
1039
+ break;
1040
+ case 3:
1041
+ this.sortOrder = this.sortOrder === 'asc' ? 'desc' : 'asc';
1042
+ await this.loadDirectory();
1043
+ break;
1044
+ case 4:
1045
+ this.cycleViewMode();
1046
+ break;
1047
+ case 5:
1048
+ this.previewMode = !this.previewMode;
1049
+ break;
1050
+ case 6:
1051
+ this.splitView = !this.splitView;
1052
+ break;
1053
+ }
1054
+ }
1055
+
1056
+ async showHelp() {
1057
+ console.clear();
1058
+ console.log('\x1b[1m\x1b[36m=== File Manager Help ===\x1b[0m');
1059
+ console.log('\x1b[1mNavigation:\x1b[0m');
1060
+ console.log(' ↑/↓, j/k - Move up/down');
1061
+ console.log(' ←/→, h/l - Parent dir/Enter dir');
1062
+ console.log(' Enter - Open file/directory');
1063
+ console.log(' g/G - Go to top/bottom');
1064
+ console.log(' u/U - Back/Forward in history');
1065
+ console.log('');
1066
+ console.log('\x1b[1mFile Operations:\x1b[0m');
1067
+ console.log(' Space - Multi-select toggle');
1068
+ console.log(' a/A - Select all/Deselect all');
1069
+ console.log(' c/x/p - Copy/Cut/Paste');
1070
+ console.log(' d - Delete');
1071
+ console.log(' r - Rename');
1072
+ console.log(' n/N - New file/New directory');
1073
+ console.log('');
1074
+ console.log('\x1b[1mView & Search:\x1b[0m');
1075
+ console.log(' v - Cycle view mode');
1076
+ console.log(' s/S - Sort by/Sort order');
1077
+ console.log(' H - Toggle hidden files');
1078
+ console.log(' f,/ - Search/Filter');
1079
+ console.log(' P - Toggle preview');
1080
+ console.log(' T - Toggle split view');
1081
+ console.log(' R - Refresh');
1082
+ console.log('');
1083
+ console.log('\x1b[1mBookmarks & Other:\x1b[0m');
1084
+ console.log(' b/B - Show bookmarks/Add bookmark');
1085
+ console.log(' ? - Show this help');
1086
+ console.log(' q - Quit');
1087
+ console.log('');
1088
+ console.log('\x1b[1mToolbar:\x1b[0m');
1089
+ console.log(' Click on toolbar icons for quick actions');
1090
+ console.log('');
1091
+ console.log('Press any key to continue...');
1092
+ await this.waitForKey();
1093
+ }
1094
+
1095
+ async getInput() {
1096
+ // Show cursor temporarily
1097
+ process.stdout.write('\x1B[?25h');
1098
+
1099
+ return new Promise((resolve) => {
1100
+ this.rl.question('', (answer) => {
1101
+ // Hide cursor again
1102
+ process.stdout.write('\x1B[?25l');
1103
+ resolve(answer);
1104
+ });
1105
+ });
1106
+ }
1107
+
1108
+ async waitForKey() {
1109
+ return new Promise((resolve) => {
1110
+ const handler = () => {
1111
+ process.stdin.removeListener('data', handler);
1112
+ resolve();
1113
+ };
1114
+ process.stdin.once('data', handler);
1115
+ });
1116
+ }
1117
+
1118
+ async run() {
1119
+ await this.loadDirectory();
1120
+ this.render();
1121
+
1122
+ process.stdin.on('data', async (key) => {
1123
+ await this.handleKeyPress(key);
1124
+ this.render();
1125
+ });
1126
+ }
1127
+ }
1128
+
1129
+ var fm = new FileManager()
1130
+ fm.run()