├── .github
└── FUNDING.yml
├── .gitignore
├── .notes
├── README.md.md
└── maskfile.md.md
├── .startproject
├── CHANGELOG.md
├── LICENSE
├── Pictures
├── Buffy-DP-MFM-500.jpg
├── Buffy-DP-MFM.jpg
├── DraculaPro-MFM.jpg
├── ModalFileManager.jpeg
├── Pref-Extensions.png
├── Pref-General1.png
├── Pref-General2.png
├── Pref-Theme1.png
├── Pref-Theme2.png
├── Pref-theme3.png
├── SecurityPrivacy1.png
├── Van-HelsingDP-MFM.jpg
├── VerifyDeveloper.png
└── VerifyDeveloper2.png
├── README.md
├── icons
├── mfm-icon.afdesign
├── mfm-icon.icns
├── mfm-icon.png
└── mfm-icon.svg
├── maskfile.md
├── package-lock.json
├── package.json
├── public
├── bundle.bin
├── bundle.css
├── bundle.js
├── global.css
├── index-dev.html
├── index-normal.html
├── index.html
└── package.json
├── rollup.config.js
└── src
├── Start.svelte
├── components
├── CommandPrompt.svelte
├── DirectoryListing.svelte
├── Entry.svelte
├── Env.svelte
├── EnvTableRow.svelte
├── ExtensionPrefs.svelte
├── ExtraPanel.svelte
├── FileManager.svelte
├── GeneralPrefs.svelte
├── GitHub.svelte
├── MessageBox.svelte
├── Pane.svelte
├── Preferences.svelte
├── QuickSearch.svelte
├── ResizeBorder.svelte
├── StatusLine.svelte
├── ThemeItem.svelte
└── ThemePrefs.svelte
├── main.js
├── modules
├── commands.js
├── extensions.js
├── filesystems.js
├── linux.js
├── macOS.js
├── util.js
└── windows.js
└── stores
├── altKey.js
├── config.js
├── ctrlKey.js
├── currentCursor.js
├── currentLeftFile.js
├── currentRightFile.js
├── dirHistory.js
├── directoryListeners.js
├── extraPanel.js
├── inputState.js
├── keyProcess.js
├── leftDir.js
├── metaKey.js
├── processKey.js
├── rightDir.js
├── shiftKey.js
├── skipKey.js
├── stateMapColors.js
└── theme.js
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [ 'raguay' ]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # large files ignore
2 | mfm.app
3 | mfm-mac.zip
4 | mac
5 |
6 | # Logs
7 | logs
8 | *.log
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 | lerna-debug.log*
13 | *.map
14 |
15 | # Diagnostic reports (https://nodejs.org/api/report.html)
16 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
17 |
18 | # Runtime data
19 | pids
20 | *.pid
21 | *.seed
22 | *.pid.lock
23 |
24 | # Directory for instrumented libs generated by jscoverage/JSCover
25 | lib-cov
26 |
27 | # Coverage directory used by tools like istanbul
28 | coverage
29 | *.lcov
30 |
31 | # nyc test coverage
32 | .nyc_output
33 |
34 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
35 | .grunt
36 |
37 | # Bower dependency directory (https://bower.io/)
38 | bower_components
39 |
40 | # node-waf configuration
41 | .lock-wscript
42 |
43 | # Compiled binary addons (https://nodejs.org/api/addons.html)
44 | build/Release
45 |
46 | # Dependency directories
47 | node_modules/
48 | jspm_packages/
49 |
50 | # TypeScript v1 declaration files
51 | typings/
52 |
53 | # TypeScript cache
54 | *.tsbuildinfo
55 |
56 | # Optional npm cache directory
57 | .npm
58 |
59 | # Optional eslint cache
60 | .eslintcache
61 |
62 | # Microbundle cache
63 | .rpt2_cache/
64 | .rts2_cache_cjs/
65 | .rts2_cache_es/
66 | .rts2_cache_umd/
67 |
68 | # Optional REPL history
69 | .node_repl_history
70 |
71 | # Output of 'npm pack'
72 | *.tgz
73 |
74 | # Yarn Integrity file
75 | .yarn-integrity
76 |
77 | # dotenv environment variables file
78 | .env
79 | .env.test
80 |
81 | # parcel-bundler cache (https://parceljs.org/)
82 | .cache
83 |
84 | # Next.js build output
85 | .next
86 |
87 | # Nuxt.js build / generate output
88 | .nuxt
89 | dist
90 |
91 | # Gatsby files
92 | .cache/
93 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
94 | # https://nextjs.org/blog/next-9-1#public-directory-support
95 | # public
96 |
97 | # vuepress build output
98 | .vuepress/dist
99 |
100 | # Serverless directories
101 | .serverless/
102 |
103 | # FuseBox cache
104 | .fusebox/
105 |
106 | # DynamoDB Local files
107 | .dynamodb/
108 |
109 | # TernJS port file
110 | .tern-port
111 |
--------------------------------------------------------------------------------
/.notes/README.md.md:
--------------------------------------------------------------------------------
1 | # README.md
2 |
3 | This is the readme file that GitHub displays on their site.
4 |
5 |
--------------------------------------------------------------------------------
/.notes/maskfile.md.md:
--------------------------------------------------------------------------------
1 | # Maskfile for Modal File Manager
2 |
3 | This file contains all the scripts for building and running the Modal File Manager.
4 |
--------------------------------------------------------------------------------
/.startproject:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | cd "$1"
4 | oni2 .
5 |
6 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 |
4 | ## 1.0.0 (2021-04-29)
5 |
6 | ### Added
7 |
8 | - ✨ MessageBox has Picker [[fe849db](https://github.com/raguay/ModalFileManager/commit/fe849dbedebc6ad7041fb7a1ed85503df41fd939)]
9 | - ✨ Added the '.' repeat last command and bug fixes [[8e3f10b](https://github.com/raguay/ModalFileManager/commit/8e3f10be554d12484ab61abd0581200138fb57d7)]
10 |
11 | ### Changed
12 |
13 | - 🎨 Removed redundant code. [[c025eb8](https://github.com/raguay/ModalFileManager/commit/c025eb8ace917053dfbe1b4a06757e1fb54ef0bc)]
14 | - 🎨 Major restructuring of LowLevel to High Level [[e06b4bd](https://github.com/raguay/ModalFileManager/commit/e06b4bd3d2f6e7bdcbdd0ebb4fa61d1731d2ad3a)]
15 | - 🎨 Moving FileSystem dependent stuff lower [[a0e7fc3](https://github.com/raguay/ModalFileManager/commit/a0e7fc324eecf9fd3878978e3e6ec15175666a15)]
16 | - 💄 Added Font Styling option [[a8ce863](https://github.com/raguay/ModalFileManager/commit/a8ce8630f97b82d296ada9c68ab28227124cd411)]
17 |
18 | ### Fixed
19 |
20 | - 🐛 Fixed Various Bugs [[b77bcc4](https://github.com/raguay/ModalFileManager/commit/b77bcc4252b1100f417f6245715630f84e414836)]
21 | - 🐛 StatusLine overflows fixed [[e67c365](https://github.com/raguay/ModalFileManager/commit/e67c365bebe55a01cb912db0ba76ef64d6e3bcf2)]
22 | - 🐛 StatusLine name and dateline fixed. [[44dd2e2](https://github.com/raguay/ModalFileManager/commit/44dd2e2c8861a0dfc819d8ada4489a974e14caae)]
23 | - 🐛 Critical bug with MessageBox [[2d0dd5f](https://github.com/raguay/ModalFileManager/commit/2d0dd5f8384cafa3ca106232fcf5a85d5c7293e1)]
24 | - 🐛 Single config file missing doesn't kill the whole thing. [[7569f6b](https://github.com/raguay/ModalFileManager/commit/7569f6bd361832db2e42a1994872c968d072ed95)]
25 | - 🐛 Extra files generated [[a1fb489](https://github.com/raguay/ModalFileManager/commit/a1fb4896803f48af3a2602b1ca15cd8539c5779a)]
26 | - 🐛 Major Bug fix and New Features [[8afcc0a](https://github.com/raguay/ModalFileManager/commit/8afcc0a966fa626bc26f9e3554a557c5f041f1c2)]
27 | - 🐛 Fixing Quick Search Position and Look [[f5d671f](https://github.com/raguay/ModalFileManager/commit/f5d671f87764be8ec7602b1930737857170b73da)]
28 |
29 | ### Miscellaneous
30 |
31 | - daily commit [[16c558a](https://github.com/raguay/ModalFileManager/commit/16c558a8c657e71ee801ed74c1e440c0bfa678b1)]
32 | - cleaning up [[7b5b805](https://github.com/raguay/ModalFileManager/commit/7b5b8057b74ce5980949bae6564bb1290d19bd95)]
33 | - cleaning up [[acb884b](https://github.com/raguay/ModalFileManager/commit/acb884bf8d1a3b32a555603f5af644b5fb98a06a)]
34 | - Adding to the README.md [[df788b9](https://github.com/raguay/ModalFileManager/commit/df788b993b19fc8e6c3cae60a7db46d58229507d)]
35 | - fixing README.md for font changes. [[73c2b58](https://github.com/raguay/ModalFileManager/commit/73c2b5872b8cec1e8f21fef39df4cf5a4ab614da)]
36 | - Fixing title [[4406c3b](https://github.com/raguay/ModalFileManager/commit/4406c3b64d054a88b7b4d088817f7282f5be568f)]
37 | - initial commit [[d8fb672](https://github.com/raguay/ModalFileManager/commit/d8fb6726c746dfa10b4a8f29ec1e37c45f06aa37)]
38 | - Create FUNDING.yml [[80365e5](https://github.com/raguay/ModalFileManager/commit/80365e51dcba76d70a231fa6d3def17c5898087f)]
39 | - Updating the README.md [[1f2ce25](https://github.com/raguay/ModalFileManager/commit/1f2ce25489f71c5406c54c3c8b0bd094657d15a5)]
40 | - Initial commit [[2b2663f](https://github.com/raguay/ModalFileManager/commit/2b2663fee1c6d1fe2ba25529ddcdf54b1e75ac42)]
41 |
42 |
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Richard Guay
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Pictures/Buffy-DP-MFM-500.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/Pictures/Buffy-DP-MFM-500.jpg
--------------------------------------------------------------------------------
/Pictures/Buffy-DP-MFM.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/Pictures/Buffy-DP-MFM.jpg
--------------------------------------------------------------------------------
/Pictures/DraculaPro-MFM.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/Pictures/DraculaPro-MFM.jpg
--------------------------------------------------------------------------------
/Pictures/ModalFileManager.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/Pictures/ModalFileManager.jpeg
--------------------------------------------------------------------------------
/Pictures/Pref-Extensions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/Pictures/Pref-Extensions.png
--------------------------------------------------------------------------------
/Pictures/Pref-General1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/Pictures/Pref-General1.png
--------------------------------------------------------------------------------
/Pictures/Pref-General2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/Pictures/Pref-General2.png
--------------------------------------------------------------------------------
/Pictures/Pref-Theme1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/Pictures/Pref-Theme1.png
--------------------------------------------------------------------------------
/Pictures/Pref-Theme2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/Pictures/Pref-Theme2.png
--------------------------------------------------------------------------------
/Pictures/Pref-theme3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/Pictures/Pref-theme3.png
--------------------------------------------------------------------------------
/Pictures/SecurityPrivacy1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/Pictures/SecurityPrivacy1.png
--------------------------------------------------------------------------------
/Pictures/Van-HelsingDP-MFM.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/Pictures/Van-HelsingDP-MFM.jpg
--------------------------------------------------------------------------------
/Pictures/VerifyDeveloper.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/Pictures/VerifyDeveloper.png
--------------------------------------------------------------------------------
/Pictures/VerifyDeveloper2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/Pictures/VerifyDeveloper2.png
--------------------------------------------------------------------------------
/icons/mfm-icon.afdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/icons/mfm-icon.afdesign
--------------------------------------------------------------------------------
/icons/mfm-icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/icons/mfm-icon.icns
--------------------------------------------------------------------------------
/icons/mfm-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/icons/mfm-icon.png
--------------------------------------------------------------------------------
/icons/mfm-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/maskfile.md:
--------------------------------------------------------------------------------
1 | ## build
2 |
3 | > Build the Modal File Manager.
4 |
5 | **OPTIONS**
6 | * dev
7 | * flags: -d --dev
8 | * type: boolean
9 | * desc: Runs the development build which doesn't compile the bitcode.
10 |
11 | ```fish
12 | npm run build
13 | if set -q dev
14 | rm public/index.html
15 | rm -Rf mfm.app/Contents/Resources/app.nw
16 | mkdir mfm.app/Contents/Resources/app.nw
17 | cp public/index-dev.html public/index.html
18 | cp public/*.js mfm.app/Contents/Resources/app.nw
19 | cp public/index.html mfm.app/Contents/Resources/app.nw
20 | cp public/package.json mfm.app/Contents/Resources/app.nw
21 | cp public/*.css mfm.app/Contents/Resources/app.nw
22 | cp icons/mfm-icon.icns mfm.app/Contents/Resources/app.icns
23 | else
24 | /Applications/nwjs-sdk/nwjc public/bundle.js public/bundle.bin
25 | rm public/*.map
26 | rm -Rf mfm.app/Contents/Resources/app.nw
27 | mkdir mfm.app/Contents/Resources/app.nw
28 | rm public/index.html
29 | cp public/index-normal.html public/index.html
30 | cp public/*.bin mfm.app/Contents/Resources/app.nw
31 | cp public/index.html mfm.app/Contents/Resources/app.nw
32 | cp public/package.json mfm.app/Contents/Resources/app.nw
33 | cp public/*.css mfm.app/Contents/Resources/app.nw
34 | cp icons/mfm-icon.icns mfm.app/Contents/Resources/app.icns
35 | end
36 | cd mfm.app/Contents/Resources/app.nw
37 | npm install
38 | ```
39 |
40 | ## launch
41 |
42 | > Launch the application using the sdk version of NW.js.
43 |
44 | **OPTIONS**
45 | * dev
46 | * flags: -d --dev
47 | * type: boolean
48 | * desc: Runs the development build which doesn't compile the bitcode.
49 |
50 | ```fish
51 | if set -q dev
52 | /Applications/nwjs-sdk/nwjs.app/Contents/MacOS/nwjs ./public > /dev/null 2>&1 &
53 | else
54 | /Applications/nwjs.app/Contents/MacOS/nwjs ./public > /dev/null 2>&1 &
55 | end
56 | ```
57 |
58 | ## launchapp
59 | > Launch the application.
60 |
61 | ```fish
62 | open -a mfm.app .
63 | ```
64 |
65 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "modalfilemanager",
3 | "description": "A file manager with Vim style hotkeys",
4 | "version": "1.0.0",
5 | "scripts": {
6 | "build": "rollup -c"
7 | },
8 | "devDependencies": {
9 | "@rollup/plugin-commonjs": "^14.0.0",
10 | "@rollup/plugin-node-resolve": "^8.0.0",
11 | "rollup": "^2.3.4",
12 | "rollup-plugin-livereload": "^2.0.0",
13 | "rollup-plugin-node-polyfills": "^0.2.1",
14 | "rollup-plugin-svelte": "^6.0.0",
15 | "rollup-plugin-terser": "^7.0.0",
16 | "svelte": "^3.0.0"
17 | },
18 | "dependencies": {
19 | "@octokit/rest": "^18.3.5",
20 | "chokidar": "^3.5.1",
21 | "sirv-cli": "^1.0.10",
22 | "svelte-color-picker": "^1.0.7",
23 | "svelte-icons": "^2.1.0"
24 | },
25 | "fig": {
26 | "build": {
27 | "name": "build",
28 | "description": "Build the Modal File Manager.",
29 | "icon": "🛠️"
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/public/bundle.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raguay/ModalFileManager-NWjs/642ed447cade19842d776a237f79dcf6468a69aa/public/bundle.bin
--------------------------------------------------------------------------------
/public/bundle.css:
--------------------------------------------------------------------------------
1 | .main-container.svelte-8esefu{width:240px;height:265px;background:#f2f2f2;border-radius:1px;-webkit-box-shadow:0px 0px 4px 0px rgba(0, 0, 0, 0.51);-moz-box-shadow:0px 0px 4px 0px rgba(0, 0, 0, 0.51);box-shadow:0px 0px 4px 0px rgba(0, 0, 0, 0.51);-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.saturation-gradient.svelte-8esefu{background:linear-gradient(to right, rgb(255, 255, 255), rgba(255, 255, 255, 0));width:240px;height:160px}.value-gradient.svelte-8esefu{background:linear-gradient(to top, rgb(0, 0, 0), rgba(0, 0, 0, 0));overflow:hidden;width:240px;height:160px}.hue-selector.svelte-8esefu{background:linear-gradient(to right, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);margin:15px 10px 10px 10px;border-radius:10px;height:10px}#hue-picker.svelte-8esefu{background:#FFF;width:12px;height:12px;border-radius:50%;left:0%;position:relative;cursor:default;transform:translate(-5px, -1px);-webkit-box-shadow:0px 0px 5px 0px rgba(0, 0, 0, 0.67);-moz-box-shadow:0px 0px 5px 0px rgba(0, 0, 0, 0.67);box-shadow:0px 0px 5px 0px rgba(0, 0, 0, 0.67)}#hue-event.svelte-8esefu{width:236px;height:14px;transform:translate(-8px, -14px);cursor:default;touch-action:none}.alpha-selector.svelte-8esefu{background-image:linear-gradient(45deg, #808080 25%, transparent 25%), linear-gradient(-45deg, #808080 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #808080 75%), linear-gradient(-45deg, transparent 75%, #808080 75%);background-size:10px 10px;background-position:0 0, 0 5px, 5px -5px, -5px 0px;margin:10px 10px;border-radius:10px;height:10px;position:relative}#alpha-picker.svelte-8esefu{background:#FFF;width:12px;height:12px;border-radius:50%;left:100%;position:relative;cursor:default;transform:translate(-5px, -11px);-webkit-box-shadow:0px 0px 5px 0px rgba(0, 0, 0, 0.67);-moz-box-shadow:0px 0px 5px 0px rgba(0, 0, 0, 0.67);box-shadow:0px 0px 5px 0px rgba(0, 0, 0, 0.67)}#alpha-event.svelte-8esefu{width:236px;height:14px;transform:translate(-8px, -24px);cursor:default;touch-action:none}.alpha-value.svelte-8esefu{background:linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1));width:100%;height:100%;border-radius:10px}.colorsquare.svelte-8esefu{background:rgb(255, 0, 0)}#colorsquare-picker.svelte-8esefu{margin:0;padding:0;width:12px;height:12px;border-radius:50%;border:2px solid #FFFB;position:relative;transform:translate(-9px, -9px);left:100%}#colorsquare-event.svelte-8esefu{width:100%;height:100%;position:relative;transform:translate(0, -16px);touch-action:none}.color-info-box.svelte-8esefu{margin:10px;width:100%;height:22px;vertical-align:middle;position:relative}.color-picked.svelte-8esefu{width:18px;height:18px;border-radius:2px;background:rgba(255, 0, 0, 1);display:inline-block}.color-picked-bg.svelte-8esefu{background-image:linear-gradient(45deg, #808080 25%, transparent 25%), linear-gradient(-45deg, #808080 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #808080 75%), linear-gradient(-45deg, transparent 75%, #808080 75%);background-size:10px 10px;background-position:0 0, 0 5px, 5px -5px, -5px 0px;border:2px solid #FFF;border-radius:4px;width:18px;height:18px;color:#fff;display:inline-block}.hex-text-block.svelte-8esefu{display:inline-block;background:white;border-radius:2px;padding:2px;border:1px solid #e3e3e3;height:16px;width:54px;vertical-align:top;text-align:center}.rgb-text-block.svelte-8esefu{display:inline-block;background:white;border-radius:2px;padding:2px;margin:0 1px;border:1px solid #dcdcdc;height:16px;width:23px;vertical-align:top;text-align:center}.rgb-text-div.svelte-8esefu{right:10%;display:inline-block;vertical-align:top;position:absolute}.text-label.svelte-8esefu{position:relative;top:-12px;font-family:sans-serif;font-size:small;color:#888}.text.svelte-8esefu{display:inline;font-family:sans-serif;margin:0;display:inline-block;font-size:12px;font-size-adjust:0.50;position:relative;top:-1px;vertical-align:middle;-webkit-touch-callout:all;-webkit-user-select:all;-khtml-user-select:all;-moz-user-select:all;-ms-user-select:all;user-select:all}
2 | svg.svelte-c8tyih{stroke:currentColor;fill:currentColor;stroke-width:0;width:100%;height:auto;max-height:100%}
3 | body{padding:0px;margin:0px;width:100%;height:100%;overflow:hidden;user-select:none}
4 | #commandPrompt.svelte-1cf6v2y.svelte-1cf6v2y{display:flex;flex-direction:column;position:absolute;z-index:500;top:20px;left:10px;border:3px solid;border-radius:5px}#commandPrompt.svelte-1cf6v2y input.svelte-1cf6v2y{outline-color:transparent;margin:auto;padding:0px;width:100px;border:0px solid transparent;margin:10px auto}#commandPrompt.svelte-1cf6v2y ul.svelte-1cf6v2y{list-style:none}#commandPrompt.svelte-1cf6v2y ul li a.svelte-1cf6v2y{text-decoration:none;color:inherit}#commandlist.svelte-1cf6v2y.svelte-1cf6v2y{overflow-y:scroll;overflow-x:hidden}.description.svelte-1cf6v2y.svelte-1cf6v2y{margin:5px 0px 10px 20px}
5 | #searchList.svelte-mc3y08.svelte-mc3y08{position:absolute;display:flex;flex-direction:column;z-index:200}#searchList.svelte-mc3y08 ul.svelte-mc3y08{list-style-type:none;margin:0px;padding:0px}#searchList.svelte-mc3y08 ul li.svelte-mc3y08{text-decoration:none;margin:0px;padding:0px}.dirList.svelte-mc3y08.svelte-mc3y08{display:flex;flex-direction:row;margin:0px;padding:0px;height:20px}.dir.svelte-mc3y08.svelte-mc3y08{height:20px;margin:0px auto 0px 10px;white-space:nowrap;overflow-x:scroll;direction:rtl}.dirinputclass.svelte-mc3y08.svelte-mc3y08{height:20px;margin:0px auto 0px 10px;white-space:nowrap}
6 | .entry.svelte-kn761a{display:flex;flex-direction:row;height:20px;max-height:20px;min-height:20px;width:100%;margin:3px 3px}.name.svelte-kn761a{white-space:nowrap}.type.svelte-kn761a{height:20px;width:20px;min-height:20px;min-width:20px;max-height:20px;max-width:20px;margin:0px 10px 0px 5px}
7 | table.svelte-1bbviaz{margin-left:10px;user-select:text}td.svelte-1bbviaz,th.svelte-1bbviaz{text-align:left}.addNewItem.svelte-1bbviaz{color:red;cursor:pointer;font-size:20px;margin-left:10px}
8 | input.svelte-26er1v{width:100%}.editTD.svelte-26er1v{margin-left:10px;cursor:pointer}.deleteTD.svelte-26er1v{margin-left:10px;cursor:pointer}
9 | #extension.svelte-1has95i.svelte-1has95i{display:flex;flex-direction:column;height:100%;margin:0px 0px 0px 10px;overflow-y:auto;overflow-x:hidden}#createExtensionButton.svelte-1has95i.svelte-1has95i{padding:5px;margin:0px 10px;border:solid 1px transparent;border-radius:5px}#msgBoxExt.svelte-1has95i.svelte-1has95i{display:flex;flex-direction:column;position:absolute;border:solid 2px;border-radius:10px;top:25%;left:25%;width:50%;padding:10px}#msgBoxExt.svelte-1has95i button.svelte-1has95i{border:solid 1px;border-radius:10px;width:50%;margin:auto}#extensionTable.svelte-1has95i.svelte-1has95i{width:100%;margin:20px 0px 20px 0px}#extensionNameInput.svelte-1has95i.svelte-1has95i{margin:0px 10px}label.svelte-1has95i.svelte-1has95i{margin:auto 10px}.deleteButton.svelte-1has95i.svelte-1has95i{border:solid 1px transparent;border-radius:10px;background:red;color:black}.editButton.svelte-1has95i.svelte-1has95i{padding:5px;margin:0px 10px;border:solid 1px transparent;border-radius:5px}.row.svelte-1has95i.svelte-1has95i{display:flex;flex-direction:row;margin:10px}
10 | #extrapanel.svelte-14e7pta.svelte-14e7pta{display:flex;flex-direction:column;height:100%;margin:0px;padding:5px;overflow-y:auto;overflow-wrap:anywhere;word-break:break-all}#extrapanel.svelte-14e7pta img.svelte-14e7pta{width:100%}.stats.svelte-14e7pta.svelte-14e7pta{display:flex;flex-direction:column}.stats.svelte-14e7pta p.svelte-14e7pta{margin:5px 0px 0px 0px}
11 | #leftSide.svelte-1az5yr9{display:flex;flex-direction:column;padding:0px;margin:0px;width:50%}#rightSide.svelte-1az5yr9{display:flex;flex-direction:column;padding:0px;margin:0px;width:50%}#container.svelte-1az5yr9{padding:0px;margin:0px;display:flex;flex-direction:row;position:fixed;top:0px;bottom:34px;width:100%}
12 | #general.svelte-1uugwin{display:flex;flex-direction:column;height:100%;overflow-y:auto;overflow-x:auto}#trashcanans.svelte-1uugwin{margin:auto 0px auto 10px}h3.svelte-1uugwin{margin-left:10px}.row.svelte-1uugwin{display:flex;flex-direction:row;margin:10px}
13 | #GitHub.svelte-34rydv.svelte-34rydv{position:absolute;display:flex;flex-direction:column;top:20px;left:20px;border:3px solid;border-radius:3px;z-index:100}#GitHubHeader.svelte-34rydv.svelte-34rydv{display:flex;flex-direction:row;margin:10px}#GitHubHeader.svelte-34rydv h3.svelte-34rydv{margin:0px auto 0px 0px}#GitHubHeader.svelte-34rydv span.svelte-34rydv{margin:0px 0px 0px auto}#GitHubList.svelte-34rydv.svelte-34rydv{margin:5px 10px;overflow-y:auto;overflow-x:hidden}#inputHidden.svelte-34rydv.svelte-34rydv{width:0px;height:0px;margin:0px;padding:0px;border:0px solid transparent}.reporow.svelte-34rydv.svelte-34rydv{display:flex;flex-direction:row;margin:0px}.reponame.svelte-34rydv.svelte-34rydv{margin:0px auto 0px 0px}.repostars.svelte-34rydv.svelte-34rydv{margin:0px 0px 0px auto}.repodisc.svelte-34rydv.svelte-34rydv{margin:0px 0px 0px 15px}.repoblock.svelte-34rydv.svelte-34rydv{display:flex;flex-direction:column;margin:5px 0px}.repobuttons.svelte-34rydv.svelte-34rydv{display:flex;flex-direction:row;margin:5px auto}.repobuttons.svelte-34rydv button.svelte-34rydv{margin:0px 10px;border-radius:5px}
14 | #messageboxbg.svelte-8ji6gn.svelte-8ji6gn{position:absolute;top:0px;left:0px;height:100%;width:100%;background-color:transparent;z-index:100}#messagebox.svelte-8ji6gn.svelte-8ji6gn{display:flex;flex-direction:column;margin:auto;padding:10px;width:70%;border:3px solid;border-radius:5px}#butRow.svelte-8ji6gn.svelte-8ji6gn{display:flex;flex-direction:row;margin:20px 0px 0px 0px}#butRow.svelte-8ji6gn button.svelte-8ji6gn:first-child{margin:auto 10px auto auto;border-radius:5px}#butRow.svelte-8ji6gn button.svelte-8ji6gn:last-child{margin:auto auto auto 10px;border-radius:5px}#pickerDiv.svelte-8ji6gn input.svelte-8ji6gn{width:100%
15 | }.picker.svelte-8ji6gn.svelte-8ji6gn{display:flex;flex-direction:column;overflow-y:scroll;overflow-x:hidden}.picker.svelte-8ji6gn a.svelte-8ji6gn{text-decoration:none}.pickerSelected.svelte-8ji6gn.svelte-8ji6gn{text-decoration:none}
16 | .panel.svelte-18ls1gz{display:flex;flex-direction:column;padding:0px;margin:0px;height:100%;overflow-y:scroll;overflow-x:hidden}.empty.svelte-18ls1gz{height:100%}
17 | #Preferences.svelte-1u7uvz.svelte-1u7uvz{display:flex;flex-direction:column;position:absolute;top:0px;left:0px;z-index:100;width:100%;height:100%}#vimInputDiv.svelte-1u7uvz.svelte-1u7uvz{width:0px;height:0px;margin:0px;padding:0px;border:0px solid transparent}#buttonRow.svelte-1u7uvz.svelte-1u7uvz{display:flex;flex-direction:row;width:100%;margin:5px}#buttonRow.svelte-1u7uvz button.svelte-1u7uvz{margin:auto;border-radius:5px;padding:5px}ul.svelte-1u7uvz.svelte-1u7uvz{display:flex;flex-direction:row;padding:0px;margin:0px 5px 0px 10px}ul.svelte-1u7uvz li.svelte-1u7uvz{border-radius:25px 5px 0px 0px;border:3px solid;padding:5px 5px 2px 15px;margin:0px 10px 0px 0px;list-style:none}h2.svelte-1u7uvz.svelte-1u7uvz{text-align:center;margin-top:3px}
18 | #quickSearch.svelte-xwdsdw.svelte-xwdsdw{position:absolute;display:flex;flex-direction:row;margin:0px;padding:0px;border:0px solid transparent;height:30px;max-height:30px;min-height:30px;width:100px;max-width:100px;min-width:100px;bottom:0px;z-index:100;user-select:none;text-decoration:none}#quickSearch.svelte-xwdsdw input.svelte-xwdsdw{outline-color:transparent;margin:auto;padding:0px;width:100px;border:3px solid transparent;border-radius:5px}
19 | #resizeBorder.svelte-1ll3mt7{display:flex;flex-direction:row;height:auto;cursor:ew-resize}
20 | #statusLine.svelte-1w9ii3p{display:flex;flex-direction:row;flex-grow:0;margin:0px;padding:0px;width:100%;min-height:31px;height:31px;max-height:31px;position:fixed;bottom:0px;left:0px}.pane.svelte-1w9ii3p{margin:0px;padding:5px 5px 5px 10px;width:35px;min-width:50px}.state.svelte-1w9ii3p{margin:0px;padding:5px 10px 5px 10px;color:black;min-width:50px}.file.svelte-1w9ii3p{margin:0px;padding:5px 10px 5px 5px;min-width:50px;overflow:hidden;white-space:nowrap}span.customdata.svelte-1w9ii3p{position:relative }span.customdata.svelte-1w9ii3p:hover::before{content:attr(data-tooltip);background-color:inherit;color:inherit;position:fixed;bottom:2em;min-width:20px;border:1px #808080 solid;padding:8px;z-index:1}
21 | .cpicker.svelte-1cxeta0.svelte-1cxeta0{display:flex;flex-direction:row;color:black}.rowCell.svelte-1cxeta0.svelte-1cxeta0{display:flex;flex-direction:row}.colorDiv.svelte-1cxeta0.svelte-1cxeta0{height:18px;width:18px;margin:auto 0px auto 10px;padding:0px;border:solid 1px}.rowCell.svelte-1cxeta0 input.svelte-1cxeta0{width:100%;border:solid 1px transparent;border-radius:10px}
22 | #theme.svelte-wb0n4a.svelte-wb0n4a{display:flex;flex-direction:column;height:100%;margin:0px 0px 0px 10px;overflow-y:auto;overflow-x:hidden}#createThemeButton.svelte-wb0n4a.svelte-wb0n4a{padding:5px;margin:0px 10px;border:solid 1px transparent;border-radius:5px}#msgBoxTheme.svelte-wb0n4a.svelte-wb0n4a{display:flex;flex-direction:column;position:absolute;border:solid 2px;border-radius:10px;top:25%;left:25%;width:50%;padding:10px}#msgBoxTheme.svelte-wb0n4a button.svelte-wb0n4a{border:solid 1px;border-radius:10px;width:50%;margin:auto}#themeTable.svelte-wb0n4a.svelte-wb0n4a{width:100%;margin:20px 0px 20px 0px}#themeNameInput.svelte-wb0n4a.svelte-wb0n4a{padding:5px;margin:0px 10px 0px 10px;border:solid 1px transparent;border-radius:5px}label.svelte-wb0n4a.svelte-wb0n4a{margin:auto 10px}table.svelte-wb0n4a.svelte-wb0n4a{width:60%}.row.svelte-wb0n4a.svelte-wb0n4a{display:flex;flex-direction:row}.updateButton.svelte-wb0n4a.svelte-wb0n4a{border:solid 1px transparent;border-radius:10px}.deleteButton.svelte-wb0n4a.svelte-wb0n4a{border:solid 1px transparent;border-radius:10px;background:red;color:black}
23 |
24 | /*# sourceMappingURL=bundle.css.map */
--------------------------------------------------------------------------------
/public/global.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | position: relative;
3 | width: 100%;
4 | height: 100%;
5 | }
6 |
7 | body {
8 | color: #333;
9 | margin: 0;
10 | padding: 8px;
11 | box-sizing: border-box;
12 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
13 | }
14 |
15 | a {
16 | color: rgb(0,100,200);
17 | text-decoration: none;
18 | }
19 |
20 | a:hover {
21 | text-decoration: underline;
22 | }
23 |
24 | a:visited {
25 | color: rgb(0,80,160);
26 | }
27 |
28 | label {
29 | display: block;
30 | }
31 |
32 | input, button, select, textarea {
33 | font-family: inherit;
34 | font-size: inherit;
35 | -webkit-padding: 0.4em 0;
36 | padding: 0.4em;
37 | margin: 0 0 0.5em 0;
38 | box-sizing: border-box;
39 | border: 1px solid #ccc;
40 | border-radius: 2px;
41 | }
42 |
43 | input:disabled {
44 | color: #ccc;
45 | }
46 |
47 | button {
48 | color: #333;
49 | background-color: #f4f4f4;
50 | outline: none;
51 | }
52 |
53 | button:disabled {
54 | color: #999;
55 | }
56 |
57 | button:not(:disabled):active {
58 | background-color: #ddd;
59 | }
60 |
61 | button:focus {
62 | border-color: #666;
63 | }
64 |
--------------------------------------------------------------------------------
/public/index-dev.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Modal File Manager
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/public/index-normal.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Modal File Manager
8 |
9 |
10 |
11 |
12 |
13 |
14 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Modal File Manager
8 |
9 |
10 |
11 |
12 |
13 |
14 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/public/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mfm",
3 | "description": "A file manager written in Svelte, HTML, and CSS running in NW.js.",
4 | "license": "MIT",
5 | "version": "1.0.0",
6 | "window": {
7 | "id": "mfm",
8 | "height": 300,
9 | "width": 500,
10 | "show_in_taskbar": true,
11 | "frame": true,
12 | "show": true,
13 | "transparent": false
14 | },
15 | "main": "index.html",
16 | "node-remote": "",
17 | "nodejs": true,
18 | "dependencies": {
19 | "chokidar": "^3.5.2"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import svelte from 'rollup-plugin-svelte';
2 | import resolve from '@rollup/plugin-node-resolve';
3 | import commonjs from '@rollup/plugin-commonjs';
4 | import livereload from 'rollup-plugin-livereload';
5 | import { terser } from 'rollup-plugin-terser';
6 | import nodePolyfills from 'rollup-plugin-node-polyfills';
7 |
8 | const production = !process.env.ROLLUP_WATCH;
9 |
10 | function serve() {
11 | let server;
12 |
13 | function toExit() {
14 | if (server) server.kill(0);
15 | }
16 |
17 | return {
18 | writeBundle() {
19 | if (server) return;
20 | server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
21 | stdio: ['ignore', 'inherit', 'inherit'],
22 | shell: true
23 | });
24 |
25 | process.on('SIGTERM', toExit);
26 | process.on('exit', toExit);
27 | }
28 | };
29 | }
30 |
31 | export default {
32 | input: 'src/main.js',
33 | output: {
34 | sourcemap: true,
35 | format: 'iife',
36 | name: 'app',
37 | file: 'public/bundle.js'
38 | },
39 | plugins: [
40 | nodePolyfills(),
41 | svelte({
42 | // enable run-time checks when not in production
43 | dev: !production,
44 | // we'll extract any component CSS out into
45 | // a separate file - better for performance
46 | css: css => {
47 | css.write('bundle.css');
48 | }
49 | }),
50 |
51 | // If you have external dependencies installed from
52 | // npm, you'll most likely need these plugins. In
53 | // some cases you'll need additional configuration -
54 | // consult the documentation for details:
55 | // https://github.com/rollup/plugins/tree/master/packages/commonjs
56 | resolve({
57 | browser: true,
58 | dedupe: ['svelte']
59 | }),
60 | commonjs(),
61 |
62 | // In dev mode, call `npm run start` once
63 | // the bundle has been generated
64 | !production && serve(),
65 |
66 | // Watch the `public` directory and refresh the
67 | // browser on changes when not in production
68 | !production && livereload('public'),
69 |
70 | // If we're building for production (npm run build
71 | // instead of npm run dev), minify
72 | production && terser()
73 | ],
74 | watch: {
75 | clearScreen: false
76 | }
77 | };
78 |
--------------------------------------------------------------------------------
/src/Start.svelte:
--------------------------------------------------------------------------------
1 | {
3 | $ctrlKey = e.ctrlKey;
4 | $shiftKey = e.shiftKey;
5 | $metaKey = e.metaKey;
6 | if(($skipKey && (e.key === 'Enter'))||(showComponent !== 'filemanager')) {
7 | $keyProcess = true;
8 | } else {
9 | if($keyProcess) {
10 | if($processKey !== null) $processKey(e);
11 | }
12 | }
13 | $skipKey = false;
14 | }}
15 | on:keyup={(e) => {
16 | $ctrlKey = e.ctrlKey;
17 | $shiftKey = e.shiftKey;
18 | $metaKey = e.metaKey;
19 | }}
20 | />
21 |
22 |
25 | {#if showComponent === 'preferences'}
26 |
29 | {/if}
30 |
31 |
41 |
42 |
59 |
--------------------------------------------------------------------------------
/src/components/CommandPrompt.svelte:
--------------------------------------------------------------------------------
1 | { exitCP(); }}
8 | >
9 |
19 | {#if filtered.length > 0}
20 |
56 | {/if}
57 |
58 |
59 |
98 |
99 |
214 |
215 |
--------------------------------------------------------------------------------
/src/components/DirectoryListing.svelte:
--------------------------------------------------------------------------------
1 |
4 | {#if show}
5 |
9 | {newPath}
10 |
11 | {:else}
12 |
{}}
17 | on:keypress|stopPropagation={(e) => {}}
18 | bind:value={inputPath}
19 | on:blur={editOff}
20 | on:input={processInput}
21 | />
22 | {#if (dirlist.length > 0) && (dirListDOM !== null) && (dirInputDOM !== null)}
23 |
26 |
27 | {#each dirlist as item, key}
28 | {#if item !== ''}
29 | -
30 | {item}
31 |
32 | {/if}
33 | {/each}
34 |
35 |
36 | {/if}
37 | {/if}
38 |
39 |
40 |
82 |
83 |
358 |
--------------------------------------------------------------------------------
/src/components/Entry.svelte:
--------------------------------------------------------------------------------
1 | {#if (((localCursor.pane === pane) && (localCursor.entry.name == entry.name))||(entry.selected))}
2 | { dropFiles(e,'dragend'); }}
10 | on:drop|preventDefault={(e) => { dropFiles(e, 'drop'); }}
11 | on:dragover|preventDefault={(e) => { dropFiles(e,'dragover'); }}
12 | on:dragenter|preventDefault={(e) => {dropFiles(e, 'dragenter'); }}
13 | >
14 |
15 | {#if entry.type === 0}
16 |
17 | {:else if entry.type === 1}
18 |
19 | {:else}
20 |
21 | {/if}
22 |
23 | {entry.name}
24 |
25 | {:else }
26 | { dropFiles(e, 'drop'); }}
33 | on:dragover|preventDefault={(e) => { dropFiles(e,'dragover'); }}
34 | >
35 |
36 | {#if entry.type === 0}
37 |
38 | {:else if entry.type === 1}
39 |
40 | {:else}
41 |
42 | {/if}
43 |
44 |
47 | {entry.name}
48 |
49 |
50 | {/if}
51 |
52 |
77 |
78 |
285 |
--------------------------------------------------------------------------------
/src/components/Env.svelte:
--------------------------------------------------------------------------------
1 | {#if ((localConfig !== null) && (typeof localConfig.configuration.env !== 'undefined'))}
2 |
48 | {/if}
49 |
50 |
67 |
68 |
108 |
--------------------------------------------------------------------------------
/src/components/EnvTableRow.svelte:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 | 🖋️
8 |
9 | |
10 |
14 |
15 | ❌
16 |
17 | |
18 | {name} |
19 | {#if editValue}
20 |
24 | {:else}
25 | {value} |
26 | {/if}
27 |
28 |
29 |
44 |
45 |
73 |
--------------------------------------------------------------------------------
/src/components/ExtensionPrefs.svelte:
--------------------------------------------------------------------------------
1 |
5 | {#if showMsgBox}
6 |
14 |
{msgTitle}
15 |
{msgText}
16 |
27 |
28 | {/if}
29 | {#if extensionList !== null}
30 |
33 |
34 |
35 |
36 | Extension Name
37 | |
38 |
39 | |
40 |
41 | |
42 |
43 |
44 |
45 | {#each extensionList as item}
46 |
47 |
48 | {item.name}
49 | |
50 |
51 |
63 | |
64 |
65 |
73 | |
74 |
75 | {/each}
76 |
77 |
78 | {/if}
79 |
82 |
87 | {
95 | setFocus(false);
96 | }}
97 | on:mouseleave={() => {
98 | setFocus(true);
99 | }}
100 | on:blur={()=>{
101 | setFocus(true);
102 | }}
103 | />
104 |
114 |
115 |
116 |
117 |
186 |
187 |
422 |
--------------------------------------------------------------------------------
/src/components/ExtraPanel.svelte:
--------------------------------------------------------------------------------
1 |
36 |
37 |
62 |
63 |
177 |
--------------------------------------------------------------------------------
/src/components/GeneralPrefs.svelte:
--------------------------------------------------------------------------------
1 |
5 | {#if localConfig !== null}
6 |
Environment for Launching Programs
7 |
8 |
9 |
Other Configuration Items
10 |
13 |
18 | {#if localConfig.configuration.useTrash}
19 |
25 | {:else}
26 |
31 | {/if}
32 |
33 | {/if}
34 |
35 |
36 |
59 |
60 |
105 |
--------------------------------------------------------------------------------
/src/components/GitHub.svelte:
--------------------------------------------------------------------------------
1 | { exitGitHub(); }}
8 | >
9 |
25 |
28 | {#await repos}
29 |
Loading Extensions Repositories....
30 | {:then value}
31 | {#if check(value)}
32 | {#each value.data.items as repo}
33 |
34 |
35 |
36 | {repo.name}
37 |
38 |
41 | {repo.stargazers_count} ⭐️ s
42 |
43 |
44 |
45 |
46 | {repo.description}
47 |
48 |
49 | {#if hasMsg(repo)}
50 |
53 | {@html getMsg(repo)}
54 |
55 | {/if}
56 |
57 | {#if extExists(repo)}
58 |
66 | {:else}
67 |
75 | {/if}
76 |
77 |
78 | {/each}
79 | {/if}
80 | {:catch error}
81 |
There was an error: {error}
82 | {/await}
83 | {#await themes}
84 |
Loading Theme Repositories....
85 | {:then valueTheme}
86 | {#if check(valueTheme)}
87 | {#each valueTheme.data.items as thm}
88 |
89 |
90 |
91 | {thm.name}
92 |
93 |
96 | {thm.stargazers_count} ⭐️ s
97 |
98 |
99 |
100 |
101 | {thm.description}
102 |
103 |
104 | {#if hasMsg(thm)}
105 |
108 | {@html getMsg(thm)}
109 |
110 | {/if}
111 |
112 | {#if themeExists(thm)}
113 |
121 |
129 | {:else}
130 |
138 | {/if}
139 |
140 |
141 | {/each}
142 | {/if}
143 | {:catch error}
144 |
There was an error: {error}
145 | {/await}
146 |
147 |
148 |
149 |
224 |
225 |
439 |
440 |
--------------------------------------------------------------------------------
/src/components/MessageBox.svelte:
--------------------------------------------------------------------------------
1 |
2 |
7 | {#if config !== null}
8 |
{config.title}
9 | {#if typeof items !== null}
10 | {#each items as item}
11 | {#if typeof item !== 'undefined'}
12 | {#if item.type === 'input'}
13 |
{item.msg}
14 |
{
18 | if(e.key === 'Enter') {
19 | e.preventDefault();
20 | returnValue(true);
21 | }
22 | }}
23 | />
24 | {:else if item.type === 'selector'}
25 |
37 | {:else if item.type === 'picker'}
38 |
79 | {:else if item.type === 'spinner'}
80 |
85 | {:else if item.type === 'label'}
86 |
92 | {:else if item.type === 'html'}
93 | {@html item.text}
94 | {/if}
95 | {:else}
96 |
System Error
97 | {/if}
98 | {/each}
99 | {:else}
100 |
System Error
101 | {/if}
102 | {#if ((typeof config.noShowButton !== 'undefined')&&(!config.noShowButton))}
103 |
104 |
107 |
110 |
111 | {/if}
112 | {/if}
113 |
114 |
115 |
116 |
172 |
173 |
361 |
362 |
--------------------------------------------------------------------------------
/src/components/Pane.svelte:
--------------------------------------------------------------------------------
1 |
5 | {#each entries as entry,index}
6 |
15 | {/each}
16 |
{cursorToPane()}}
20 | on:drop|preventDefault={(e) => { dropFiles(e, 'drop'); }}
21 | on:dragover|preventDefault={(e) => { dropFiles(e,'dragover'); }}
22 | >
23 |
24 |
25 |
26 |
41 |
42 |
197 |
--------------------------------------------------------------------------------
/src/components/Preferences.svelte:
--------------------------------------------------------------------------------
1 |
8 |
{
12 | if(keepBlur&&(vimInput !== null)) {
13 | vimInput.focus();
14 | }
15 | }}
16 | on:keydown={(e) => {
17 | if(keepBlur) {
18 | e.preventDefault();
19 | switch(e.key) {
20 | case 'g':
21 | showPanel = 'general';
22 | break;
23 | case 't':
24 | showPanel = 'theme';
25 | break;
26 | case 'e':
27 | showPanel = 'extension';
28 | break;
29 | case 'ArrowUp':
30 | case 'k':
31 | scrollDiv(-1);
32 | break;
33 | case 'ArrowDown':
34 | case 'j':
35 | scrollDiv(1);
36 | break;
37 | case 'Escape':
38 | keepBlur = false;
39 | exitPrefs();
40 | break;
41 | default:
42 | break;
43 | }
44 | }
45 | }}
46 | />
47 |
Preferences
48 |
49 | {#if showPanel === 'general'}
50 | - { showPanel = 'general'; }}
52 | style="border-color: {$theme.textColor};
53 | background-color: {$theme.textColor};
54 | color: {$theme.backgroundColor};"
55 | >
56 | General
57 |
58 | {:else}
59 | - { showPanel = 'general'; }}
61 | style="border-color: {$theme.textColor};
62 | color: {$theme.textColor};
63 | background-color: {$theme.backgroundColor};"
64 | >
65 | General
66 |
67 | {/if}
68 | {#if showPanel === 'theme'}
69 | - { showPanel = 'theme'; }}
71 | style="border-color: {$theme.textColor};
72 | background-color: {$theme.textColor};
73 | color: {$theme.backgroundColor};"
74 | >
75 | Theme
76 |
77 | {:else}
78 | - { showPanel = 'theme'; }}
80 | style="border-color: {$theme.textColor};
81 | color: {$theme.textColor};
82 | background-color: {$theme.backgroundColor};"
83 | >
84 | Theme
85 |
86 | {/if}
87 | {#if showPanel === 'extension'}
88 | - { showPanel = 'extension'; }}
90 | style="border-color: {$theme.textColor};
91 | background-color: {$theme.textColor};
92 | color: {$theme.backgroundColor};"
93 | >
94 | Extension
95 |
96 | {:else}
97 | - { showPanel = 'extension'; }}
99 | style="border-color: {$theme.textColor};
100 | color: {$theme.textColor};
101 | background-color: {$theme.backgroundColor};"
102 | >
103 | Extension
104 |
105 | {/if}
106 |
107 | {#if showPanel === 'general'}
108 |
{
110 | scrollDOM = e.detail.DOM;
111 | }}
112 | on:setKeyProcess={(e) => {
113 | keepBlur = e.detail.blur;
114 | if(keepBlur) timeOut = setTimeout(focusInput, 1000);
115 | }}
116 | />
117 | {:else if showPanel === 'theme'}
118 | {
120 | scrollDOM = e.detail.DOM;
121 | }}
122 | on:setKeyProcess={(e) => {
123 | keepBlur = e.detail.blur;
124 | if(keepBlur) timeOut = setTimeout(focusInput, 1000);
125 | }}
126 | />
127 | {:else if showPanel === 'extension'}
128 | {
130 | switchView(e.detail.view);
131 | }}
132 | on:setScrollDOM={(e) => {
133 | scrollDOM = e.detail.DOM;
134 | }}
135 | on:setKeyProcess={(e) => {
136 | keepBlur = e.detail.blur;
137 | if(keepBlur) timeOut = setTimeout(focusInput, 1000);
138 | }}
139 | />
140 | {/if}
141 |
144 |
155 |
156 |
157 |
158 |
211 |
212 |
347 |
348 |
--------------------------------------------------------------------------------
/src/components/QuickSearch.svelte:
--------------------------------------------------------------------------------
1 |
4 | { exitQS(); }}
10 | style="background-color: {localTheme.textColor};
11 | text-color: {localTheme.backgroundColor};"
12 | />
13 |
14 |
15 |
44 |
45 |
146 |
--------------------------------------------------------------------------------
/src/components/ResizeBorder.svelte:
--------------------------------------------------------------------------------
1 | {
10 | dispatch('mouseDown', true);
11 | }}
12 | on:mouseup={e => {
13 | dispatch('mouseDown', false);
14 | }}
15 | >
16 |
17 |
18 |
26 |
27 |
44 |
--------------------------------------------------------------------------------
/src/components/StatusLine.svelte:
--------------------------------------------------------------------------------
1 |
4 |
7 | {localInputState}
8 |
9 |
12 | {localCurrentCursor.pane}
13 |
14 |
18 | {localCurrentCursor.entry.name}
19 |
20 |
24 | {localCurrentCursor.entry.datetime}
25 |
26 |
31 | {size}
32 |
33 |
34 |
35 |
89 |
90 |
148 |
149 |
--------------------------------------------------------------------------------
/src/components/ThemeItem.svelte:
--------------------------------------------------------------------------------
1 |
2 |
3 | {label}
4 | |
5 | { changeValue(); }}
7 | >
8 |
9 | {#if changeString}
10 | {
17 | changeStringValue(value);
18 | }}
19 | />
20 | {:else}
21 | {value}
22 | {/if}
23 | {#if value.startsWith('#')}
24 |
25 |
26 | {/if}
27 |
28 | |
29 |
30 | {#if changeColor}
31 |
34 | { colorCallback(e.detail); }} startColor={value}/>
35 |
40 |
41 | {/if}
42 |
43 |
69 |
70 |
118 |
--------------------------------------------------------------------------------
/src/components/ThemePrefs.svelte:
--------------------------------------------------------------------------------
1 |
5 |
Current Theme Values
6 | {#if localTheme !== null}
7 |
8 |
9 |
10 |
11 | Name
12 | |
13 |
14 | Value
15 | |
16 |
17 |
18 |
19 | {#each Object.entries(localTheme) as kv}
20 | { changeValue(kv, e); }}
24 | />
25 | {/each}
26 |
27 |
28 | {/if}
29 |
Save as New Theme
30 |
33 |
38 | {
46 | setFocus(false);
47 | }}
48 | on:mouseleave={() => {
49 | setFocus(true);
50 | }}
51 | on:blur={() => {
52 | setFocus(true);
53 | }}
54 | />
55 |
65 |
66 | {#if showMsgBox}
67 |
75 |
{msgTitle}
76 |
{msgText}
77 |
88 |
89 | {/if}
90 |
Existing Themes
91 | {#if themeList !== null}
92 |
156 | {/if}
157 |
158 |
159 |
233 |
234 |
373 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Start from './Start.svelte';
2 |
3 | var mb = new nw.Menu({type:"menubar"});
4 | var submenu = new nw.Menu();
5 | submenu.append(new nw.MenuItem({
6 | label: "About MFM"
7 | }));
8 | submenu.append(new nw.MenuItem({ type: 'separator' }));
9 | submenu.append(new nw.MenuItem({
10 | label: "Quit MFM",
11 | click: function() {
12 | nw.App.closeAllWindows();
13 | },
14 | key: 'q'
15 | }))
16 | mb.append(new nw.MenuItem({
17 | label: "MFM",
18 | tooltip: "MFM",
19 | submenu: submenu
20 | }));
21 | nw.Window.get().menu = mb;
22 |
23 | const ST = new Start({
24 | target: document.body,
25 | props: {}
26 | });
27 |
28 | export default ST;
29 |
--------------------------------------------------------------------------------
/src/modules/commands.js:
--------------------------------------------------------------------------------
1 | //
2 | // File: commands.js
3 | //
4 | // Description: This file contains the commands object for dealing with commands that
5 | // SvelteFileManager uses.
6 | //
7 |
8 | var commands = {
9 | commandList: [],
10 | lastError: '',
11 | addCommand: function(name, altname, description, command) {
12 | commands.commandList.push({
13 | name: name,
14 | altname: altname,
15 | description: description,
16 | command: command
17 | });
18 | },
19 | runCommand: function(com) {
20 | //
21 | // Get the command.
22 | //
23 | const command = commands.getCommand(com);
24 |
25 | //
26 | // Run the command.
27 | //
28 | try {
29 | if(command !== null) command.command();
30 | } catch(e) {
31 | //
32 | // Something happened in the command. Tell about it.
33 | //
34 | commands.lastError = e;
35 | console.log(e);
36 | }
37 | },
38 | getCommand: function(com) {
39 | return commands.commandList.find(item => item.name === com )
40 | },
41 | getAltCommand: function(com) {
42 | return commands.commandList.find(item => item.altname === com )
43 | },
44 | listCommands: function() {
45 | return commands.commandList.map(item => {
46 | return {
47 | name: item.name,
48 | description: item.description
49 | };
50 | })
51 | },
52 | removeCommand: function(com) {
53 | commands.commandList = commands.commandList.filter(item => item.name !== com);
54 | }
55 | }
56 |
57 | export default commands;
58 |
--------------------------------------------------------------------------------
/src/modules/extensions.js:
--------------------------------------------------------------------------------
1 | //
2 | // File: extensions.js
3 | //
4 | // Description: This object contains the extensions used and interacts with them.
5 | //
6 |
7 | var extensions = {
8 | fileSystems: null,
9 | commands: null,
10 | extCommandList: [],
11 | extensionList: [],
12 | extensionDir: '',
13 | localFS: null,
14 | config: null,
15 | load: function(confg, LFS) {
16 | //
17 | // Load the extensions from the file system. The extension directory
18 | // contains each extension in it's own directory. Each extension should
19 | // have a standard `package.json` that all npm packages have. This json
20 | // will also have fields for the extension as follows:
21 | //
22 | //{
23 | // ...
24 | // mfmextension: {
25 | // name: 'name of the extension',
26 | // description: 'description of the extension',
27 | // type: 0, // This a designation as the extensions origin: 0 - local, 1 - github
28 | // github: 'address to the extension on GitHub',
29 | // main: 'name of the JavaScript file'
30 | // }
31 | //}
32 | //
33 | // Single files in the extensions directory will be ignored.
34 | //
35 | extensions.config = confg;
36 | extensions.localFS = LFS;
37 | try{
38 | var items = extensions.localFS.readDir(extensions.extensionDir);
39 | var stats = [];
40 | for (var i=0; i {
116 | return { name: item.name, description: item.description};
117 | });
118 | },
119 | getExtCommand: function(name) {
120 | return(extensions.extCommandList.find(item => item.name === name));
121 | },
122 | addExtension: function(name, description, extension, type, github) {
123 | //
124 | // Add it to the stack.
125 | //
126 | extensions.extensionList.push({
127 | name: name,
128 | description: description,
129 | extension: extension,
130 | type: type,
131 | github: github
132 | });
133 | },
134 | unloadExtensions: function() {
135 | if(extensions.extensionList !== null) {
136 | extensions.extensionList.forEach(item => {
137 | if((typeof item.extension !== 'undefined')&&(typeof item.extension.unload !== 'undefined')) item.extension.unload();
138 | });
139 | }
140 | },
141 | init: function() {
142 | if(extensions.extensionList !== null) {
143 | extensions.extensionList.forEach(item => {
144 | if(typeof item.extension !== 'undefined') item.extension.init(extensions);
145 | });
146 | }
147 | },
148 | installKeyMaps: function() {
149 | if(extensions.extensionList !== null) {
150 | extensions.extensionList.forEach(item => {
151 | if(typeof item.extension !== 'undefined') item.extension.installKeyMaps();
152 | });
153 | }
154 | },
155 | getExtension: function(ext) {
156 | return extensions.extensionList.find((item) => { item.name == ext });
157 | },
158 | listExtensions: function() {
159 | return extensions.extensionList.map(item => {
160 | return {
161 | name: item.name,
162 | description: item.description
163 | };
164 | })
165 | },
166 | removeExtension: function(ext) {
167 | var exten = extensions.extensionList.filter(item => item.name === ext)[0];
168 | exten.unload();
169 | extensions.extensionList = extensions.extensionList.filter(item => item.name != ext);
170 | },
171 | getLocalFS: function() {
172 | return extensions.localFS;
173 | }
174 | }
175 |
176 | export default extensions;
177 |
178 |
--------------------------------------------------------------------------------
/src/modules/filesystems.js:
--------------------------------------------------------------------------------
1 | //
2 | // File: filesystems.js
3 | //
4 | // Description: This module contains all the supported file system objects.
5 | //
6 |
7 | var filesystems = {
8 | fileSystemList: [],
9 | addFileSystem: function(name, description, fs) {
10 | filesystems.fileSystemList.push({
11 | name: name,
12 | description: description,
13 | fileSystem: fs
14 | });
15 | },
16 | getFileSystem: function(fs) {
17 | return filesystems.fileSystemList.find((item) => { item.name == com })
18 | },
19 | listFileSystems: function() {
20 | return filesystems.fileSystemList.map(item => {
21 | return {
22 | name: item.name,
23 | description: item.description
24 | };
25 | })
26 | },
27 | removeFileSystem: function(fs) {
28 | filesystems.fileSystemList = filesystems.fileSystemList.filter(item => item.name != fs);
29 | }
30 | }
31 |
32 | export default filesystems;
33 |
--------------------------------------------------------------------------------
/src/modules/linux.js:
--------------------------------------------------------------------------------
1 | //
2 | // File: linux.js
3 | //
4 | // Description: This file contains all low level
5 | // functions that would be OS dependent.
6 | //
7 |
8 | const path = require('path');
9 | const os = require('os');
10 | const fs = require('fs');
11 | const childProcess = require('child_process');
12 |
13 | var linux = {
14 | dirFirst: true,
15 | sortFunction: null,
16 | filterFunction: null,
17 | lastError: '',
18 | lastOutput: '',
19 | getExtension: function(file) {
20 | return path.extname(file);
21 | },
22 | getConfigDir: function() {
23 | return this.appendPath(this.getHomeDir(), '.config/modalfilemanager');
24 | },
25 | terminalScript: "bin/openTerminal.scpt",
26 | init: function() {
27 | this.sortFunction = this.alphaSort;
28 | this.filterFunction = this.defaultFilter;
29 | },
30 | getDirFirst: function() {
31 | return dirFirst;
32 | },
33 | setDirFirst: function(flag) {
34 | if(typeof flag === 'boolean') {
35 | this.dirFirst = flag;
36 | }
37 | },
38 | setDirSort: function(sortFunction) {
39 | this.sortFunction = sortFunction;
40 | },
41 | setFilter: function(flt) {
42 | this.filterFunction = flt;
43 | },
44 | getTerminalScript: function() {
45 | return this.appendPath(this.getHomeDir(), this.terminalScript);
46 | },
47 | setTerminalScript: function(scrpt) {
48 | this.terminalScript = scrpt;
49 | },
50 | getHomeDir: function() {
51 | return(os.homedir());
52 | },
53 | pathSep() {
54 | return(path.sep);
55 | },
56 | readDir: function(dir) {
57 | if(typeof dir === 'object') dir = this.appendPath(dir.dir, dir.name);
58 | return fs.readdirSync(this.preserveQuotes(dir));
59 | },
60 | dirExists: function(dir) {
61 | if(typeof dir.name !== 'undefined') dir = this.appendPath(dir.dir, dir.name);
62 | return this.fileExists(this.preserveQuotes(dir));
63 | },
64 | fileExists: function(file) {
65 | var result = true;
66 | if(typeof file === 'object') file = this.preserveQuotes(this.appendPath(file.dir, file.name));
67 | try {
68 | fs.accessSync(file);
69 | result = true;
70 | } catch(e) {
71 | result = false;
72 | }
73 | return result;
74 | },
75 | makeDir: function(dir) {
76 | if(typeof dir.name !== 'undefined') dir = this.appendpath(dir.dir, dir.name);
77 | fs.mkdirSync(this.preserveQuotes(dir));
78 | },
79 | preserveQuotes: function(str) {
80 | return(str.replaceAll(/(\"|\'|\`)/gi,'\\$1'));
81 | },
82 | moveEntries: function(from, to, callback) {
83 | var fromName = this.preserveQuotes(from.fileSystem.appendPath(from.dir, from.name));
84 | var toName = this.preserveQuotes(to.dir);
85 | var that = this;
86 | if(typeof callback === 'undefined') {
87 | childProcess.exec("mv '" + fromName + "' '" + toName + "'",(err,stdout) => {
88 | if(err) that.lastError = err;
89 | that.lastOutput = stdout;
90 | });
91 | } else {
92 | childProcess.exec("mv '" + fromName + "' '" + toName + "'", callback);
93 | }
94 | },
95 | copyEntries: function(from, to, flag, callback) {
96 | if(typeof flag === 'undefined') flag = false;
97 | var fromName = this.preserveQuotes(from.fileSystem.appendPath(from.dir, from.name));
98 | var toName = this.preserveQuotes(to.dir);
99 | if(flag) {
100 | toName = this.preserveQuotes(to.fileSystem.appendPath(to.dir,to.name));
101 | }
102 | var that = this;
103 | if(typeof callback === 'undefined') {
104 | childProcess.exec("cp -R '" + fromName + "' '" + toName + "'",(err,stdout) => {
105 | if(err) that.lastError = err;
106 | that.lastOutput = stdout;
107 | });
108 | } else {
109 | childProcess.exec("cp -R '" + fromName + "' '" + toName + "'", callback);
110 | }
111 | },
112 | deleteEntries: function(entry, callback) {
113 | var item = this.preserveQuotes(entry.fileSystem.appendPath(entry.dir, entry.name));
114 | var that = this;
115 | if(typeof callback === 'undefined') {
116 | childProcess.exec("rm -R '" + item + "'",(err,stdout) => {
117 | if(err) that.lastError = err;
118 | that.lastOutput = stdout;
119 | });
120 | } else {
121 | childProcess.exec("rm -R '" + item + "'", callback);
122 | }
123 | },
124 | getDirList: function(dir) {
125 | //
126 | // A directory list is provided giving an array of entry object. Each
127 | // entry object has:
128 | // name The name of the file
129 | // type The type of the entry: a file - 0, a directory - 1, a link - 2
130 | // fileSystem The current file system object
131 | // dir The directory of the file
132 | // datetime The creation datetime of the file
133 | // size The integer size of the file in 1kb (Directories and links have zero size)
134 | // selected Boolean true is selected, false is not selected
135 | //
136 | var entries = [];
137 | if((typeof dir === 'object') && (typeof dir.name !== 'undefined')) dir = this.appendPath(dir.dir, dir.name);
138 | if(this.dirExists(dir)) {
139 | var items = fs.readdirSync(dir);
140 | for (var i=0; i 1)) {
145 | file = dir + path.sep + items[i];
146 | } else {
147 | file = path.sep + items[i];
148 | }
149 | var newEntry;
150 | try {
151 | var stats = fs.statSync(file);
152 | } catch(e) {
153 | newEntry = {
154 | name: items[i],
155 | dir: dir,
156 | fileSystemType: "macOS",
157 | fileSystem: this,
158 | selected: false,
159 | datetime: '',
160 | type: 0,
161 | size: 0,
162 | stats: null
163 | };
164 | }
165 | newEntry = {
166 | name: items[i],
167 | dir: dir,
168 | fileSystemType: "macOS",
169 | fileSystem: this,
170 | selected: false,
171 | datetime: stats.mtime.toLocaleString(),
172 | type: 0,
173 | size: 0,
174 | stats: stats
175 | };
176 | if(stats.isDirectory()) {
177 | //
178 | // It's a directory.
179 | //
180 | newEntry.type = 1;
181 | newEntry.size = 0;
182 | } else if(stats.isSymbolicLink()) {
183 | //
184 | // It's a link.
185 | //
186 | newEntry.type = 2;
187 | newEntry.size = stats['size'];
188 | } else {
189 | //
190 | // It's a file.
191 | //
192 | newEntry.type = 0;
193 | newEntry.size = stats['size'];
194 | }
195 | entries.push(newEntry);
196 | }
197 | }
198 |
199 | //
200 | // filter out the entries.
201 | //
202 | entries = entries.filter(this.filterFunction);
203 |
204 | //
205 | // Sort the entries.
206 | //
207 | if(this.dirFirst) {
208 | var dirEntries = entries.filter(item => item.type === 1);
209 | var fileEntries = entries.filter(item => item.type === 0);
210 | dirEntries.sort(this.sortFunction);
211 | fileEntries.sort(this.sortFunction);
212 | entries = [...dirEntries, ...fileEntries];
213 | } else {
214 | entries.sort(this.sortFunction);
215 | }
216 | }
217 |
218 | //
219 | // Return the result.
220 | //
221 | return(entries);
222 | },
223 | defaultFilter: function(item) {
224 | return ((item.name[0] !== '.') &&
225 | (!item.name.includes('Icon')));
226 | },
227 | allFilter: function(item) {
228 | //
229 | // Still, don't show the Icon and DS_Store files.
230 | //
231 | return((!item.name.includes('Icon')) &&
232 | (!item.name.includes('.DS_Store')));
233 | },
234 | alphaSort: function(item1, item2) {
235 | const a = item1.name.toLowerCase();
236 | const b = item2.name.toLowerCase();
237 | return a === b ? 0 : a > b ? 1 : -1;
238 | },
239 | openFile: function(dir, file) {
240 | //
241 | // For macOS, open with the open command line command.
242 | //
243 | childProcess.execSync("/usr/bin/open '" + this.appendPath(dir, file) + "'");
244 | },
245 | openFileWithProgram: function(prog,file) {
246 | //
247 | // For macOS, open with the open command line command.
248 | //
249 | childProcess.exec("/usr/bin/open -a '" + prog + "' '" + file + "'", (err, stdin, stdout) => {});
250 | },
251 | openInTerminal: function(prog, file) {
252 | childProcess.exec("/usr/bin/osascript " + this.terminalScript + " '" + prog + " \"" + file + "\"'", (err, stdin, stdout) => {});
253 | },
254 | runCommandLine: function(line, callback) {
255 | if(typeof callback === 'undefined') {
256 | childProcess.exec(line, (err, stdin, stdout) => {});
257 | } else {
258 | childProcess.exec(line, callback);
259 | }
260 | },
261 | appendPath: function(dir, name) {
262 | //
263 | // dir can be an entry or a path string. name is always a string.
264 | //
265 | if(typeof dir === 'object') dir = this.appendPath(dir.dir, dir.name);
266 | if(dir == path.sep) {
267 | return path.sep + name;
268 | } else {
269 | if(dir[dir.length-1] === path.sep) {
270 | return dir + name;
271 | } else {
272 | return dir + path.sep + name;
273 | }
274 | }
275 | },
276 | getStats: function(file) {
277 | return fs.statSync(file);
278 | },
279 | readFile: function(file) {
280 | if(typeof file === 'objct') file = this.appendPath(file.dir, file.name);
281 | return fs.readFileSync(file);
282 | },
283 | writeFile: function(file,data) {
284 | if(typeof file === 'object') file = this.appendPath(file.dir, file.name);
285 | fs.writeFileSync(file,data);
286 | },
287 | renameEntry: function(oldE, newE) {
288 | console.log(oldE);
289 | console.log(newE);
290 | var fromName = this.preserveQuotes(oldE.fileSystem.appendPath(oldE.dir, oldE.name));
291 | var toName = this.preserveQuotes(newE.fileSystem.appendPath(newE.dir, newE.name));
292 | childProcess.execSync('mv "' + fromName + '" "' + toName + '"');
293 | },
294 | createFile: function(file) {
295 | var fnm = this.preserveQuotes(file.fileSystem.appendPath(file.dir, file.name));
296 | childProcess.execSync('touch "' + fnm + '"');
297 | },
298 | createDir: function(dir) {
299 | var dnm = this.preserveQuotes(dir.fileSystem.appendPath(dir.dir, dir.name));
300 | childProcess.execSync('mkdir "' + dnm + '"');
301 | },
302 | loadJavaScript: function(file) {
303 | var result = '';
304 |
305 | //
306 | // Read in the JavaScript file and run it. It should return an extension object.
307 | //
308 | var jfile = this.readFile(file).toString();
309 | try {
310 | var scriptFunction = new Function('',jfile);
311 | result = scriptFunction();
312 | }catch(e) {
313 | console.log(e);
314 | this.lastError = e.toString();
315 | result = null;
316 | }
317 | return(result);
318 | },
319 | searchDir: function(pat, dir, numEntries, returnFunction) {
320 | try {
321 | if(dir === '') dir = this.pathSep();
322 | if(pat !== '') {
323 | childProcess.exec('/usr/local/bin/fd -i --max-results ' + numEntries + ' -t d "' + pat + '" "' + dir + '"', (err, data) => {
324 | if(err) {
325 | console.log(err);
326 | this.lastError = err.toString();
327 | } else {
328 | returnFunction(data.toString().split('\n'));
329 | }
330 | });
331 | }
332 | } catch(e) {
333 | console.log(e);
334 | this.lastError = e.toString();
335 | }
336 | }
337 | }
338 |
339 | export default linux;
340 |
--------------------------------------------------------------------------------
/src/modules/macOS.js:
--------------------------------------------------------------------------------
1 | //
2 | // File: macOS.js
3 | //
4 | // Description: This file contains all low level
5 | // functions that would be OS dependent.
6 | //
7 |
8 | const path = require('path');
9 | const os = require('os');
10 | const fs = require('fs');
11 | const childProcess = require('child_process');
12 |
13 | var macOS = {
14 | dirFirst: true,
15 | sortFunction: null,
16 | filterFunction: null,
17 | lastError: '',
18 | lastOutput: '',
19 | config: null,
20 | type: 'macOS',
21 | setConfig: function(cfg) {
22 | this.config = cfg;
23 | },
24 | getExtension: function(file) {
25 | return path.extname(file);
26 | },
27 | getConfigDir: function() {
28 | return this.appendPath(this.getHomeDir(), '.config/modalfilemanager');
29 | },
30 | terminalScript: "bin/openTerminal.scpt",
31 | init: function() {
32 | this.sortFunction = this.alphaSort;
33 | this.filterFunction = this.defaultFilter;
34 | },
35 | getDirFirst: function() {
36 | return dirFirst;
37 | },
38 | setDirFirst: function(flag) {
39 | if(typeof flag === 'boolean') {
40 | this.dirFirst = flag;
41 | }
42 | },
43 | setDirSort: function(sortFunction) {
44 | this.sortFunction = sortFunction;
45 | },
46 | setFilter: function(flt) {
47 | this.filterFunction = flt;
48 | },
49 | getTerminalScript: function() {
50 | return this.appendPath(this.getHomeDir(), this.terminalScript);
51 | },
52 | setTerminalScript: function(scrpt) {
53 | this.terminalScript = scrpt;
54 | },
55 | getHomeDir: function() {
56 | return(os.homedir());
57 | },
58 | pathSep() {
59 | return(path.sep);
60 | },
61 | readDir: function(dir) {
62 | if(typeof dir === 'object') dir = this.appendPath(dir.dir, dir.name);
63 | return fs.readdirSync(this.preserveQuotes(dir));
64 | },
65 | normalize: function(dir) {
66 | if(dir[0] === '~') dir = this.appendPath(this.getHomeDir(),dir.slice(1,dir.length));
67 | dir = path.normalize(dir);
68 | return(dir);
69 | },
70 | dirExists: function(dir) {
71 | if((typeof dir === 'object') && (typeof dir.name !== 'undefined')) dir = this.appendPath(dir.dir, dir.name);
72 | return this.fileExists(this.preserveQuotes(dir));
73 | },
74 | fileExists: function(file) {
75 | var result = true;
76 | if(typeof file === 'object') file = this.preserveQuotes(this.appendPath(file.dir, file.name));
77 | try {
78 | fs.accessSync(file);
79 | result = true;
80 | } catch(e) {
81 | result = false;
82 | }
83 | return result;
84 | },
85 | makeDir: function(dir) {
86 | if(typeof dir.name !== 'undefined') dir = this.appendpath(dir.dir, dir.name);
87 | fs.mkdirSync(this.preserveQuotes(dir));
88 | },
89 | preserveQuotes: function(str) {
90 | return(str);
91 | },
92 | moveEntries: function(from, to, callback) {
93 | var fromName = this.preserveQuotes(this.appendPath(from.dir, from.name));
94 | var toName = this.preserveQuotes(to.dir);
95 | var that = this;
96 | if(typeof callback === 'undefined') {
97 | childProcess.exec("mv '" + fromName + "' '" + toName + "'",(err,stdout) => {
98 | if(err) that.lastError = err;
99 | that.lastOutput = stdout;
100 | });
101 | } else {
102 | childProcess.exec("mv '" + fromName + "' '" + toName + "'", callback);
103 | }
104 | },
105 | copyEntries: function(from, to, flag, callback) {
106 | if(typeof flag === 'undefined') flag = false;
107 | var fromName = this.preserveQuotes(this.appendPath(from.dir, from.name));
108 | if(typeof from === 'string') fromName = this.preserveQuotes(from);
109 | var toName = this.preserveQuotes(to.dir);
110 | if(typeof to === 'string') toName = this.preserveQuotes(to);
111 | if(flag) {
112 | toName = this.preserveQuotes(to.fileSystem.appendPath(to.dir,to.name));
113 | }
114 | var that = this;
115 | if(typeof callback === 'undefined') {
116 | childProcess.exec("cp -R '" + fromName + "' '" + toName + "'",(err,stdout) => {
117 | if(err) that.lastError = err;
118 | that.lastOutput = stdout;
119 | });
120 | } else {
121 | childProcess.exec("cp -R '" + fromName + "' '" + toName + "'", callback);
122 | }
123 | },
124 | deleteEntries: function(entry, callback) {
125 | var item = this.preserveQuotes(this.appendPath(entry.dir, entry.name));
126 | var that = this;
127 | if(this.config !== null) {
128 | if(this.config.useTrash) {
129 | //
130 | // Use the trashcan on the system.
131 | //
132 | if(typeof callback === 'undefined') {
133 | this.runCommandLine("trash '" + item + "'",(err,stdout) => {
134 | if(err) {
135 | that.lastError = err;
136 | console.log(err);
137 | }
138 | that.lastOutput = stdout;
139 | });
140 | } else {
141 | this.runCommandLine("trash '" + item + "'", callback);
142 | }
143 | } else {
144 | if(typeof callback === 'undefined') {
145 | childProcess.exec("rm -R '" + item + "'",(err,stdout) => {
146 | if(err) that.lastError = err;
147 | that.lastOutput = stdout;
148 | });
149 | } else {
150 | childProcess.exec("rm -R '" + item + "'", callback);
151 | }
152 | }
153 | }
154 | },
155 | getDirList: function(dir) {
156 | //
157 | // A directory list is provided giving an array of entry object. Each
158 | // entry object has:
159 | // name The name of the file
160 | // type The type of the entry: a file - 0, a directory - 1, a link - 2
161 | // fileSystem The current file system object
162 | // dir The directory of the file
163 | // datetime The creation datetime of the file
164 | // size The integer size of the file in 1kb (Directories and links have zero size)
165 | // selected Boolean true is selected, false is not selected
166 | //
167 | var entries = [];
168 | if((typeof dir === 'object') && (typeof dir.name !== 'undefined')) dir = this.appendPath(dir.dir, dir.name);
169 | dir = this.normalize(dir);
170 | if(this.dirExists(dir)) {
171 | var items = fs.readdirSync(dir);
172 | for (var i=0; i 1)) {
177 | file = dir + path.sep + items[i];
178 | } else {
179 | file = path.sep + items[i];
180 | }
181 | var newEntry;
182 | try {
183 | var stats = fs.statSync(file);
184 | } catch(e) {
185 | newEntry = {
186 | name: items[i],
187 | dir: dir,
188 | fileSystemType: "macOS",
189 | fileSystem: this,
190 | selected: false,
191 | datetime: '',
192 | type: 0,
193 | size: 0,
194 | stats: null
195 | };
196 | }
197 | newEntry = {
198 | name: items[i],
199 | dir: dir,
200 | fileSystemType: "macOS",
201 | fileSystem: this,
202 | selected: false,
203 | datetime: stats.mtime.toLocaleString(),
204 | type: 0,
205 | size: 0,
206 | stats: stats
207 | };
208 | if(stats.isDirectory()) {
209 | //
210 | // It's a directory.
211 | //
212 | newEntry.type = 1;
213 | newEntry.size = 0;
214 | } else if(stats.isSymbolicLink()) {
215 | //
216 | // It's a link.
217 | //
218 | newEntry.type = 2;
219 | newEntry.size = stats['size'];
220 | } else {
221 | //
222 | // It's a file.
223 | //
224 | newEntry.type = 0;
225 | newEntry.size = stats['size'];
226 | }
227 | entries.push(newEntry);
228 | }
229 | }
230 |
231 | //
232 | // filter out the entries.
233 | //
234 | entries = entries.filter(this.filterFunction);
235 |
236 | //
237 | // Sort the entries.
238 | //
239 | if(this.dirFirst) {
240 | var dirEntries = entries.filter(item => item.type === 1);
241 | var fileEntries = entries.filter(item => item.type === 0);
242 | dirEntries.sort(this.sortFunction);
243 | fileEntries.sort(this.sortFunction);
244 | entries = [...dirEntries, ...fileEntries];
245 | } else {
246 | entries.sort(this.sortFunction);
247 | }
248 | }
249 |
250 | //
251 | // Return the result.
252 | //
253 | return(entries);
254 | },
255 | defaultFilter: function(item) {
256 | return ((item.name[0] !== '.') &&
257 | (!item.name.includes('Icon')));
258 | },
259 | allFilter: function(item) {
260 | //
261 | // Still, don't show the Icon and DS_Store files.
262 | //
263 | return((!item.name.includes('Icon')) &&
264 | (!item.name.includes('.DS_Store')));
265 | },
266 | alphaSort: function(item1, item2) {
267 | const a = item1.name.toLowerCase();
268 | const b = item2.name.toLowerCase();
269 | return a === b ? 0 : a > b ? 1 : -1;
270 | },
271 | openFile: function(dir, file) {
272 | //
273 | // For macOS, open with the open command line command.
274 | //
275 | childProcess.execSync("/usr/bin/open '" + this.appendPath(dir, file) + "'");
276 | },
277 | openFileWithProgram: function(prog,file) {
278 | //
279 | // For macOS, open with the open command line command.
280 | //
281 | childProcess.exec("/usr/bin/open -a '" + prog + "' '" + file + "'", (err, stdin, stdout) => {});
282 | },
283 | openInTerminal: function(prog, file) {
284 | childProcess.exec("/usr/bin/osascript " + this.terminalScript + " '" + prog + " \"" + file + "\"'", (err, stdin, stdout) => {});
285 | },
286 | getConfig: function() {
287 | if(this.config === null) {
288 | //
289 | // Create the minimum config and then add to the path as needed. The path from process
290 | // will not contain everything the user would have in his shell.
291 | //
292 | this.config = {
293 | env: null,
294 | shell: '',
295 | useTrash: false
296 | };
297 |
298 | //
299 | // Copy the environment from the process.
300 | //
301 | this.config.env = {
302 | ...process.env
303 | };
304 |
305 | //
306 | // Add directories that the user's system should have.
307 | //
308 | if(this.dirExists(this.getHomeDir() + '/bin')) {
309 | this.config.env.PATH = this.getHomeDir() + '/bin:' + this.config.env.PATH;
310 | }
311 | if(this.dirExists('/opt/homebrew/bin')) {
312 | this.config.env.PATH = '/opt/homebrew/bin:' + this.config.env.PATH;
313 | }
314 | if(this.dirExists('/usr/local/bin')) {
315 | this.config.env.PATH = '/usr/local/bin:' + this.config.env.PATH;
316 | }
317 | if(this.dirExists(this.getHomeDir() + '/.cargo/bin')) {
318 | this.config.env.PATH += ':' + this.getHomeDir() + '/.cargo/bin';
319 | }
320 | if(this.dirExists(this.getHomeDir() + '/go/bin')) {
321 | this.config.env.PATH += ':' + this.getHomeDir() + '/go/bin';
322 | }
323 |
324 | //
325 | // Set the defaults for everything else.
326 | //
327 | this.config.shell = this.config.env.SHELL;
328 | this.config.useTrash = true;
329 | }
330 | return(this.config);
331 | },
332 | runCommandLine: function(line, callback, rEnv, rOpt) {
333 | //
334 | // Get the environment to use.
335 | //
336 | var cnfg = this.getConfig();
337 | var nEnv = {...cnfg.env};
338 | if(typeof rEnv !== 'undefined') {
339 | nEnv = {...nEnv, ...rEnv};
340 | }
341 |
342 | var nOpt = {
343 | env: nEnv,
344 | shell: cnfg.shell
345 | };
346 | if(typeof rOpt !== 'undefined') {
347 | nOpt = {...nOpt, ...rOpt};
348 | }
349 |
350 | //
351 | // Run with default callback if a callback wasn't given.
352 | //
353 | if(typeof callback === 'undefined') {
354 | childProcess.exec(line, nOpt, (err, stdin, stdout) => {});
355 | } else {
356 | childProcess.exec(line, nOpt, callback);
357 | }
358 | },
359 | appendPath: function(dir, name) {
360 | //
361 | // dir can be an entry or a path string. name is always a string.
362 | //
363 | if(typeof dir === 'object') dir = this.appendPath(dir.dir, dir.name);
364 | if(dir == path.sep) {
365 | return path.normalize(path.sep + name);
366 | } else {
367 | if(dir[dir.length-1] === path.sep) {
368 | return path.normalize(dir + name);
369 | } else {
370 | return path.normalize(dir + path.sep + name);
371 | }
372 | }
373 | },
374 | getStats: function(file) {
375 | return fs.statSync(file);
376 | },
377 | readFile: function(file) {
378 | if(typeof file === 'objct') file = this.appendPath(file.dir, file.name);
379 | return fs.readFileSync(file);
380 | },
381 | writeFile: function(file,data) {
382 | if(typeof file === 'object') file = this.appendPath(file.dir, file.name);
383 | fs.writeFileSync(file,data);
384 | },
385 | renameEntry: function(oldE, newE) {
386 | var fromName = this.preserveQuotes(this.appendPath(oldE.dir, oldE.name));
387 | var toName = this.preserveQuotes(this.appendPath(newE.dir, newE.name));
388 | childProcess.execSync('mv "' + fromName + '" "' + toName + '"');
389 | },
390 | createFile: function(file) {
391 | var fnm = this.preserveQuotes(this.appendPath(file.dir, file.name));
392 | childProcess.execSync('touch "' + fnm + '"');
393 | },
394 | createDir: function(dir) {
395 | var dnm = dir;
396 | if(typeof dir === 'object') dnm = this.preserveQuotes(this.appendPath(dir.dir, dir.name));
397 | childProcess.execSync('mkdir "' + dnm + '"');
398 | },
399 | loadJavaScript: function(file) {
400 | var result = '';
401 |
402 | //
403 | // Read in the JavaScript file and run it. It should return an extension object.
404 | //
405 | var jfile = this.readFile(file).toString();
406 | try {
407 | var scriptFunction = new Function('',jfile);
408 | result = scriptFunction();
409 | }catch(e) {
410 | console.log(e);
411 | this.lastError = e.toString();
412 | result = null;
413 | }
414 | return(result);
415 | },
416 | searchDir: function(pat, dir, numEntries, returnFunction) {
417 | try {
418 | if(dir === '') dir = this.pathSep();
419 | if(pat !== '') {
420 | this.runCommandLine('fd -i --max-results ' + numEntries + ' -t d "' + pat + '" "' + dir + '"', (err, data) => {
421 | if(err) {
422 | console.log(err);
423 | this.lastError = err.toString();
424 | } else {
425 | returnFunction(data.toString().split('\n'));
426 | }
427 | });
428 | }
429 | } catch(e) {
430 | console.log(e);
431 | this.lastError = e.toString();
432 | }
433 | },
434 | splitFilePath: function(filePath) {
435 | return({
436 | dir: path.dirname(filePath),
437 | name: path.basename(filePath)
438 | });
439 | }
440 | }
441 |
442 | export default macOS;
443 |
--------------------------------------------------------------------------------
/src/modules/util.js:
--------------------------------------------------------------------------------
1 | //
2 | // File: util.js
3 | //
4 | // Description: This file contains utility functions used in the project.
5 | //
6 |
7 | var util = {
8 | readableSize: function(sz) {
9 | var size = sz;
10 | if(size < 1024) {
11 | size = size + ' Bytes';
12 | } else {
13 | size = size / 1024;
14 | if(size < 1024) {
15 | size = size.toFixed(2) + " KBytes";
16 | } else {
17 | size = size / 1024;
18 | if(size < 1024) {
19 | size = size.toFixed(2) + " MBytes";
20 | } else {
21 | size = size / 1024;
22 | size = size.toFixed(2) + " GBytes";
23 | }
24 | }
25 | }
26 | return(size);
27 | },
28 | //
29 | // Taken from:
30 | // https://stackoverflow.com/questions/5560248/programmatically-lighten-or-darken-a-hex-color-or-rgb-and-blend-colors
31 | //
32 | pSBC: function(p,c0,c1,l) {
33 | let r,g,b,P,f,t,h,i=parseInt,m=Math.round,a=typeof(c1)=="string";
34 | if(typeof(p)!="number"||p<-1||p>1||typeof(c0)!="string"||(c0[0]!='r'&&c0[0]!='#')||(c1&&!a))return null;
35 | if(!this.pSBCr)this.pSBCr=(d)=>{
36 | let n=d.length,x={};
37 | if(n>9){
38 | [r,g,b,a]=d=d.split(","),n=d.length;
39 | if(n<3||n>4)return null;
40 | x.r=i(r[3]=="a"?r.slice(5):r.slice(4)),x.g=i(g),x.b=i(b),x.a=a?parseFloat(a):-1
41 | }else{
42 | if(n==8||n==6||n<4)return null;
43 | if(n<6)d="#"+d[1]+d[1]+d[2]+d[2]+d[3]+d[3]+(n>4?d[4]+d[4]:"");
44 | d=i(d.slice(1),16);
45 | if(n==9||n==5)x.r=d>>24&255,x.g=d>>16&255,x.b=d>>8&255,x.a=m((d&255)/0.255)/1000;
46 | else x.r=d>>16,x.g=d>>8&255,x.b=d&255,x.a=-1
47 | }return x};
48 | h=c0.length>9,h=a?c1.length>9?true:c1=="c"?!h:false:h,f=this.pSBCr(c0),P=p<0,t=c1&&c1!="c"?this.pSBCr(c1):P?{r:0,g:0,b:0,a:-1}:{r:255,g:255,b:255,a:-1},p=P?p*-1:p,P=1-p;
49 | if(!f||!t)return null;
50 | if(l)r=m(P*f.r+p*t.r),g=m(P*f.g+p*t.g),b=m(P*f.b+p*t.b);
51 | else r=m((P*f.r**2+p*t.r**2)**0.5),g=m((P*f.g**2+p*t.g**2)**0.5),b=m((P*f.b**2+p*t.b**2)**0.5);
52 | a=f.a,t=t.a,f=a>=0||t>=0,a=f?a<0?t:t<0?a:a*P+t*p:0;
53 | if(h)return"rgb"+(f?"a(":"(")+r+","+g+","+b+(f?","+m(a*1000)/1000:"")+")";
54 | else return"#"+(4294967296+r*16777216+g*65536+b*256+(f?m(a*255):0)).toString(16).slice(1,f?undefined:-2)
55 | }
56 | }
57 |
58 | export default util;
59 |
--------------------------------------------------------------------------------
/src/modules/windows.js:
--------------------------------------------------------------------------------
1 | //
2 | // File: windows.js
3 | //
4 | // Description: This file contains all low level
5 | // functions that would be OS dependent.
6 | //
7 |
8 | const path = require('path');
9 | const os = require('os');
10 | const fs = require('fs');
11 | const childProcess = require('child_process');
12 |
13 | var windows = {
14 | dirFirst: true,
15 | sortFunction: null,
16 | filterFunction: null,
17 | lastError: '',
18 | lastOutput: '',
19 | getExtension: function(file) {
20 | return path.extname(file);
21 | },
22 | getConfigDir: function() {
23 | return this.appendPath(this.getHomeDir(), '.config/modalfilemanager');
24 | },
25 | terminalScript: "bin/openTerminal.scpt",
26 | init: function() {
27 | this.sortFunction = this.alphaSort;
28 | this.filterFunction = this.defaultFilter;
29 | },
30 | getDirFirst: function() {
31 | return dirFirst;
32 | },
33 | setDirFirst: function(flag) {
34 | if(typeof flag === 'boolean') {
35 | this.dirFirst = flag;
36 | }
37 | },
38 | setDirSort: function(sortFunction) {
39 | this.sortFunction = sortFunction;
40 | },
41 | setFilter: function(flt) {
42 | this.filterFunction = flt;
43 | },
44 | getTerminalScript: function() {
45 | return this.appendPath(this.getHomeDir(), this.terminalScript);
46 | },
47 | setTerminalScript: function(scrpt) {
48 | this.terminalScript = scrpt;
49 | },
50 | getHomeDir: function() {
51 | return(os.homedir());
52 | },
53 | pathSep() {
54 | return(path.sep);
55 | },
56 | readDir: function(dir) {
57 | if(typeof dir === 'object') dir = this.appendPath(dir.dir, dir.name);
58 | return fs.readdirSync(this.preserveQuotes(dir));
59 | },
60 | dirExists: function(dir) {
61 | if(typeof dir.name !== 'undefined') dir = this.appendPath(dir.dir, dir.name);
62 | return this.fileExists(this.preserveQuotes(dir));
63 | },
64 | fileExists: function(file) {
65 | var result = true;
66 | if(typeof file === 'object') file = this.preserveQuotes(this.appendPath(file.dir, file.name));
67 | try {
68 | fs.accessSync(file);
69 | result = true;
70 | } catch(e) {
71 | result = false;
72 | }
73 | return result;
74 | },
75 | makeDir: function(dir) {
76 | if(typeof dir.name !== 'undefined') dir = this.appendpath(dir.dir, dir.name);
77 | fs.mkdirSync(this.preserveQuotes(dir));
78 | },
79 | preserveQuotes: function(str) {
80 | return(str.replaceAll(/(\"|\'|\`)/gi,'\\$1'));
81 | },
82 | moveEntries: function(from, to, callback) {
83 | var fromName = this.preserveQuotes(from.fileSystem.appendPath(from.dir, from.name));
84 | var toName = this.preserveQuotes(to.dir);
85 | var that = this;
86 | if(typeof callback === 'undefined') {
87 | childProcess.exec("mv '" + fromName + "' '" + toName + "'",(err,stdout) => {
88 | if(err) that.lastError = err;
89 | that.lastOutput = stdout;
90 | });
91 | } else {
92 | childProcess.exec("mv '" + fromName + "' '" + toName + "'", callback);
93 | }
94 | },
95 | copyEntries: function(from, to, flag, callback) {
96 | if(typeof flag === 'undefined') flag = false;
97 | var fromName = this.preserveQuotes(from.fileSystem.appendPath(from.dir, from.name));
98 | var toName = this.preserveQuotes(to.dir);
99 | if(flag) {
100 | toName = this.preserveQuotes(to.fileSystem.appendPath(to.dir,to.name));
101 | }
102 | var that = this;
103 | if(typeof callback === 'undefined') {
104 | childProcess.exec("cp -R '" + fromName + "' '" + toName + "'",(err,stdout) => {
105 | if(err) that.lastError = err;
106 | that.lastOutput = stdout;
107 | });
108 | } else {
109 | childProcess.exec("cp -R '" + fromName + "' '" + toName + "'", callback);
110 | }
111 | },
112 | deleteEntries: function(entry, callback) {
113 | var item = this.preserveQuotes(entry.fileSystem.appendPath(entry.dir, entry.name));
114 | var that = this;
115 | if(typeof callback === 'undefined') {
116 | childProcess.exec("rm -R '" + item + "'",(err,stdout) => {
117 | if(err) that.lastError = err;
118 | that.lastOutput = stdout;
119 | });
120 | } else {
121 | childProcess.exec("rm -R '" + item + "'", callback);
122 | }
123 | },
124 | getDirList: function(dir) {
125 | //
126 | // A directory list is provided giving an array of entry object. Each
127 | // entry object has:
128 | // name The name of the file
129 | // type The type of the entry: a file - 0, a directory - 1, a link - 2
130 | // fileSystem The current file system object
131 | // dir The directory of the file
132 | // datetime The creation datetime of the file
133 | // size The integer size of the file in 1kb (Directories and links have zero size)
134 | // selected Boolean true is selected, false is not selected
135 | //
136 | var entries = [];
137 | if((typeof dir === 'object') && (typeof dir.name !== 'undefined')) dir = this.appendPath(dir.dir, dir.name);
138 | if(this.dirExists(dir)) {
139 | var items = fs.readdirSync(dir);
140 | for (var i=0; i 1)) {
145 | file = dir + path.sep + items[i];
146 | } else {
147 | file = path.sep + items[i];
148 | }
149 | var newEntry;
150 | try {
151 | var stats = fs.statSync(file);
152 | } catch(e) {
153 | newEntry = {
154 | name: items[i],
155 | dir: dir,
156 | fileSystemType: "macOS",
157 | fileSystem: this,
158 | selected: false,
159 | datetime: '',
160 | type: 0,
161 | size: 0,
162 | stats: null
163 | };
164 | }
165 | newEntry = {
166 | name: items[i],
167 | dir: dir,
168 | fileSystemType: "macOS",
169 | fileSystem: this,
170 | selected: false,
171 | datetime: stats.mtime.toLocaleString(),
172 | type: 0,
173 | size: 0,
174 | stats: stats
175 | };
176 | if(stats.isDirectory()) {
177 | //
178 | // It's a directory.
179 | //
180 | newEntry.type = 1;
181 | newEntry.size = 0;
182 | } else if(stats.isSymbolicLink()) {
183 | //
184 | // It's a link.
185 | //
186 | newEntry.type = 2;
187 | newEntry.size = stats['size'];
188 | } else {
189 | //
190 | // It's a file.
191 | //
192 | newEntry.type = 0;
193 | newEntry.size = stats['size'];
194 | }
195 | entries.push(newEntry);
196 | }
197 | }
198 |
199 | //
200 | // filter out the entries.
201 | //
202 | entries = entries.filter(this.filterFunction);
203 |
204 | //
205 | // Sort the entries.
206 | //
207 | if(this.dirFirst) {
208 | var dirEntries = entries.filter(item => item.type === 1);
209 | var fileEntries = entries.filter(item => item.type === 0);
210 | dirEntries.sort(this.sortFunction);
211 | fileEntries.sort(this.sortFunction);
212 | entries = [...dirEntries, ...fileEntries];
213 | } else {
214 | entries.sort(this.sortFunction);
215 | }
216 | }
217 |
218 | //
219 | // Return the result.
220 | //
221 | return(entries);
222 | },
223 | defaultFilter: function(item) {
224 | return ((item.name[0] !== '.') &&
225 | (!item.name.includes('Icon')));
226 | },
227 | allFilter: function(item) {
228 | //
229 | // Still, don't show the Icon and DS_Store files.
230 | //
231 | return((!item.name.includes('Icon')) &&
232 | (!item.name.includes('.DS_Store')));
233 | },
234 | alphaSort: function(item1, item2) {
235 | const a = item1.name.toLowerCase();
236 | const b = item2.name.toLowerCase();
237 | return a === b ? 0 : a > b ? 1 : -1;
238 | },
239 | openFile: function(dir, file) {
240 | //
241 | // For macOS, open with the open command line command.
242 | //
243 | childProcess.execSync("/usr/bin/open '" + this.appendPath(dir, file) + "'");
244 | },
245 | openFileWithProgram: function(prog,file) {
246 | //
247 | // For macOS, open with the open command line command.
248 | //
249 | childProcess.exec("/usr/bin/open -a '" + prog + "' '" + file + "'", (err, stdin, stdout) => {});
250 | },
251 | openInTerminal: function(prog, file) {
252 | childProcess.exec("/usr/bin/osascript " + this.terminalScript + " '" + prog + " \"" + file + "\"'", (err, stdin, stdout) => {});
253 | },
254 | runCommandLine: function(line, callback) {
255 | if(typeof callback === 'undefined') {
256 | childProcess.exec(line, (err, stdin, stdout) => {});
257 | } else {
258 | childProcess.exec(line, callback);
259 | }
260 | },
261 | appendPath: function(dir, name) {
262 | //
263 | // dir can be an entry or a path string. name is always a string.
264 | //
265 | if(typeof dir === 'object') dir = this.appendPath(dir.dir, dir.name);
266 | if(dir == path.sep) {
267 | return path.sep + name;
268 | } else {
269 | if(dir[dir.length-1] === path.sep) {
270 | return dir + name;
271 | } else {
272 | return dir + path.sep + name;
273 | }
274 | }
275 | },
276 | getStats: function(file) {
277 | return fs.statSync(file);
278 | },
279 | readFile: function(file) {
280 | if(typeof file === 'objct') file = this.appendPath(file.dir, file.name);
281 | return fs.readFileSync(file);
282 | },
283 | writeFile: function(file,data) {
284 | if(typeof file === 'object') file = this.appendPath(file.dir, file.name);
285 | fs.writeFileSync(file,data);
286 | },
287 | renameEntry: function(oldE, newE) {
288 | console.log(oldE);
289 | console.log(newE);
290 | var fromName = this.preserveQuotes(oldE.fileSystem.appendPath(oldE.dir, oldE.name));
291 | var toName = this.preserveQuotes(newE.fileSystem.appendPath(newE.dir, newE.name));
292 | childProcess.execSync('mv "' + fromName + '" "' + toName + '"');
293 | },
294 | createFile: function(file) {
295 | var fnm = this.preserveQuotes(file.fileSystem.appendPath(file.dir, file.name));
296 | childProcess.execSync('touch "' + fnm + '"');
297 | },
298 | createDir: function(dir) {
299 | var dnm = this.preserveQuotes(dir.fileSystem.appendPath(dir.dir, dir.name));
300 | childProcess.execSync('mkdir "' + dnm + '"');
301 | },
302 | loadJavaScript: function(file) {
303 | var result = '';
304 |
305 | //
306 | // Read in the JavaScript file and run it. It should return an extension object.
307 | //
308 | var jfile = this.readFile(file).toString();
309 | try {
310 | var scriptFunction = new Function('',jfile);
311 | result = scriptFunction();
312 | }catch(e) {
313 | console.log(e);
314 | this.lastError = e.toString();
315 | result = null;
316 | }
317 | return(result);
318 | },
319 | searchDir: function(pat, dir, numEntries, returnFunction) {
320 | try {
321 | if(dir === '') dir = this.pathSep();
322 | if(pat !== '') {
323 | childProcess.exec('/usr/local/bin/fd -i --max-results ' + numEntries + ' -t d "' + pat + '" "' + dir + '"', (err, data) => {
324 | if(err) {
325 | console.log(err);
326 | this.lastError = err.toString();
327 | } else {
328 | returnFunction(data.toString().split('\n'));
329 | }
330 | });
331 | }
332 | } catch(e) {
333 | console.log(e);
334 | this.lastError = e.toString();
335 | }
336 | }
337 | }
338 |
339 | export default windows;
340 |
--------------------------------------------------------------------------------
/src/stores/altKey.js:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 |
3 | export const altKey = writable(false);
4 |
--------------------------------------------------------------------------------
/src/stores/config.js:
--------------------------------------------------------------------------------
1 | import { writable, get } from 'svelte/store';
2 |
3 | var confg = {
4 | configDir: '',
5 | localFS: {},
6 | configuration: {},
7 | commands: {},
8 | extensions: {},
9 | userEditor: ''
10 | }
11 |
12 | export const config = writable(confg);
13 |
14 |
--------------------------------------------------------------------------------
/src/stores/ctrlKey.js:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 |
3 | export const ctrlKey = writable(false);
4 |
--------------------------------------------------------------------------------
/src/stores/currentCursor.js:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 |
3 | export const currentCursor = writable({
4 | pane: 'right',
5 | entry: {}
6 | });
7 |
8 |
--------------------------------------------------------------------------------
/src/stores/currentLeftFile.js:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 |
3 | export const currentLeftFile = writable({
4 | entry: {}
5 | });
6 |
7 |
--------------------------------------------------------------------------------
/src/stores/currentRightFile.js:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 |
3 | export const currentRightFile = writable({
4 | entry: {}
5 | });
6 |
7 |
--------------------------------------------------------------------------------
/src/stores/dirHistory.js:
--------------------------------------------------------------------------------
1 | import { writable, get } from 'svelte/store';
2 | import { config } from '../stores/config.js';
3 |
4 | var history = {
5 | histStore: [],
6 | addHistory: function(dir) {
7 | var el = this.histStore.find(item => item.toLowerCase().includes(dir.toLowerCase()));
8 | if((typeof el === 'undefined')||(el === null)) {
9 | this.histStore.push(dir);
10 | this.saveHistory();
11 | }
12 | },
13 | searchHistory: function(pat) {
14 | return this.histStore.filter(item => item.toLowerCase().includes(pat.toLowerCase()));
15 | },
16 | saveHistory: function() {
17 | const cfg = get(config);
18 | if((cfg.configDir !== '')&&(cfg.localFS !== null)) {
19 | //
20 | // Save the history.
21 | //
22 | cfg.localFS.writeFile(cfg.localFS.appendPath(cfg.configDir, 'history.json'), JSON.stringify(this.histStore));
23 | }
24 | },
25 | loadHistory: function() {
26 | const cfg = get(config);
27 | const hf = cfg.localFS.appendPath(cfg.configDir, 'history.json');
28 | if((cfg.configDir !== '')&&(cfg.localFS !== null)) {
29 | //
30 | // load the history.
31 | //
32 | if(cfg.localFS.fileExists(hf)) {
33 | try {
34 | this.histStore = JSON.parse(cfg.localFS.readFile(cfg.localFS.appendPath(cfg.configDir, 'history.json')));
35 | } catch(e) {
36 | //
37 | // Something was wrong with the history. Just forget it.
38 | //
39 | this.histStore = [];
40 | }
41 | }
42 | }
43 | }
44 | }
45 |
46 | export const dirHistory = writable(history);
47 |
48 |
--------------------------------------------------------------------------------
/src/stores/directoryListeners.js:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 |
3 | export const directoryListeners = writable([]);
4 |
--------------------------------------------------------------------------------
/src/stores/extraPanel.js:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 |
3 | export const extraPanel = writable([]);
4 |
--------------------------------------------------------------------------------
/src/stores/inputState.js:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 |
3 | export const inputState = writable('normal');
4 |
5 |
--------------------------------------------------------------------------------
/src/stores/keyProcess.js:
--------------------------------------------------------------------------------
1 | //
2 | // KeyProcess
3 | //
4 | // Description:
5 | // When true, keypresses will be processed for functionality. Otherwise,
6 | // they will be treated as standard typing.
7 | //
8 |
9 | import { writable } from 'svelte/store';
10 |
11 | export const keyProcess = writable(true);
12 |
13 |
--------------------------------------------------------------------------------
/src/stores/leftDir.js:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 |
3 | export const leftDir = writable({
4 | fileSystemType: 'local',
5 | fileSystem: null,
6 | path: ''
7 | });
8 |
9 |
--------------------------------------------------------------------------------
/src/stores/metaKey.js:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 |
3 | export const metaKey = writable(false);
4 |
--------------------------------------------------------------------------------
/src/stores/processKey.js:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 |
3 | export const processKey = writable(null);
4 |
--------------------------------------------------------------------------------
/src/stores/rightDir.js:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 |
3 | export const rightDir = writable({
4 | fileSystemType: 'local',
5 | fileSystem: null,
6 | path: ''
7 | });
8 |
9 |
--------------------------------------------------------------------------------
/src/stores/shiftKey.js:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 |
3 | export const shiftKey = writable(false);
4 |
--------------------------------------------------------------------------------
/src/stores/skipKey.js:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 |
3 | export const skipKey = writable(false);
4 |
--------------------------------------------------------------------------------
/src/stores/stateMapColors.js:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 |
3 | export const stateMapColors = writable([]);
4 |
5 |
--------------------------------------------------------------------------------
/src/stores/theme.js:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store';
2 |
3 | export const theme = writable({
4 | });
5 |
6 |
--------------------------------------------------------------------------------