Repository: JumpFm/jumpfm
Branch: master
Commit: c89609281b56
Files: 41
Total size: 84.8 KB
Directory structure:
gitextract_g_cm2yzf/
├── .gitignore
├── .travis.yml
├── .vscode/
│ └── cSpell.json
├── README.md
├── app.js
├── appveyor.yml
├── build/
│ └── superhero.png.icns
├── index.html
├── misc/
│ ├── ascii.txt
│ ├── cookbook.txt
│ ├── icons.awk
│ ├── icons.txt
│ ├── init-demo.sh
│ ├── link-all.sh
│ ├── modd.conf
│ ├── omg-ubunut.txt
│ └── reddit.txt
├── package.json
├── scss/
│ ├── _app.scss
│ ├── _classes.scss
│ ├── _colors.scss
│ ├── _dialog.scss
│ ├── _panel.scss
│ ├── _panels.scss
│ ├── _statusbar.scss
│ ├── _tooltip.scss
│ └── index.scss
├── ts/
│ ├── Dialog.ts
│ ├── Filter.ts
│ ├── Item.ts
│ ├── JumpFm.ts
│ ├── Panel.ts
│ ├── PluginManager.ts
│ ├── Settings.ts
│ ├── StatusBar.ts
│ ├── files.ts
│ ├── icons.ts
│ ├── main.ts
│ └── shortway.ts
├── tsconfig.json
└── updates.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
node_modules/
dist/
.sass-cache/
css
_config.yml
js
*.swp
old
*.log
================================================
FILE: .travis.yml
================================================
sudo: required
dist: trusty
language: node_js
node_js: 6
branches:
only:
- master
before_install:
- npm i -g npm@latest
- npm i -g typescript
- npm i -g node-sass
install:
- npm i
cache:
directories:
- "node_modules"
env:
global:
- secure: >-
QdHr5mVFKOyS/SmQPd7X6TOGhi4XhlJU+2w0O+RubOo/h2i1wLXD/f0DbAjEc0XfpTBSt9IDtUtqSuTi1YIUbafrqUwLFBUZ5VGYsPcMA48pD6+nzflMuaaCf//+pM8WtIAfgOK1i5k30faRzRnvSIYgX/Yyf4vS9j9OmfwD6342d1eD2b+h24R9z+eLlG9QaoJx9EAYhr1UAFbERiBlN40ThGU7uSkehzuYtGaQil8J8cV/MRqPCTSMAcZvpiUM7yGri11AWxJ5bOmyn7yVTFRBVWZDb4XaREXVTaEIXhvi/g+NO0e9WFETkw2cR7/Q6lw3kr/DqGz6Ks9XCOGppb1xILbvkUID/7pW+UKuzt6hc4AdoJLR+p0QtMWQn51oh1npGXzaMwjGGQ36gr+rdzc3Lukh08URzAFC2kywCg1sCsNCfoEH0hVD8OUhNQIkVOz530isjkZ8q9S1Us0ib5uviSZeA9XmfPBKe7aRxMY98WUvaeDCRmHqtmWy0nl9+ys4aI0197+4XlXijWeezd/ESLcWlZozKysAvTavC+hZPIBYz3Hpd6JOVzDGDgxWTpF91nCOJ+/gz/kHztAcaIC/iIVwIH4NhovuZ8t0kCKraEOWCnVkrikw9yK8unxgQfGWqU38ywUR7dN5if+u6Y67ih4ASnJ/LCqvUQvzy1o=
before_script:
- tsc
- node-sass scss -o css
script:
- npm run publish
================================================
FILE: .vscode/cSpell.json
================================================
// cSpell Settings
{
// Version of the setting file. Always 0.1
"version": "0.1",
// language - current active spelling language
"language": "en",
// words - list of words to be always considered correct
"words": [
"Msgs",
"jumpfm",
"keyboardjs",
"keycode",
"pagedown",
"pageup",
"shortway"
],
// flagWords - list of words to be always considered incorrect
// This is useful for offensive words and common spelling errors.
// For example "hte" should be "the"
"flagWords": [
"hte"
]
}
================================================
FILE: README.md
================================================
[](https://travis-ci.org/Gilad-Kutiel-App/jumpfm) [](https://ci.appveyor.com/project/gkutiel/jumpfm)
# About
JumpFm is a cross platform1,2 dual panel file manager with builtin superpowers.
----
1
Windows built is not tested at all, install it at your own risk
2
For a Mac release see [this fork](https://github.com/heywoodlh/jumpfm)
# \
## tl;dr.1,2
```
git clone git@github.com:Gilad-Kutiel-App/jumpfm.git
npm i -g typescript electron
cd jumpfm
npm i
tsc -w
sass --watch scss:css
electron .
```
---
1
You might want to [change npm's default directory](https://docs.npmjs.com/getting-started/fixing-npm-permissions#option-2-change-npms-default-directory-to-another-directory)
2
Use different terminal for each of ```tsc -w```, ```sass --watch scss:css```, ```electron .```
## More \
JumpFm is an [Electron](https://electron.atom.io/) based app.
It is written in [TypeScript](https://www.typescriptlang.org/).
To hack the code all you need is [node.js](https://nodejs.org/en/) a
[decent editor](http://bit.ly/2wHIoSz) and a [sass compiler](http://sass-lang.com/).
This is how your terminal should looks like:

================================================
FILE: app.js
================================================
const updater = require('electron-simple-updater');
const electron = require('electron')
const app = electron.app
const BrowserWindow = electron.BrowserWindow
const path = require('path')
const url = require('url')
// const log = require('electron-log');
app.on('window-all-closed', function() {
if (process.platform != 'darwin') {
app.quit();
}
});
app.on('ready', function() {
updater.init(
'https://raw.githubusercontent.com/Gilad-Kutiel-App/jumpfm/master/updates.json'
);
const { width, height } = electron.screen.getPrimaryDisplay().workAreaSize;
const w = parseInt(width * .8);
const h = parseInt(height * .8);
global.argv = process.argv
// log.transports.file.level = 'debug';
// log.transports.console.level = 'debug';
// Create the browser window.
let mainWindow = new BrowserWindow({
width: w,
height: h,
icon: path.join(__dirname, 'build/icons/64x64.png'),
});
// mainWindow.setMenu(null);
mainWindow.loadURL('file://' + __dirname + '/index.html');
mainWindow.on('closed', function() {
mainWindow = null;
});
});
================================================
FILE: appveyor.yml
================================================
os: unstable
branches:
only:
- master
cache:
- node_modules
environment:
GH_TOKEN:
secure: RcYdy07RY6uQXRBSJWfJfNG7FzZd7Y62IOTQ1n1pYjKTh9sgklNfNB9DBHZdOCzA
matrix:
- nodejs_version: 6
install:
- ps: Install-Product node $env:nodejs_version
- set CI=true
- npm install -g npm@latest
- npm i -g typescript
- npm i -g node-sass
- set PATH=%APPDATA%\npm;%PATH%
- npm install
matrix:
fast_finish: true
build: off
version: '{build}'
shallow_clone: true
clone_depth: 1
test_script:
- tsc
- node-sass scss -o css
- npm run publish
================================================
FILE: index.html
================================================
JumpFm
================================================
FILE: misc/ascii.txt
================================================
_ ______
| | | ____|
| |_ _ _ __ ___ _ __ | |__ _ __ ___
_ | | | | | '_ ` _ \| '_ \| __| '_ ` _ \
| |__| | |_| | | | | | | |_) | | | | | | | |
\____/ \__,_|_| |_| |_| .__/|_| |_| |_| |_|
| |
|_|
================================================
FILE: misc/cookbook.txt
================================================
find {{pwd}} -name {{input}}
find {{sel}} -name {{input}}
cat ~/favorits.txt
================================================
FILE: misc/icons.awk
================================================
#!/usr/bin/awk -f
BEGIN {
RS="}"
print "{"
}
match($0, /icon: '([^']+)'.*extensions: (\[[^\].]+\])/, a){
print "\""a[1]"\"", ":", gensub(/'/,"\"", "g", a[2])","
}
match($0, /icon: '([^']+)'.*languages: (\[[^\]]+\])/, a){
print "\""a[1]"\"", ":", gensub(/languages\.(\w+)/, "\"\\1\"", "g", a[2])","
}
END {
print "}"
}
================================================
FILE: misc/icons.txt
================================================
/* tslint:disable max-line-length */
import { languages } from './languages';
import { FileFormat, IFileCollection } from '../models';
export const extensions: IFileCollection = {
default: {
file: { icon: 'file', format: FileFormat.svg },
},
supported: [
{ icon: 'access', extensions: ['accdb', 'accdt', 'mdb', 'accda', 'accdc', 'accde', 'accdp', 'accdr', 'accdu', 'ade', 'adp', 'laccdb', 'ldb', 'mam', 'maq', 'mdw'], format: FileFormat.svg },
{ icon: 'actionscript', extensions: [], languages: [languages.actionscript], format: FileFormat.svg },
{ icon: 'ai', extensions: ['ai'], format: FileFormat.svg },
{ icon: 'ai2', extensions: ['ai'], format: FileFormat.svg, disabled: true },
{ icon: 'org', extensions: ['org'], format: FileFormat.svg },
{ icon: 'angular', extensions: ['.angular-cli.json', 'angular-cli.json'], filename: true, format: FileFormat.svg },
{ icon: 'ng_component_ts', extensions: ['component.ts'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_component_js', extensions: ['component.js'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_controller_ts', extensions: ['controller.ts'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_controller_js', extensions: ['controller.js'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_directive_ts', extensions: ['directive.ts'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_directive_js', extensions: ['directive.js'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_guard_ts', extensions: ['guard.ts'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_guard_js', extensions: ['guard.js'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_module_ts', extensions: ['module.ts'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_module_js', extensions: ['module.js'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_pipe_ts', extensions: ['pipe.ts'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_pipe_js', extensions: ['pipe.js'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_routing_ts', extensions: ['routing.ts'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_routing_js', extensions: ['routing.js'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_routing_ts', extensions: ['app-routing.module.ts'], filename: true, format: FileFormat.svg, disabled: true },
{ icon: 'ng_routing_js', extensions: ['app-routing.module.js'], filename: true, format: FileFormat.svg, disabled: true },
{ icon: 'ng_smart_component_ts', extensions: ['page.ts', 'container.ts'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_smart_component_js', extensions: ['page.js', 'container.js'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_service_ts', extensions: ['service.ts'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_service_js', extensions: ['service.js'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_component_ts2', extensions: ['component.ts'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_component_js2', extensions: ['component.js'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_directive_ts2', extensions: ['directive.ts'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_directive_js2', extensions: ['directive.js'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_module_ts2', extensions: ['module.ts'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_module_js2', extensions: ['module.js'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_pipe_ts2', extensions: ['pipe.ts'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_pipe_js2', extensions: ['pipe.js'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_routing_ts2', extensions: ['routing.ts'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_routing_js2', extensions: ['routing.js'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_routing_ts2', extensions: ['app-routing.module.ts'], filename: true, format: FileFormat.svg, disabled: true },
{ icon: 'ng_routing_js2', extensions: ['app-routing.module.js'], filename: true, format: FileFormat.svg, disabled: true },
{ icon: 'ng_smart_component_ts2', extensions: ['page.ts', 'container.ts'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_smart_component_js2', extensions: ['page.js', 'container.js'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_service_ts2', extensions: ['service.ts'], format: FileFormat.svg, disabled: true },
{ icon: 'ng_service_js2', extensions: ['service.js'], format: FileFormat.svg, disabled: true },
{ icon: 'ansible', extensions: [], languages: [languages.ansible], format: FileFormat.svg },
{ icon: 'anyscript', extensions: [], languages: [languages.anyscript], format: FileFormat.svg },
{ icon: 'apache', extensions: [], languages: [languages.apache], format: FileFormat.svg },
{ icon: 'apib', extensions: [], languages: [languages.apib], format: FileFormat.svg },
{ icon: 'applescript', extensions: [], languages: [languages.applescript], format: FileFormat.svg },
{ icon: 'appveyor', extensions: ['appveyor.yml', '.appveyor.yml'], filename: true, format: FileFormat.svg },
{ icon: 'asp', extensions: [], languages: [languages.asp], format: FileFormat.svg },
{ icon: 'aspx', extensions: ['aspx', 'ascx'], format: FileFormat.svg },
{ icon: 'assembly', extensions: [], languages: [languages.assembly], format: FileFormat.svg },
{ // https://en.wikipedia.org/wiki/Audio_file_format
icon: 'audio',
extensions: ['aac', 'act', 'aiff', 'amr', 'ape', 'au', 'dct', 'dss', 'dvf', 'flac', 'gsm',
'iklax', 'ivs', 'm4a', 'm4b', 'm4p', 'mmf', 'mogg', 'mp3', 'mpc', 'msv', 'oga', 'ogg', 'opus',
'ra', 'raw', 'tta', 'vox', 'wav', 'wma'],
format: FileFormat.svg,
},
{ icon: 'aurelia', extensions: ['aurelia.json'], filename: true, format: FileFormat.svg },
{ icon: 'autohotkey', extensions: [], languages: [languages.autohotkey], format: FileFormat.svg },
{ icon: 'autoit', extensions: [], languages: [languages.autoit], format: FileFormat.svg },
{ icon: 'aws', extensions: [], format: FileFormat.svg },
{ icon: 'babel', extensions: ['.babelrc', 'babelrc.js'], light: true, filename: true, format: FileFormat.svg },
{ icon: 'babel2', extensions: ['.babelrc', 'babelrc.js'], light: true, filename: true, format: FileFormat.svg, disabled: true },
{ icon: 'bat', extensions: [], languages: [languages.bat], format: FileFormat.svg },
{ // http://www.file-extensions.org/filetype/extension/name/binary-files
icon: 'binary',
extensions: ['a', 'app', 'bin', 'cmo', 'cmx', 'cma', 'cmxa', 'cmi', 'dll', 'exe', 'hl', 'ilk',
'lib', 'n', 'ndll', 'o', 'obj', 'pyc', 'pyd', 'pyo', 'pdb', 'scpt', 'scptd', 'so'],
format: FileFormat.svg,
},
{ icon: 'bithound', extensions: ['.bithoundrc'], filename: true, format: FileFormat.svg },
{ icon: 'blade', extensions: [], languages: [languages.blade], format: FileFormat.svg },
{ icon: 'bolt', extensions: [], languages: [languages.bolt], filename: true, format: FileFormat.svg },
{ icon: 'bower', extensions: ['.bowerrc', 'bower.json'], filename: true, format: FileFormat.svg },
{ icon: 'buckbuild', extensions: ['.buckconfig'], filename: true, format: FileFormat.svg },
{ icon: 'bundler', extensions: ['gemfile', 'gemfile.lock'], filename: true, format: FileFormat.svg },
{ icon: 'c', extensions: [], languages: [languages.c], format: FileFormat.svg },
{ icon: 'c2', extensions: [], languages: [languages.c], format: FileFormat.svg, disabled: true },
{ icon: 'cabal', extensions: [], languages: [languages.cabal], format: FileFormat.svg },
{ icon: 'cake', extensions: [], languages: [languages.cake], format: FileFormat.svg },
{ icon: 'cakephp', extensions: [], format: FileFormat.svg },
{ icon: 'cert', extensions: ['csr', 'crt', 'cer', 'der', 'pfx', 'p12', 'p7b', 'p7r', 'src', 'crl', 'sst', 'stl'], format: FileFormat.svg },
{ icon: 'cf', extensions: ['lucee'], languages: [languages.coldfusion], format: FileFormat.svg },
{ icon: 'cf2', extensions: ['lucee'], languages: [languages.coldfusion], format: FileFormat.svg, disabled: true },
{ icon: 'cfc', extensions: [], languages: [languages.cfc], format: FileFormat.svg },
{ icon: 'cfc2', extensions: [], languages: [languages.cfc], format: FileFormat.svg, disabled: true },
{ icon: 'cfm', extensions: [], languages: [languages.cfm], format: FileFormat.svg },
{ icon: 'cfm2', extensions: [], languages: [languages.cfm], format: FileFormat.svg, disabled: true },
{ icon: 'cheader', extensions: ['h'], format: FileFormat.svg },
{ icon: 'chef', extensions: ['chefignore', 'berksfile', 'berksfile.lock', 'policyfile'], filename: true, format: FileFormat.svg },
{ icon: 'class', extensions: ['class'], format: FileFormat.svg },
{ icon: 'circleci', extensions: ['circle.yml'], light: true, filename: true, format: FileFormat.svg },
{ icon: 'clojure', extensions: ['cjm', 'cljc'], languages: [languages.clojure], format: FileFormat.svg },
{ icon: 'cmake', extensions: [], languages: [languages.cmake, languages.cmakecache], format: FileFormat.svg },
{ icon: 'cobol', extensions: [], languages: [languages.cobol], format: FileFormat.svg },
{ icon: 'codeclimate', extensions: ['.codeclimate.yml'], light: true, filename: true, format: FileFormat.svg },
{ icon: 'codecov', extensions: ['codecov.yml', '.codecov.yml'], filename: true, format: FileFormat.svg },
{ icon: 'codekit', extensions: ['kit'], format: FileFormat.svg },
{ icon: 'codekit', extensions: ['config.codekit', 'config.codekit2', 'config.codekit3'], filename: true, format: FileFormat.svg },
{ icon: 'coffeescript', extensions: [], languages: [languages.coffeescript], format: FileFormat.svg },
{ icon: 'config', extensions: ['env'], light: true, languages: [languages.properties], format: FileFormat.svg },
{ icon: 'config', extensions: ['.env.example'], light: true, filename: true, format: FileFormat.svg },
{ icon: 'compass', extensions: [], format: FileFormat.svg },
{ icon: 'composer', extensions: ['composer.json', 'composer.lock'], filename: true, format: FileFormat.svg },
{ icon: 'cpp', extensions: [], languages: [languages.cpp], format: FileFormat.svg },
{ icon: 'cpp2', extensions: [], languages: [languages.cpp], format: FileFormat.svg, disabled: true },
{ icon: 'cppheader', extensions: ['hpp'], format: FileFormat.svg },
{ icon: 'crystal', extensions: [], languages: [languages.crystal], format: FileFormat.svg },
{ icon: 'csharp', extensions: ['csx'], languages: [languages.csharp], format: FileFormat.svg },
{ icon: 'csproj', extensions: ['csproj'], format: FileFormat.svg },
{ icon: 'css', extensions: [], languages: [languages.css], format: FileFormat.svg },
{ icon: 'csslint', extensions: ['.csslintrc'], filename: true, format: FileFormat.svg },
{ icon: 'cssmap', extensions: ['css.map'], format: FileFormat.svg },
{ icon: 'cucumber', extensions: [], languages: [languages.cucumber], format: FileFormat.svg },
{ icon: 'dartlang', extensions: [], languages: [languages.dart], format: FileFormat.svg },
{ icon: 'db', extensions: ['db'], light: true, format: FileFormat.svg },
{ icon: 'delphi', extensions: [], languages: [languages.pascal], format: FileFormat.svg },
{ icon: 'dlang', extensions: [], languages: [languages.dlang], format: FileFormat.svg },
{ icon: 'diff', extensions: [], languages: [languages.diff], format: FileFormat.svg },
{ icon: 'docker', extensions: ['.dockerignore', 'docker-compose.yml', 'docker-compose.ci-build.yml', 'docker-compose.override.yml', 'docker-compose.vs.debug.yml', 'docker-compose.vs.release.yml', 'docker-cloud.yml'], filename: true, languages: [languages.dockerfile], format: FileFormat.svg },
{ icon: 'docker2', extensions: ['.dockerignore', 'docker-compose.yml', 'docker-compose.ci-build.yml', 'docker-compose.override.yml', 'docker-compose.vs.debug.yml', 'docker-compose.vs.release.yml', 'docker-cloud.yml'], filename: true, languages: [languages.dockerfile], format: FileFormat.svg, disabled: true },
{ icon: 'doxygen', extensions: [], languages: [languages.doxygen], format: FileFormat.svg },
{ icon: 'drone', extensions: ['.drone.yml', '.drone.yml.sig'], light: true, filename: true, format: FileFormat.svg },
{ icon: 'editorconfig', extensions: ['.editorconfig'], filename: true, format: FileFormat.svg },
{ icon: 'ejs', extensions: ['ejs'], format: FileFormat.svg },
{ icon: 'elasticbeanstalk', extensions: [], format: FileFormat.svg },
{ icon: 'elixir', extensions: [], languages: [languages.elixir], format: FileFormat.svg },
{ icon: 'elm', extensions: ['elm-package.json'], filename: true, languages: [languages.elm], format: FileFormat.svg },
{ icon: 'elm2', extensions: ['elm-package.json'], filename: true, languages: [languages.elm], format: FileFormat.svg, disabled: true },
{ icon: 'emacs', extensions: ['el', 'elc'], format: FileFormat.svg },
{ icon: 'ember', extensions: ['.ember-cli'], filename: true, format: FileFormat.svg },
{ icon: 'ensime', extensions: ['ensime'], format: FileFormat.svg },
{ icon: 'eps', extensions: ['eps'], format: FileFormat.svg },
{ icon: 'erb', extensions: [], languages: [languages.erb], format: FileFormat.svg },
{ icon: 'erlang', extensions: ['emakefile', '.emakerfile'], filename: true, languages: [languages.erlang], format: FileFormat.svg },
{ icon: 'eslint', extensions: ['.eslintrc', '.eslintignore', '.eslintrc.js', '.eslintrc.json', '.eslintrc.yaml', '.eslintrc.yml'], filename: true, format: FileFormat.svg },
{ icon: 'eslint2', extensions: ['.eslintrc', '.eslintignore', '.eslintrc.js', '.eslintrc.json', '.eslintrc.yaml', '.eslintrc.yml'], filename: true, format: FileFormat.svg, disabled: true },
{ icon: 'excel', extensions: ['xls', 'xlsx', 'xlsm', 'ods'], format: FileFormat.svg },
{ icon: 'favicon', extensions: ['favicon.ico'], filename: true, format: FileFormat.svg },
{ icon: 'firebase', extensions: ['.firebaserc'], filename: true, format: FileFormat.svg },
{ icon: 'flash', extensions: ['swf', 'swc'], format: FileFormat.svg },
{ icon: 'flow', extensions: ['js.flow'], format: FileFormat.svg },
{ icon: 'flow', extensions: ['.flowconfig'], filename: true, format: FileFormat.svg },
{ icon: 'font', extensions: ['woff', 'woff2', 'ttf', 'otf', 'eot', 'pfa', 'pfb', 'sfd'], light: true, format: FileFormat.svg },
{ icon: 'fortran', extensions: [], languages: [languages.fortran], format: FileFormat.svg },
{ icon: 'fsharp', extensions: [], languages: [languages.fsharp], format: FileFormat.svg },
{ icon: 'fsproj', extensions: ['fsproj'], format: FileFormat.svg },
{ icon: 'freemarker', extensions: [], languages: [languages.freemarker], format: FileFormat.svg },
{ icon: 'fusebox', extensions: ['fuse.js'], filename: true, format: FileFormat.svg },
{ icon: 'galen', extensions: [], languages: [languages.galen], format: FileFormat.svg },
{ icon: 'galen2', extensions: [], languages: [languages.galen], format: FileFormat.svg, disabled: true },
{ icon: 'git', extensions: ['.gitattributes', '.gitconfig', '.gitignore', '.gitmodules', '.gitkeep'], filename: true, languages: [languages.git], format: FileFormat.svg },
{ icon: 'gamemaker', extensions: ['gmx'], languages: [languages.gamemaker], format: FileFormat.svg },
{ icon: 'gamemaker2', extensions: ['yy', 'yyp'], light: true, languages: [languages.gamemaker2], format: FileFormat.svg },
{ icon: 'gamemaker81', extensions: [], languages: [languages.gamemaker81], format: FileFormat.svg },
{ icon: 'gitlab', extensions: ['.gitlab-ci.yml'], filename: true, format: FileFormat.svg },
{ icon: 'glsl', extensions: [], languages: [languages.glsl], format: FileFormat.svg },
{ icon: 'go', extensions: [], languages: [languages.go], format: FileFormat.svg },
{ icon: 'godot', extensions: [], languages: [languages.godot], format: FileFormat.svg },
{ icon: 'gradle', extensions: ['gradle'], format: FileFormat.svg },
{ icon: 'graphql', extensions: ['.gqlconfig'], filename: true, languages: [languages.graphql], format: FileFormat.svg },
{ icon: 'graphviz', extensions: [], languages: [languages.graphviz], format: FileFormat.svg },
{ icon: 'groovy', extensions: [], languages: [languages.groovy], format: FileFormat.svg },
{ icon: 'groovy2', extensions: [], languages: [languages.groovy], format: FileFormat.svg, disabled: true },
{
icon: 'grunt', extensions: [
'gruntfile.coffee',
'gruntfile.babel.coffee',
'gruntfile.js',
'gruntfile.babel.js',
'gruntfile.ts',
'gruntfile.babel.ts',
],
filename: true, format: FileFormat.svg,
},
{
icon: 'gulp', extensions: [
'gulpfile.coffee',
'gulpfile.babel.coffee',
'gulpfile.js',
'gulpfile.babel.js',
'gulpfile.ts',
'gulpfile.babel.ts',
],
filename: true, format: FileFormat.svg,
},
{ icon: 'haml', extensions: [], languages: [languages.haml], format: FileFormat.svg },
{ icon: 'handlebars', extensions: [], languages: [languages.handlebars], format: FileFormat.svg },
{ icon: 'handlebars2', extensions: [], languages: [languages.handlebars], format: FileFormat.svg, disabled: true },
{ icon: 'harbour', extensions: [], languages: [languages.harbour], format: FileFormat.svg },
{ icon: 'haskell', extensions: [], languages: [languages.haskell, languages.literatehaskell], format: FileFormat.svg },
{ icon: 'haskell2', extensions: [], languages: [languages.haskell, languages.literatehaskell], format: FileFormat.svg, disabled: true },
{ icon: 'haxe', extensions: ['haxelib.json'], filename: true, languages: [languages.haxe], format: FileFormat.svg },
{ icon: 'haxecheckstyle', extensions: ['checkstyle.json'], filename: true, format: FileFormat.svg },
{ icon: 'haxedevelop', extensions: ['hxproj'], format: FileFormat.svg },
{ icon: 'hlsl', extensions: [], languages: [languages.hlsl], format: FileFormat.svg },
{ icon: 'html', extensions: [], languages: [languages.html], format: FileFormat.svg },
{ icon: 'idris', extensions: ['idr', 'lidr'], format: FileFormat.svg },
{ icon: 'idrisbin', extensions: ['ibc'], format: FileFormat.svg },
{ icon: 'idrispkg', extensions: ['ipkg'], format: FileFormat.svg },
{ icon: 'image', extensions: ['jpeg', 'jpg', 'gif', 'png', 'bmp', 'tiff', 'ico'], format: FileFormat.svg },
{ icon: 'ini', extensions: [], languages: [languages.ini], light: true, format: FileFormat.svg },
{ icon: 'infopath', extensions: ['infopathxml', 'xsn', 'xsf', 'xtp2'], format: FileFormat.svg },
{ icon: 'ionic', extensions: ['ionic.project', 'ionic.config.json'], filename: true, format: FileFormat.svg },
{ icon: 'jar', extensions: ['jar'], format: FileFormat.svg },
{ icon: 'java', extensions: [], languages: [languages.java], format: FileFormat.svg },
{ icon: 'jbuilder', extensions: ['jbuilder'], format: FileFormat.svg },
{ icon: 'jenkins', extensions: ['jenkinsfile'], filename: true, format: FileFormat.svg },
{ icon: 'jest', extensions: ['jest.config.js', 'jest.json', 'jest.config.json', '.jestrc'], filename: true, format: FileFormat.svg },
{ icon: 'jinja', extensions: [], languages: [languages.jinja], format: FileFormat.svg },
{ icon: 'js', extensions: [], light: true, languages: [languages.javascript], format: FileFormat.svg },
{ icon: 'js_official', extensions: [], languages: [languages.javascript], format: FileFormat.svg, disabled: true },
{ icon: 'jsconfig', extensions: ['jsconfig.json'], light: true, filename: true, format: FileFormat.svg },
{ icon: 'jshint', extensions: ['.jshintrc', '.jshintignore'], filename: true, format: FileFormat.svg },
{ icon: 'jsmap', extensions: ['js.map'], light: true, format: FileFormat.svg },
{ icon: 'json', extensions: [], light: true, languages: [languages.json, languages.textmatejson], format: FileFormat.svg },
{ icon: 'json_official', extensions: [], languages: [languages.json, languages.textmatejson], format: FileFormat.svg, disabled: true },
{ icon: 'json2', extensions: [], languages: [languages.json, languages.textmatejson], format: FileFormat.svg, disabled: true },
{ icon: 'jsonld', extensions: ['jsonld', 'json-ld'], light: true, format: FileFormat.svg },
{ icon: 'jsp', extensions: ['jsp'], format: FileFormat.svg },
{ icon: 'julia', extensions: [], languages: [languages.julia], format: FileFormat.svg },
{ icon: 'julia2', extensions: [], languages: [languages.julia], format: FileFormat.svg, disabled: true },
{ icon: 'karma', extensions: ['karma.conf.js', 'karma.conf.coffee'], filename: true, format: FileFormat.svg },
{ icon: 'key', extensions: ['key', 'pem'], format: FileFormat.svg },
{ icon: 'kite', extensions: ['.kiteignore'], light: true, filename: true, format: FileFormat.svg },
{ icon: 'kitchenci', extensions: ['.kitchen.yml'], filename: true, format: FileFormat.svg },
{ icon: 'kotlin', extensions: [], languages: [languages.kotlin], format: FileFormat.svg },
{ icon: 'layout', extensions: ['master', 'layout.html', 'layout.htm'], format: FileFormat.svg },
{ icon: 'layout', extensions: ['layout.html', 'layout.htm'], filename: true, format: FileFormat.svg },
{ icon: 'lerna', extensions: ['lerna.json'], light: true, filename: true, format: FileFormat.svg },
{ icon: 'less', extensions: [], languages: [languages.less], format: FileFormat.svg },
{ icon: 'license', extensions: ['enc'], format: FileFormat.svg },
{ icon: 'license', extensions: ['license', 'licence', 'license.md', 'licence.md', 'license.txt', 'licence.txt'], filename: true, format: FileFormat.svg },
{ icon: 'lisp', extensions: [], languages: [languages.lisp], format: FileFormat.svg },
{ icon: 'lime', extensions: ['hxp'], format: FileFormat.svg },
{ icon: 'lime', extensions: ['include.xml'], filename: true, format: FileFormat.svg },
{ icon: 'liquid', extensions: ['liquid'], format: FileFormat.svg },
{ icon: 'locale', extensions: [], format: FileFormat.svg },
{ icon: 'log', extensions: ['log'], format: FileFormat.svg },
{ icon: 'lsl', extensions: ['lsl'], format: FileFormat.svg },
{ icon: 'lua', extensions: [], languages: [languages.lua], format: FileFormat.svg },
{ icon: 'lync', extensions: ['crec', 'ocrec'], format: FileFormat.svg },
{ icon: 'makefile', extensions: ['makefile'], format: FileFormat.svg },
{ icon: 'makefile', extensions: [], languages: [languages.makefile], format: FileFormat.svg },
{ icon: 'map', extensions: ['map'], format: FileFormat.svg },
{ icon: 'markdown', extensions: ['mdown', 'markdown'], languages: [languages.markdown], format: FileFormat.svg },
{ icon: 'markdownlint', extensions: ['.markdownlint.json'], filename: true, format: FileFormat.svg },
{ icon: 'marko', extensions: [], languages: [languages.marko], format: FileFormat.svg },
{ icon: 'markojs', extensions: ['marko.js'], format: FileFormat.svg },
{ icon: 'matlab', extensions: ['fig', 'mex', 'mexn', 'mexrs6', 'mn', 'mum', 'mx', 'mx3', 'rwd', 'slx', 'slddc', 'smv', 'tikz', 'xvc'], languages: [languages.matlab], format: FileFormat.png },
{ icon: 'meteor', extensions: [], format: FileFormat.svg },
{ icon: 'mjml', extensions: [], languages: [languages.mjml], format: FileFormat.svg },
{ icon: 'mustache', extensions: ['mustache', 'mst'], light: true, format: FileFormat.svg },
{ icon: 'nim', extensions: [], languages: [languages.nim], format: FileFormat.svg },
{ icon: 'njsproj', extensions: ['njsproj'], format: FileFormat.svg },
{ icon: 'node', extensions: ['.nvmrc'], filename: true, format: FileFormat.svg },
{ icon: 'node2', extensions: [], format: FileFormat.svg, disabled: true },
{ icon: 'npm', extensions: ['.npmignore', '.npmrc', 'package.json', 'package-lock.json'], filename: true, format: FileFormat.svg },
{ icon: 'nsi', extensions: [], languages: [languages.nsis], format: FileFormat.svg },
{ icon: 'nuget', extensions: ['nupkg', 'nuspec', 'psmdcp'], format: FileFormat.svg },
{ icon: 'nunjucks', extensions: ['nunj', 'njs'], languages: [languages.nunjucks], format: FileFormat.svg },
{ icon: 'objectivec', extensions: [], languages: [languages.objectivec], format: FileFormat.svg },
{ icon: 'objectivecpp', extensions: [], languages: [languages.objectivecpp], format: FileFormat.svg },
{ icon: 'ocaml', extensions: ['.merlin'], filename: true, languages: [languages.ocaml], format: FileFormat.svg },
{ icon: 'onenote', extensions: ['one', 'onepkg', 'onetoc', 'onetoc2', 'sig'], format: FileFormat.svg },
{ icon: 'opencl', extensions: ['cl', 'opencl'], format: FileFormat.svg },
{ icon: 'outlook', extensions: ['pst', 'bcmx', 'otm', 'msg', 'oft'], format: FileFormat.svg },
{ icon: 'package', extensions: ['pkg'], format: FileFormat.svg },
{ icon: 'paket', extensions: ['paket.dependencies', 'paket.lock', 'paket.references', 'paket.template', 'paket.local'], filename: true, format: FileFormat.svg },
{ icon: 'patch', extensions: ['patch'], format: FileFormat.svg },
{ icon: 'pcl', extensions: ['pcd'], light: true, format: FileFormat.svg },
{ icon: 'pdf', extensions: ['pdf'], format: FileFormat.svg },
{ icon: 'pdf2', extensions: ['pdf'], format: FileFormat.svg, disabled: true },
{ icon: 'perl', extensions: [], languages: [languages.perl], format: FileFormat.svg },
{ icon: 'perl2', extensions: [], languages: [languages.perl], format: FileFormat.svg, disabled: true },
{ icon: 'perl6', extensions: [], languages: [languages.perl6], format: FileFormat.svg },
{ icon: 'photoshop', extensions: ['psd'], format: FileFormat.svg },
{ icon: 'photoshop2', extensions: ['psd'], format: FileFormat.svg, disabled: true },
{ icon: 'php', extensions: ['php1', 'php2', 'php3', 'php4', 'php5', 'php6', 'phps', 'phpsa', 'phpt', 'phtml', 'phar'], languages: [languages.php], format: FileFormat.svg },
{ icon: 'php2', extensions: ['php1', 'php2', 'php3', 'php4', 'php5', 'php6', 'phps', 'phpsa', 'phpt', 'phtml', 'phar'], languages: [languages.php], format: FileFormat.svg, disabled: true },
{ icon: 'php3', extensions: ['php1', 'php2', 'php3', 'php4', 'php5', 'php6', 'phps', 'phpsa', 'phpt', 'phtml', 'phar'], languages: [languages.php], format: FileFormat.svg, disabled: true },
{ icon: 'phpunit', extensions: ['phpunit', 'phpunit.xml', 'phpunit.xml.dist'], filename: true, format: FileFormat.svg },
{ icon: 'plantuml', extensions: ['pu', 'plantuml', 'iuml', 'puml'], format: FileFormat.svg },
{ icon: 'plsql', extensions: [], languages: [languages.plsql], format: FileFormat.svg },
{ icon: 'plsql_package', extensions: ['pck'], format: FileFormat.svg },
{ icon: 'plsql_package_body', extensions: ['pkb'], format: FileFormat.svg },
{ icon: 'plsql_package_header', extensions: ['pkh'], format: FileFormat.svg },
{ icon: 'plsql_package_spec', extensions: ['pks'], format: FileFormat.svg },
{ icon: 'poedit', extensions: ['po', 'mo'], format: FileFormat.svg },
{ icon: 'polymer', extensions: [], languages: [languages.polymer], format: FileFormat.svg },
{ icon: 'postcss', extensions: ['.postcssrc.js', 'postcss.config.js'], filename: true, languages: [languages.postcss], format: FileFormat.svg },
{ icon: 'powerpoint', extensions: ['pot', 'potx', 'potm', 'pps', 'ppsx', 'ppsm', 'ppt', 'pptx', 'pptm', 'pa', 'ppa', 'ppam', 'sldm', 'sldx'], format: FileFormat.svg },
{ icon: 'powershell', extensions: [], languages: [languages.powershell], format: FileFormat.svg },
{ icon: 'procfile', extensions: ['procfile'], filename: true, format: FileFormat.svg },
{ icon: 'progress', extensions: [], languages: [languages.openEdge], format: FileFormat.svg },
{ icon: 'prolog', extensions: ['pro', 'P'], languages: [languages.prolog], format: FileFormat.svg },
{ icon: 'protobuf', extensions: [], languages: [languages.protobuf], format: FileFormat.svg },
{ icon: 'protractor', extensions: ['protractor.conf.js'], filename: true, format: FileFormat.svg },
{ icon: 'publisher', extensions: ['pub', 'puz'], format: FileFormat.svg },
{ icon: 'puppet', extensions: [], languages: [languages.puppet], format: FileFormat.svg },
{ icon: 'pug', extensions: ['.jade-lintrc', '.pug-lintrc', '.jade-lint.json', '.pug-lintrc.js', '.pug-lintrc.json'], filename: true, languages: [languages.pug], format: FileFormat.svg },
{ icon: 'purescript', extensions: [], light: true, languages: [languages.purescript], format: FileFormat.svg },
{ icon: 'python', extensions: [], languages: [languages.python], format: FileFormat.svg },
{ icon: 'qlikview', extensions: ['qvd', 'qvw'], languages: [languages.qlik], format: FileFormat.svg },
{ icon: 'r', extensions: [], languages: [languages.r], format: FileFormat.svg },
{ icon: 'rails', extensions: [], format: FileFormat.svg },
{ icon: 'rake', extensions: ['rake'], format: FileFormat.svg },
{ icon: 'rake', extensions: ['rakefile'], filename: true, format: FileFormat.svg },
{ icon: 'raml', extensions: [], languages: [languages.raml], format: FileFormat.svg },
{ icon: 'razor', extensions: [], languages: [languages.razor], format: FileFormat.svg },
{ icon: 'reactjs', extensions: [], languages: [languages.javascriptreact], format: FileFormat.svg },
{ icon: 'reacttemplate', extensions: ['rt'], format: FileFormat.svg },
{ icon: 'reactts', extensions: [], languages: [languages.typescriptreact], format: FileFormat.svg },
{ icon: 'reason', extensions: [], languages: [languages.reason], format: FileFormat.svg },
{ icon: 'rest', extensions: [], languages: [languages.restructuredtext], format: FileFormat.svg },
{ icon: 'registry', extensions: ['reg'], format: FileFormat.svg },
{ icon: 'riot', extensions: [], languages: [languages.riot], format: FileFormat.svg },
{ icon: 'robotframework', extensions: [], languages: [languages.robot], format: FileFormat.svg },
{ icon: 'rollup', extensions: ['rollup.config.js', 'rollup.config.ts'], filename: true, format: FileFormat.svg },
{ icon: 'rspec', extensions: ['.rspec'], filename: true, format: FileFormat.svg },
{ icon: 'ruby', extensions: [], languages: [languages.ruby], format: FileFormat.svg },
{ icon: 'rust', extensions: [], languages: [languages.rust], format: FileFormat.svg },
{ icon: 'saltstack', extensions: ['sls'], format: FileFormat.svg },
{ icon: 'sass', extensions: ['sass'], format: FileFormat.svg },
{ icon: 'sbt', extensions: [], languages: [languages.sbt], format: FileFormat.svg },
{ icon: 'scala', extensions: [], languages: [languages.scala], format: FileFormat.svg },
{ icon: 'script', extensions: [], languages: [languages.vbscript], format: FileFormat.svg },
{ icon: 'scss', extensions: ['scssm'], languages: [languages.scss], format: FileFormat.svg },
{ icon: 'sequelize', extensions: ['.sequelizerc'], filename: true, format: FileFormat.svg },
{ icon: 'shaderlab', extensions: [], languages: [languages.shaderlab], light: true, format: FileFormat.svg },
{ icon: 'shell', extensions: ['fish'], languages: [languages.shellscript], format: FileFormat.svg },
{ icon: 'slim', extensions: [], languages: [languages.slim], format: FileFormat.svg },
{ icon: 'sln', extensions: ['sln'], format: FileFormat.svg },
{ icon: 'smarty', extensions: [], languages: [languages.smarty], format: FileFormat.svg },
{ icon: 'snyk', extensions: ['.snyk'], filename: true, format: FileFormat.svg },
{ icon: 'solidity', extensions: [], light: true, languages: [languages.solidity], format: FileFormat.svg },
{ icon: 'source', extensions: [], format: FileFormat.svg },
{ icon: 'sqf', extensions: [], languages: [languages.sqf], format: FileFormat.svg },
{ icon: 'sql', extensions: [], languages: [languages.sql], format: FileFormat.svg },
{ icon: 'sqlite', extensions: ['sqlite', 'sqlite3', 'db3'], format: FileFormat.svg },
{ icon: 'sss', extensions: ['sss'], format: FileFormat.svg },
{ icon: 'style', extensions: [], format: FileFormat.svg },
{ icon: 'stylelint', extensions: ['.stylelintrc', 'stylelint.config.js', '.stylelintignore'], light: true, filename: true, format: FileFormat.svg },
{ icon: 'stylus', extensions: [], languages: [languages.stylus], format: FileFormat.svg },
{ icon: 'storyboard', extensions: ['storyboard'], format: FileFormat.svg },
{ icon: 'storybook', extensions: ['story.js', 'stories.js'], format: FileFormat.svg },
{ icon: 'svg', extensions: ['svg'], format: FileFormat.svg },
{ icon: 'swagger', extensions: [], languages: [languages.swagger], format: FileFormat.svg },
{ icon: 'swift', extensions: ['package.pins'], filename: true, languages: [languages.swift], format: FileFormat.svg },
{ icon: 'tcl', extensions: ['tcl', 'exp'], format: FileFormat.svg },
{ icon: 'terraform', extensions: ['tfstate'], languages: [languages.terraform], format: FileFormat.svg },
{ icon: 'test', extensions: ['tst'], format: FileFormat.svg },
{ icon: 'testjs', extensions: ['test.js', 'spec.js', 'test.jsx', 'spec.jsx'], light: true, format: FileFormat.svg },
{ icon: 'testts', extensions: ['test.ts', 'test.tsx', 'spec.ts', 'spec.tsx'], format: FileFormat.svg },
{ icon: 'tex', extensions: ['texi'], languages: [languages.tex, languages.latex], light: true, format: FileFormat.svg },
{ icon: 'text', extensions: ['csv'], languages: [languages.plaintext], format: FileFormat.svg },
{ icon: 'textile', extensions: [], languages: [languages.textile], format: FileFormat.svg },
{ icon: 'tfs', extensions: ['.tfignore'], filename: true, format: FileFormat.svg },
{ icon: 'todo', extensions: ['todo'], light: true, format: FileFormat.svg },
{ icon: 'toml', extensions: [], languages: [languages.toml], format: FileFormat.svg },
{ icon: 'travis', extensions: ['.travis.yml'], filename: true, format: FileFormat.svg },
{ icon: 'tsconfig', extensions: ['tsconfig.json', 'tsconfig.app.json', 'tsconfig.spec.json', 'tsconfig.e2e.json'], filename: true, format: FileFormat.svg },
{ icon: 'tslint', extensions: ['tslint.json'], filename: true, format: FileFormat.svg },
{ icon: 'twig', extensions: [], languages: [languages.twig], format: FileFormat.svg },
{ icon: 'typescript', extensions: [], languages: [languages.typescript], format: FileFormat.svg },
{ icon: 'typescript_official', extensions: [], languages: [languages.typescript], format: FileFormat.svg, disabled: true },
{ icon: 'typescriptdef', extensions: ['d.ts'], format: FileFormat.svg },
{ icon: 'typescriptdef_official', extensions: ['d.ts'], format: FileFormat.svg, disabled: true },
{ icon: 'vagrant', extensions: ['vagrantfile'], filename: true, format: FileFormat.svg },
{ icon: 'vash', extensions: ['vash'], light: true, format: FileFormat.svg },
{ icon: 'vb', extensions: [], languages: [languages.vb], format: FileFormat.svg },
{ icon: 'vbhtml', extensions: ['vbhtml'], format: FileFormat.svg },
{ icon: 'vbproj', extensions: ['vbproj'], format: FileFormat.svg },
{ icon: 'vcxproj', extensions: ['vcxproj'], format: FileFormat.svg },
{ icon: 'vhdl', extensions: [], languages: [languages.vhdl], format: FileFormat.svg },
{ // https://en.wikipedia.org/wiki/Video_file_format
icon: 'video',
extensions: ['3g2', '3gp', 'asf', 'amv', 'avi', 'divx', 'qt', 'f4a', 'f4b', 'f4p', 'f4v', 'flv',
'm2v', 'm4v', 'mkv', 'mk3d', 'mov', 'mp2', 'mp4', 'mpe', 'mpeg', 'mpeg2', 'mpg', 'mpv', 'nsv',
'ogv', 'rm', 'rmvb', 'svi', 'vob', 'webm', 'wmv'],
format: FileFormat.svg,
},
{ icon: 'view', extensions: [], format: FileFormat.svg },
{ icon: 'vim', extensions: ['.vimrc', '.gvimrc'], filename: true, languages: [languages.viml], format: FileFormat.svg },
{ icon: 'volt', extensions: [], languages: [languages.volt], format: FileFormat.svg },
{
icon: 'vscode',
extensions: [
'vscodeignore.json',
'launch.json',
'tasks.json',
'.vscodeignore',
],
filename: true,
format: FileFormat.svg,
},
{ icon: 'vsix', extensions: ['vsix'], light: true, format: FileFormat.svg },
{ icon: 'vue', extensions: [], languages: [languages.vue], format: FileFormat.svg },
{ icon: 'watchmanconfig', extensions: ['.watchmanconfig'], filename: true, format: FileFormat.svg },
{
icon: 'webpack',
extensions: [
'webpack.base.conf.coffee',
'webpack.base.conf.js',
'webpack.base.conf.ts',
'webpack.common.coffee',
'webpack.common.js',
'webpack.common.ts',
'webpack.config.coffee',
'webpack.config.base.coffee',
'webpack.config.common.coffee',
'webpack.config.dev.coffee',
'webpack.config.development.coffee',
'webpack.config.staging.coffee',
'webpack.config.test.coffee',
'webpack.config.prod.coffee',
'webpack.config.production.coffee',
'webpack.config.js',
'webpack.config.base.js',
'webpack.config.common.js',
'webpack.config.dev.js',
'webpack.config.development.js',
'webpack.config.staging.js',
'webpack.config.test.js',
'webpack.config.prod.js',
'webpack.config.production.js',
'webpack.config.ts',
'webpack.config.base.ts',
'webpack.config.common.ts',
'webpack.config.dev.ts',
'webpack.config.development.ts',
'webpack.config.staging.ts',
'webpack.config.test.ts',
'webpack.config.prod.ts',
'webpack.config.production.ts',
'webpack.config.babel.js',
'webpack.config.base.babel.js',
'webpack.config.common.babel.js',
'webpack.config.dev.babel.js',
'webpack.config.development.babel.js',
'webpack.config.staging.babel.js',
'webpack.config.test.babel.js',
'webpack.config.prod.babel.js',
'webpack.config.production.babel.js',
'webpack.dev.coffee',
'webpack.dev.js',
'webpack.dev.ts',
'webpack.dev.conf.coffee',
'webpack.dev.conf.js',
'webpack.dev.conf.ts',
'webpack.prod.coffee',
'webpack.prod.js',
'webpack.prod.ts',
'webpack.prod.conf.coffee',
'webpack.prod.conf.js',
'webpack.prod.conf.ts',
'webpack.mix.coffee',
'webpack.mix.js',
'webpack.mix.ts',
'webpack.test.conf.coffee',
'webpack.test.conf.js',
'webpack.test.conf.ts',
],
filename: true,
format: FileFormat.svg,
},
{ icon: 'wercker', extensions: ['wercker.yml'], filename: true, format: FileFormat.svg },
{ icon: 'word', extensions: ['doc', 'docx', 'docm', 'dot', 'dotx', 'dotm', 'wll'], format: FileFormat.svg },
{ icon: 'wxml', extensions: ['wxml'], format: FileFormat.svg },
{ icon: 'wxss', extensions: ['wxss'], format: FileFormat.svg },
{ icon: 'xcode', extensions: ['xcodeproj'], format: FileFormat.svg },
{ icon: 'xib', extensions: ['xib'], format: FileFormat.svg },
{ icon: 'xliff', extensions: ['xliff', 'xlf'], format: FileFormat.svg },
{ icon: 'xml', extensions: ['pex', 'tmlanguage'], languages: [languages.xml], format: FileFormat.svg },
{ icon: 'xsl', extensions: [], languages: [languages.xsl], format: FileFormat.svg },
{ icon: 'yaml', extensions: ['yml'], light: true, languages: [languages.yaml, languages.textmateyaml], format: FileFormat.svg },
{ icon: 'yang', extensions: [], languages: [languages.yang], format: FileFormat.svg },
{ icon: 'yarn', extensions: ['yarn.lock', '.yarnrc', '.yarnclean', '.yarn-integrity', '.yarn-metadata.json', '.yarnignore'], filename: true, format: FileFormat.svg },
{ icon: 'yeoman', extensions: ['.yo-rc.json'], filename: true, format: FileFormat.svg },
{ icon: 'zip', extensions: ['zip', 'rar', '7z', 'tar', 'gz', 'bzip2', 'xz', 'bz2'], format: FileFormat.svg },
{ icon: 'zip2', extensions: ['zip', 'rar', '7z', 'tar', 'gz', 'bzip2', 'xz', 'bz2'], format: FileFormat.svg, disabled: true },
],
};
================================================
FILE: misc/init-demo.sh
================================================
#!/bin/bash
rm -rf ~/demo
mkdir -p ~/demo/mybooklive/Tv.Show
for i in {1..5}; do
folder=~/demo/downloads/Tv.Show.S01E0$i
mkdir -p $folder
touch $folder/Tv.Show.S01E0$i.{mkv,srt,nfo}
done
================================================
FILE: misc/link-all.sh
================================================
#!/bin/bash
npm link jumpfm-clock jumpfm-copy jumpfm-fs jumpfm-gist jumpfm-git-status jumpfm-history jumpfm-jump jumpfm-version jumpfm-weather jumpfm-zip jumpfm-file-ops jumpfm-key-nav jumpfm-flat-mode
================================================
FILE: misc/modd.conf
================================================
icons.awk {
prep: ./icons.awk icons.txt
}
================================================
FILE: misc/omg-ubunut.txt
================================================
Hi Joey,
I'm the developer of JumpFm (http://jumpfm.org).
Its an Electron based (I know, I know.. but its really not that bad) file manager inspired by fman (http://bit.ly/2x70Q39) and exa (https://the.exa.website/).
It comes with some cool features like jumping, git status integration and more (see http://jumpfm.org for more details).
It is a completely open source project, and it is extremely easy (as easy as writing NPM package) to extend its functionality via its plug-in system.
It can be downloaded from github as an AppImage (https://github.com/Gilad-Kutiel-App/jumpfm/releases).
I also made some short videos (http://bit.ly/2wljs2L).
Best,
Gilad Kutiel
================================================
FILE: misc/reddit.txt
================================================
tl;dr
I made JumpFm (http://bit.ly/2uBFWIC), need your feedback
Hi,
I decided to format my machine last weekend.
Then, after reinstalling almost everything, I was about to reinstall double commander.
Basically, I like double commander but it takes me a lot of configuration to bring it to a state that I can work with it.
I also came across fman (http://bit.ly/2uBwIw6) couple of weeks ago, and it seems really great but it is not an open source and requires a subscription fee.
Also, I haven't done any programming for quite a while and wanted to refresh my memory a bit.
For all the above reasons I decided to start a new side project and create my own file manager - JumpFm (http://bit.ly/2uBFWIC).
It is currently in a very early stage but there is already a working version. It was only tested on my machine but it seems to do what is suppose to do.
I'm planning to keep working on this project and would really appreciate your feedback:
- Which FM are you using ?
- What features do you like/need/must ?
- Feedback about the site/help/etc...
- Bugs
Thank you very much,
Gilad
================================================
FILE: package.json
================================================
{
"name": "jumpfm",
"version": "1.0.6",
"description": "A file manager that lets you jump",
"author": {
"name": "Gilad Kutiel",
"email": "gilad.kutiel@gmail.com"
},
"license": "ISC",
"dependencies": {
"check-dependencies": "^1.1.0",
"electron-simple-updater": "^1.2.1",
"filesize": "^3.5.10",
"fs-extra": "^4.0.1",
"homedir": "^0.6.0",
"moment": "^2.18.1",
"node-cmd": "^3.0.0",
"node-watch": "^0.5.5",
"node.os": "^1.2.4",
"npm": "^6.2.0"
},
"devDependencies": {
"@types/electron": "^1.6.10",
"@types/fs-extra": "^4.0.8",
"@types/keyboardjs": "^2.2.31",
"@types/node": "^8.10.21",
"@types/npm": "^2.0.29",
"@types/watch": "^1.0.0",
"electron-builder": "^19.56.2",
"jumpfm-api": "^1.1.4"
},
"optionalDependencies": {
"7zip-bin-win": "^2.1.0"
},
"homepage": "https://jumpfm.org",
"repository": {
"type": "git",
"url": "git+https://github.com/Gilad-Kutiel-App/jumpfm.git"
},
"bugs": {
"url": "https://github.com/Gilad-Kutiel-App/jumpfm/issues"
},
"main": "app.js",
"build": {
"appId": "com.github.gkutiel.jumpfm",
"compression": "maximum",
"electronVersion": "1.7.5",
"linux": {
"files": [
"**/*",
"build/icons/*"
],
"target": [
"AppImage"
],
"category": "Utility",
"publish": "github"
},
"win": {
"target": "nsis"
}
},
"scripts": {
"test": "mocha js",
"pack": "build --dir",
"dist": "build",
"publish": "build --publish always",
"postinstall": "electron-builder install-app-deps"
}
}
================================================
FILE: scss/_app.scss
================================================
@import 'colors';
@font-face {
font-family: DroidSansMono;
src: url('../fonts/DroidSansMono.ttf');
}
* {
font-family: 'DroidSansMono';
box-sizing: border-box;
}
html,
body {
height: 100%;
width: 100%;
padding: 0;
margin: 0;
}
#app {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
flex-direction: column;
}
html,
body,
table,
input {
background-color: $color-bg;
color: $color-fg;
}
input {
font-size: inherit;
padding: .2em;
outline: none;
border: none;
background-color: $color-bg;
}
================================================
FILE: scss/_classes.scss
================================================
@import 'colors';
.border {
border: 1px solid $color-natural;
}
.txt-no-wrap {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
::-webkit-scrollbar {
display: none;
}
.file-icon {
width: 1em;
height: 1em;
}
================================================
FILE: scss/_colors.scss
================================================
$color-border:#646464;
$color-err:#F58E8E;
$color-natural:#A9D3AB;
$color-warn:#FED37E;
$color-info:#7AABD4;
$color-06:#D6ADD5;
$color-07:#79D4D5;
$color-08:#D4D4D4;
$color-bg:lighten(#2D2D2D, 5%);
$color-fg:#D4D4D4;
================================================
FILE: scss/_dialog.scss
================================================
@import 'colors';
@import 'classes';
#dialog {
display: none;
position: absolute;
top: 20%;
left: 0;
right: 0;
width: 600px;
margin: 0 auto;
background-color: transparent;
}
#dialog-label,
#dialog-input {
padding: .8em;
}
#dialog-label,
#dialog-input,
#dialog-sug {
@extend .border;
background-color: $color-bg;
}
#dialog-input,
#dialog-sug {
width: 100%;
}
#dialog-content {
display: flex;
#dialog-label {
color: $color-info;
font-weight: bold;
border-right: none;
}
}
#dialog-left {
flex-grow: 0;
flex-shrink: 0;
}
#dialog-right {
flex-grow: 1;
overflow: hidden;
#dialog-input {
border-left: none;
padding-left: 0;
}
#dialog-sug {
border-top: none;
line-height: 1.5;
padding: 0;
list-style-type: none;
margin: 0;
[cur] {
background-color: $color-border;
}
b {
color: $color-info;
}
li {
@extend .txt-no-wrap;
padding: 0 .2em;
}
}
}
================================================
FILE: scss/_panel.scss
================================================
@import 'colors';
@import 'classes';
.panel {
display: flex;
flex-direction: column;
flex-grow: 1;
width: 50%;
border: 0 solid $color-border;
&:first-child {
border-right-width: 2px
}
&:nth-child(2) {
border-left-width: 2px
}
}
.panel>title {
@extend .txt-no-wrap;
flex-shrink: 0;
display: block;
width: 100%;
}
.panel[active]>title {
background-color: darken($color-info, 20%);
font-weight: bold;
}
.panel tr[hidden] {
display: none;
}
.panel tr[cur] {
background-color: $color-border;
font-weight: bold;
}
.panel[active] tr[selected] {
background-color: darken($color-warn, 55%);
}
.panel[active] tr[cur] {
background-color: darken($color-warn, 45%);
}
table {
width: 100%;
display: flex;
flex-direction: column;
table-layout: fixed;
flex-grow: 1;
border-collapse: collapse;
text-align: left;
}
thead {
background-image: linear-gradient($color-border, $color-bg);
}
tbody {
display: block;
overflow-y: auto;
flex-grow: 1;
}
thead,
tbody tr {
display: table;
width: 100%;
table-layout: fixed;
}
th:first-child,
td:first-child {
width: 1.5em;
}
th:nth-child(3),
td:nth-child(3) {
width: 6em;
}
th:nth-child(4),
td:nth-child(4) {
width: 10.5em;
}
th,
td {
@extend .txt-no-wrap;
padding: .1em .2em;
&:first-child {
padding-left: .5em;
padding-right: 1.5em;
}
&:last-child {
padding-right: .5em;
}
}
.filter {
border-top: 1px solid $color-border;
}
================================================
FILE: scss/_panels.scss
================================================
#panels {
flex-grow: 1;
display: flex;
flex-direction: row;
}
================================================
FILE: scss/_statusbar.scss
================================================
@import 'colors';
//
#statusbar {
display: flex;
flex-shrink: 0;
padding: .1em 0;
border-top: 3px solid $color-border;
}
#statusbar-msgs {
display: flex;
flex-grow: 1;
align-items: center;
font-size: 90%;
padding-right: 1em;
overflow: hidden;
.msg {
cursor: default;
flex-shrink: 0;
border-right: 1px solid $color-border;
padding: 0 .5em;
&:last-child {
border-right: none;
}
&:empty {
padding: 0;
}
}
}
#statusbar-buttons {
display: flex;
flex-direction: row-reverse;
flex-shrink: 0;
font-size: 120%;
.btn {
padding-right: .5em;
}
}
.btn {
font-size: 1em;
display: inline-block;
cursor: pointer;
padding: 0;
margin: 0;
color: $color-natural;
border: none;
outline: none;
opacity: .5;
text-decoration: none;
&:hover {
opacity: 1;
}
}
[type=info],
[type=info][data-title]:before {
color: $color-info;
}
[type=warn],
[type=warn][data-title]:before {
color: $color-warn;
}
[type=err],
[type=err][data-title]:before {
color: $color-err;
}
================================================
FILE: scss/_tooltip.scss
================================================
@import 'colors';
[data-title] {
&:before {
position: absolute;
content: attr(data-title);
white-space: nowrap;
color: $color-fg;
background-color: $color-bg;
width: auto;
padding: .5em;
border: 1px solid $color-border;
border-radius: 3px;
transform: translate(1em, -3.5em);
opacity: 0;
visibility: hidden;
}
&:hover:before {
visibility: visible;
transition: all .2s;
opacity: 1;
height: auto;
}
}
#statusbar-buttons {
[data-title] {
&:before {
font-size: 80%;
transform: translate(-3em, -3em);
}
&:hover:before {}
}
}
================================================
FILE: scss/index.scss
================================================
@import 'app';
@import 'panels';
@import 'panel';
@import 'classes';
@import 'dialog';
@import 'statusbar';
@import 'tooltip';
================================================
FILE: ts/Dialog.ts
================================================
import { Dialog as DialogApi, DialogSpec, Suggestion } from 'jumpfm-api'
import { shortway } from "./shortway";
interface SuggestionLi extends Suggestion {
li: HTMLLIElement
}
export class Dialog implements DialogApi {
private readonly divDialog: HTMLDivElement =
document.getElementById('dialog') as HTMLDivElement
private readonly divLabel: HTMLDivElement =
document.getElementById('dialog-label') as HTMLDivElement
private readonly input: HTMLInputElement =
document.getElementById('dialog-input') as HTMLInputElement
private readonly olSug: HTMLOListElement =
document.getElementById('dialog-sug') as HTMLOListElement
private suggestions: SuggestionLi[] = []
private onAccept = (val: string, sug: Suggestion) => { }
private suggest = val => []
private cur: number
constructor() {
const on = (type, action, capture = false) =>
this.input.addEventListener(type, action, capture)
on('keydown', e => {
e.stopPropagation()
})
on('blur', this.close)
on('keydown', shortway('esc', this.close))
on('keydown', shortway('enter', () => {
this.close()
this.onAccept(
this.input.value,
this.suggestions[this.cur]
)
}))
on('keydown', shortway('down', () =>
this.setCur(this.cur + 1)
))
on('keydown', shortway('up', () =>
this.setCur(this.cur - 1)
))
on('input', this.updateSuggestions)
}
private updateSuggestions = () => {
this.suggestions =
this.suggest(this.input.value)
.map(sug => {
const li = document.createElement('li')
li.innerHTML = sug.html
sug.li = li
return sug
})
this.clearSuggestions()
this.suggestions.forEach(sug => {
this.olSug.appendChild(sug.li)
})
const curSuggestion = this.suggestions[this.cur = 0]
if (curSuggestion) curSuggestion.li.setAttribute('cur', '')
}
private setCur = (i: number) => {
if (!this.suggestions.length) return
const curSug = this.suggestions[this.cur]
if (curSug) curSug.li.removeAttribute('cur')
this.cur = Math.max(0, Math.min(i, this.suggestions.length - 1))
this.suggestions[this.cur].li.setAttribute('cur', '')
}
private clearSuggestions = () => {
while (this.olSug.lastChild) this.olSug.removeChild(this.olSug.lastChild)
}
private close = () => {
this.divDialog.style.display = 'none'
}
open = (spec: DialogSpec) => {
this.suggestions = []
this.clearSuggestions()
this.divLabel.textContent = spec.label
this.cur = 0
this.divDialog.style.display = 'block'
this.input.value = ''
this.input.select()
spec.onOpen && spec.onOpen(this.input)
this.suggest = spec.suggest || (val => [])
this.onAccept = spec.onAccept
}
}
================================================
FILE: ts/Filter.ts
================================================
import { FilterBox as FilterApi } from 'jumpfm-api'
import { getKeys } from "./files";
import { shortway } from "./shortway";
export class Filter implements FilterApi {
readonly input: HTMLInputElement = document.createElement('input')
private readonly handlers: ((val: string) => void)[] = []
constructor() {
this.input.className = 'filter'
this.input.addEventListener('keydown', e => e.stopPropagation(), false)
this.input.addEventListener('input', () => {
this.notifyAll()
}, false)
this.input.addEventListener('blur', this.hide, false)
this.hide()
}
private notifyAll(): any {
this.handlers.forEach(handler => {
handler(this.input.value)
});
}
set = (val: string) => {
this.input.value = val
this.notifyAll()
}
get = () => {
return this.input.value
}
onChange = (handler: (val: string) => void) => this.handlers.push(handler)
focus = () => {
this.input.style.display = 'block'
this.input.focus()
}
hide = () => this.input.style.display = 'none'
bind = (actionName: string, defaultKeys: string[], action: () => void) => {
getKeys(actionName, defaultKeys).forEach(combo => {
const cb = shortway(combo, (e) => {
e.preventDefault()
action()
})
this.input.addEventListener('keydown', cb, false)
})
}
}
================================================
FILE: ts/Item.ts
================================================
import { Item as ItemApi, File } from 'jumpfm-api'
import * as moment from 'moment'
import * as fileSize from 'filesize'
type attr = 'cur' | 'hidden' | 'selected'
export class Item implements ItemApi {
private hidden: boolean = false
private selected: boolean = false
private readonly icon = document.createElement('img')
private readonly tdIcon = document.createElement('td')
private readonly tdName = document.createElement('td')
private readonly tdSize = document.createElement('td')
private readonly tdTime = document.createElement('td')
readonly tr = document.createElement('tr')
constructor(item: File) {
this.icon.className = 'file-icon'
this.tdIcon.appendChild(this.icon)
this.tr.appendChild(this.tdIcon)
this.tr.appendChild(this.tdName)
this.tr.appendChild(this.tdSize)
this.tr.appendChild(this.tdTime)
this.path = item.path
this.name = item.name
this.tdName.textContent =
item.name || '--'
}
readonly path: string;
readonly name: string;
private readonly attrs: { [attr: string]: boolean } = {}
private set = (attr: attr) => (b: boolean) => {
this.attrs[attr] = b
if (b)
this.tr.setAttribute(attr, '')
else
this.tr.removeAttribute(attr)
return this
}
private is = (attr: attr) => this.attrs[attr]
setCur: (b: boolean) => void = this.set('cur')
setHidden: (b: boolean) => void = this.set('hidden')
setSelected: (b: boolean) => void = this.set('selected')
hide = () => this.setHidden(true)
show = () => this.setHidden(false)
isSelected = () => this.is('selected')
isHidden = () => this.is('hidden')
setAttribute(name: string, val: string = '') {
this.tr.setAttribute(name, val)
return this
}
setIcon = (src: string) => {
this.icon.src = src
return this
}
setTime = (time: number) => {
this.tdTime.textContent =
(time && moment(time).format('DD/MM/YYYY hh:mm') || '--')
return this
}
setSize = (size: number) => {
this.tdSize.textContent =
(size && fileSize(size) || '--')
return this
}
}
================================================
FILE: ts/JumpFm.ts
================================================
import { } from 'electron'
import { JumpFm as JumpFmApi } from 'jumpfm-api'
import { Panel } from "./Panel"
import { StatusBar } from "./StatusBar"
import { PluginManager } from "./PluginManager";
import { Dialog } from "./Dialog";
import { shortway } from "./shortway";
import { Settings } from "./Settings";
import {
getKeys
, saveKeyboard
, root
, packageJson
, keyboardPath
, settingsPath
, pluginsPackageJson
} from "./files";
import * as homedir from 'homedir'
import * as fs from 'fs'
import * as watch from 'node-watch'
export class JumpFm implements JumpFmApi {
private active: 0 | 1 = 0
private readonly divPanels = document.getElementById('panels')
private readonly pluginManager = new PluginManager(this)
private readonly watchers: { [name: string]: fs.FSWatcher } = {}
readonly package = packageJson
readonly root = root
readonly settings = new Settings()
readonly dialog = new Dialog()
readonly electron: Electron.AllElectron = require('electron')
readonly panels: Panel[] = [new Panel(), new Panel()]
readonly statusBar: StatusBar = new StatusBar()
readonly argv: string[]
private passive = (): 0 | 1 => (this.active + 1) % 2 as 0 | 1
private setActive = (i: 0 | 1) => {
this.active = i
this.panels[this.active].setActive(true)
this.panels[this.passive()].setActive(false)
}
watchStart = (name, path, then, recursive = false) => {
this.watchStop(name)
console.log('WATCH START', name, path)
setImmediate(() => {
let to
this.watchers[name] = watch(path, { recursive: recursive }, () => {
clearTimeout(to)
to = setTimeout(then, 10)
})
})
}
watchStop = (name: string) => {
if (this.watchers[name]) {
console.log('WATCH STOP', name)
this.watchers[name].close()
}
}
getPanelActive = () =>
this.panels[this.active]
getPanelPassive = () =>
this.panels[this.passive()]
panelsSwap = () => {
this.active = this.passive()
const tmp = this.panels[0]
this.panels[0] = this.panels[1]
this.panels[1] = tmp
this.divPanels.insertBefore(this.panels[0].divPanel, this.panels[1].divPanel)
}
panelsSwitch = () =>
this.setActive(this.passive())
bind = (actionName: string, defaultKeys: string[], action: () => void) => {
getKeys(actionName, defaultKeys).forEach(combo => {
const cb = shortway(combo, (e) => {
e.preventDefault()
action()
})
document.addEventListener('keydown', cb, false)
})
}
constructor(argv: string[]) {
this.argv = argv
this.panels.forEach(panel => {
this.divPanels.appendChild(panel.divPanel)
})
const opn = (url) => () => this.electron.shell.openItem(url)
this.statusBar.buttonAdd('fa-info', 'About', opn('http://jumpfm.org'))
this.statusBar.buttonAdd('fa-key', 'Keyboard', opn(keyboardPath))
this.statusBar.buttonAdd('fa-gear', 'Settings', opn(settingsPath))
this.statusBar.buttonAdd('fa-plug', 'Plugins', opn(pluginsPackageJson))
this.pluginManager.loadAndUpdatePlugins(() => {
saveKeyboard()
this.panels.forEach(panel => panel.cd(homedir()))
this.setActive(0)
})
}
}
================================================
FILE: ts/Panel.ts
================================================
import { Panel as PanelApi, Url, File } from 'jumpfm-api'
import { Item } from './Item'
import { Filter } from './Filter'
import { getKeys } from "./files";
import { shortway } from "./shortway";
export class Panel implements PanelApi {
private readonly table: HTMLTableElement = document.createElement('table')
private readonly tbody: HTMLTableSectionElement = document.createElement('tbody')
private readonly thead: HTMLTableSectionElement = document.createElement('thead')
private readonly title: HTMLTitleElement = document.createElement('title')
private readonly trHead: HTMLTableRowElement = document.createElement('tr')
readonly divPanel: HTMLDivElement = document.createElement('div')
readonly filterBox: Filter = new Filter()
private readonly filters: { [name: string]: ((item: Item) => boolean) } = {}
private readonly onCds: (() => void)[] = []
private readonly onItemsAddeds: ((newItems: Item[]) => void)[] = []
private readonly onLoads: (() => void)[] = []
private active = false
private url: Url
private cur: number = 0
private items: Item[] = []
private visibleItems: Item[] = []
constructor() {
['', 'Name', 'Size', 'Time'].forEach(head => {
const td = document.createElement('td')
td.textContent = head
this.trHead.appendChild(td)
})
this.divPanel.className = 'panel'
this.divPanel.appendChild(this.title)
this.divPanel.appendChild(this.table)
this.thead.appendChild(this.trHead)
this.table.appendChild(this.thead)
this.table.appendChild(this.tbody)
this.divPanel.appendChild(this.filterBox.input)
this.filterBox.onChange(this.setTitle)
}
private clearItems = () => {
while (this.tbody.lastChild) this.tbody.removeChild(this.tbody.lastChild)
}
private addItems = (items: Item[]) => {
items.forEach(item => this.tbody.appendChild(item.tr))
}
private setTitle = () => {
const filter = this.filterBox.get()
const protocol = this.url.protocol
this.title.textContent =
(protocol ? protocol + ':' : '')
+ this.url.path
+ (filter ? ' [' + filter + ']' : '')
}
private scrollToCur = () => {
const curItem = this.getCurrentItem()
if (!curItem) return
const tr = curItem.tr
const trRect = tr.getBoundingClientRect()
const tbodyRect = this.tbody.getBoundingClientRect()
if (trRect.bottom > tbodyRect.bottom)
tr.scrollIntoView(false)
if (trRect.top < tbodyRect.top)
tr.scrollIntoView(true)
}
private safeUpdateCurrent = (b: boolean) => {
const item = this.getCurrentItem()
if (item) item.setCur(b)
}
private setCur = (i) => {
this.safeUpdateCurrent(false)
this.cur = Math.max(0, Math.min(i, this.visibleItems.length - 1))
this.safeUpdateCurrent(true)
}
private progressiveProcessItems =
(process: (items: Item[]) => void) =>
(from: number, done?: () => void) => {
if (from > this.items.length) return done && done()
const to = from + 100
process(this.items.slice(from, to))
setImmediate(() => {
this.progressiveProcessItems(process)(to, done)
})
}
private progressiveAddItems = this.progressiveProcessItems(items => {
this.addItems(items)
this.onItemsAddeds.forEach(f =>
setImmediate(() => f(items))
)
})
private progressiveUpdateVisibility = this.progressiveProcessItems(items => {
items.forEach(item => {
const visible =
Object.values(this.filters)
.every(filter => filter(item))
if (visible) {
this.visibleItems.push(item)
item.show()
} else {
item.hide()
}
})
})
private updateVisibility = () => {
console.log('updateVisibility')
this.safeUpdateCurrent(false)
this.visibleItems = []
this.progressiveUpdateVisibility(0, () => {
console.log('progressiveUpdateVisibility')
this.setCur(this.cur)
this.scrollToCur()
})
}
private selectRange = (from: number, to: number) => {
if (from > to) return this.selectRange(to, from)
for (let i = from
; i <= Math.min(to, this.visibleItems.length - 1)
; i++
) {
const item = this.visibleItems[i]
if (item) item.setSelected(true)
}
}
private rowsInView = () => {
return Math.floor(this.tbody.clientHeight / this.visibleItems[0].tr.scrollHeight)
}
private stepTo = (i, select: boolean = false) => {
if (select) this.selectRange(this.cur, i)
this.setCur(i)
this.scrollToCur()
}
setActive = (b: boolean) => {
this.active = b
if (b)
this.divPanel.setAttribute('active', '')
else
this.divPanel.removeAttribute('active')
}
cd(path: string): void;
cd(url: Url): void;
cd(pathOrUrl: any) {
if (typeof pathOrUrl == 'string') return this.cd({
protocol: '',
path: pathOrUrl,
query: {}
})
this.url = pathOrUrl as Url
console.log('cd', this.url)
this.setTitle()
this.onCds.forEach(f => setImmediate(f))
}
onCd = (then: () => void) =>
this.onCds.push(then)
onItemsAdded = (then: (newItems: Item[]) => void) =>
this.onItemsAddeds.push(then)
onLoad = (then: () => void) => {
this.onLoads.push(then)
}
step = (d: number, select?: boolean) => {
const newCur
= d < 0
? Math.max(0, this.cur + d)
: Math.min(this.cur + d, this.visibleItems.length - 1)
this.stepTo(newCur, select)
}
stepPgUp = (select?: boolean) => {
this.step(-this.rowsInView(), select)
}
stepPgDown = (select?: boolean) => {
this.step(this.rowsInView(), select)
}
stepStart = (select?: boolean) => {
this.stepTo(0, select)
}
stepEnd = (select?: boolean) => {
this.stepTo(this.visibleItems.length - 1, select)
}
selectNone = () =>
this.items.forEach(item => item.setSelected(false))
selectAll = () =>
this.items.forEach(item => item.setSelected(true))
selectToggleCurrent = () => {
const item = this.getCurrentItem()
item.setSelected(!item.isSelected())
}
getUrl = () =>
this.url
getItems = () =>
this.items
getSelectedItems = () =>
this.visibleItems.filter((item, i) =>
i === this.cur || item.isSelected()
)
getCurrentItem = () =>
this.visibleItems[this.cur]
setItems = (items: File[]) => {
console.log('setItems')
this.items = items.map(item => new Item(item))
this.clearItems()
this.progressiveAddItems(0, () => {
this.onLoads.forEach(onLoad => {
setImmediate(onLoad)
})
})
this.updateVisibility()
return this
}
filterSet = (name: string, filter: (item: Item) => boolean) => {
console.log('filterSet', filter)
this.filters[name] = filter
this.updateVisibility()
}
filterRemove = (name: string) => {
delete this.filters[name]
this.updateVisibility()
}
bind = (actionName: string, defaultKeys: string[], action: () => void) => {
getKeys(actionName, defaultKeys).forEach(combo => {
const cb = shortway(combo, (e) => {
if (!this.active) return
e.preventDefault()
action()
})
document.addEventListener('keydown', cb, false)
})
}
}
================================================
FILE: ts/PluginManager.ts
================================================
import { JumpFm } from 'jumpfm-api'
import {
pluginsPath,
pluginsPackageJson,
packageJson,
savePlugins
} from './files';
import * as check from 'check-dependencies'
import * as fs from 'fs-extra'
import * as npm from 'npm'
import * as path from 'path'
import * as watch from 'node-watch'
interface checkRes {
status: number, // 0 if successful, 1 otherwise
depsWereOk: boolean, // true if dependencies were already satisfied
log: string[], // array of logged messages
error: string[], // array of logged errors
}
const defaultPlugins = {
dependencies: {
"jumpfm-font-size": "latest",
"jumpfm-clock": "latest",
"jumpfm-copy": "latest",
"jumpfm-file-ops": "latest",
"jumpfm-filter": "latest",
"jumpfm-flat-mode": "latest",
"jumpfm-fs": "latest",
"jumpfm-gist": "latest",
"jumpfm-git-status": "latest",
"jumpfm-history": "latest",
"jumpfm-icons": "latest",
"jumpfm-jump": "latest",
"jumpfm-key-nav": "latest",
"jumpfm-version": "latest",
"jumpfm-weather": "latest",
"jumpfm-zip": "latest"
}
}
class PluginsLoader {
jumpFm: JumpFm;
done: (err?: any) => void;
private loaded = {}
constructor(jumpFm: JumpFm, done: (err?) => void) {
this.jumpFm = jumpFm
this.done = () => {
jumpFm.statusBar.msg('plugins').setText('')
done()
}
}
private loadCss = (href) => {
if (!href) return
const link = document.createElement('link')
link.setAttribute('rel', 'stylesheet')
link.setAttribute('href', href)
document.head.appendChild(link)
}
private loadPlugin = (name: string) => {
try {
console.time(name)
if (this.loaded[name]) return
this.loaded[name] = true
const pluginDir = path.join(pluginsPath, 'node_modules', name)
const plugin = require(pluginDir)
if (plugin.css)
plugin.css.forEach(css =>
this.loadCss(path.join(pluginDir, css))
)
plugin.load(this.jumpFm)
console.timeEnd(name)
} catch (e) {
console.log(e)
}
}
private getPackage = () => {
try {
return fs.readJsonSync(pluginsPackageJson)
} catch (e) {
fs.writeFileSync(
pluginsPackageJson
, JSON.stringify(defaultPlugins, null, 4)
)
return defaultPlugins
}
}
loadPlugins(pkg) {
try {
Object.keys(pkg.dependencies).forEach(name => {
this.loadPlugin(name)
})
this.done()
} catch (e) {
console.log(e)
this.done(e)
}
}
load() {
const pkg = this.getPackage()
const checkRes: checkRes = check.sync({
packageDir: pluginsPath
})
if (checkRes.depsWereOk) {
this.loadPlugins(pkg)
}
process.chdir(pluginsPath)
npm.load({
save: true
}, (err, res) => {
if (err) return this.done(err)
npm.commands.update([], (err, res) => {
if (err) return this.done(err)
if (!checkRes.depsWereOk) this.loadPlugins(pkg)
})
})
}
}
export class PluginManager {
readonly jumpFm: JumpFm
constructor(jumpFm) {
this.jumpFm = jumpFm
}
loadAndUpdatePlugins = (done: (err?) => void) => {
this.jumpFm.statusBar.msg('plugins')
.setType('info')
.setText('Downloading plugins (can take a while)...')
.setTooltip('This might take some time')
const pluginLoader = new PluginsLoader(this.jumpFm, done)
pluginLoader.load()
watch(pluginsPackageJson, () => {
pluginLoader.load()
})
}
}
================================================
FILE: ts/Settings.ts
================================================
import { Settings as SettingsApi } from 'jumpfm-api'
import { settings, saveSettings } from './files'
type Type = 'string' | 'number'
export class Settings implements SettingsApi {
private get = (type: Type) => (key: string, defaultValue: T) => {
const val = settings[key]
if (val && (typeof val === type)) return val
settings[key] = defaultValue
saveSettings(settings)
return defaultValue
}
getNum = this.get('number')
getStr = this.get('string')
}
================================================
FILE: ts/StatusBar.ts
================================================
import { StatusBar as StatusBarApi, Msg as MsgAPi } from 'jumpfm-api'
class Msg implements MsgAPi {
readonly divMsg: HTMLDivElement = document.createElement('div')
constructor() {
this.divMsg.className = 'msg'
}
setType = (type: "info" | "warn" | "err") => {
this.divMsg.setAttribute('type', type)
return this
}
setText = (txt: string) => {
this.divMsg.textContent = txt
return this
}
setTooltip = (txt: string) => {
this.divMsg.setAttribute('data-title', txt)
return this
}
setClearTimeout = (timeout: number) => {
setTimeout(() => this.setText(''), timeout)
return this
}
setAttr = (name: string, b: boolean) => {
if (b)
this.divMsg.setAttribute(name, '')
else
this.divMsg.removeAttribute(name)
return this
}
}
class Button {
readonly a: HTMLAnchorElement = document.createElement('a')
private readonly i = document.createElement('i')
constructor(faIcon: string, tooltip: string) {
this.a.className = 'btn'
this.a.setAttribute('data-title', tooltip)
this.a.target = 'about:blank'
this.i.className = `fa ${faIcon}`
this.a.appendChild(this.i)
}
}
export class StatusBar implements StatusBarApi {
private readonly divMsgs: HTMLDivElement = document
.getElementById('statusbar-msgs') as HTMLDivElement
private readonly divButtons: HTMLDivElement = document
.getElementById('statusbar-buttons') as HTMLDivElement
private readonly msgs: { [name: string]: Msg } = {}
msg = (name: string): MsgAPi => {
if (this.msgs[name]) return this.msgs[name]
const msg = new Msg()
this.msgs[name] = msg
this.divMsgs.appendChild(msg.divMsg)
return msg
}
clear = (name: string) => {
const msg = this.msgs[name]
if (!msg) return
msg.setText('')
}
msgClear = this.clear
buttonAdd(faIcon: string, tooltip: string, action: () => void) {
const a = new Button(faIcon, tooltip).a
this.divButtons.appendChild(a)
a.addEventListener('click', action, false)
}
}
================================================
FILE: ts/files.ts
================================================
import * as fs from 'fs-extra';
import * as homedir from 'homedir';
import * as path from 'path';
export interface Plugins {
name: string
version: string
dependencies: { [name: string]: string }
}
const load = (path: string) => {
try {
return require(path)
} catch (e) {
console.log(e)
return {}
}
}
const save = (path: string) => (obj: T) => {
fs.writeFileSync(path, JSON.stringify(obj, null, 4))
return obj;
}
export const packageJson = require('../package.json')
export const root = path.join(homedir(), ".jumpfm")
export const pluginsPath = path.join(root, 'plugins')
if (!fs.existsSync(pluginsPath))
fs.mkdirpSync(pluginsPath)
export const pluginsPackageJson = path.join(pluginsPath, 'package.json')
export const settingsPath = path.join(root, 'settings.json')
export const keyboardPath = path.join(root, 'keyboard.json')
export const settings = load(settingsPath)
export const keyboard = load(keyboardPath)
export const saveSettings = save(settingsPath)
export const saveKeyboard = () => save(keyboardPath)(keyboard)
export const savePlugins = save(pluginsPackageJson)
export const getKeys = (actionName: string, defaultKeys: string[]): string[] => {
const keys = keyboard[actionName]
if (keys && Array.isArray(keys)) return keys
keyboard[actionName] = defaultKeys
return defaultKeys
}
================================================
FILE: ts/icons.ts
================================================
const icons = require('../icons.json')
const extensions = {}
for (var icon in icons) {
icons[icon].forEach(ext => {
extensions[ext] = icon
});
}
export const getExtIcon = (ext: string) => {
const icon = extensions[ext]
if (icon) return 'file-icons/file_type_' + icon + '.svg'
}
================================================
FILE: ts/main.ts
================================================
document.addEventListener('DOMContentLoaded', () => {
console.time('main')
new (require('./js/JumpFm.js').JumpFm)(require('electron').remote.getGlobal('argv'))
console.timeEnd('main')
}, false)
================================================
FILE: ts/shortway.ts
================================================
const keyCodes: { [name: string]: number } = {
backspace: 8,
tab: 9,
enter: 13,
pause: 19,
esc: 27,
space: 32,
pageup: 33,
pagedown: 34,
end: 35,
home: 36,
left: 37,
up: 38,
right: 39,
down: 40,
insert: 45,
del: 46,
slash: 191,
0: 48,
1: 49,
2: 50,
3: 51,
4: 52,
5: 53,
6: 54,
7: 55,
8: 56,
9: 57,
a: 65,
b: 66,
c: 67,
d: 68,
e: 69,
f: 70,
g: 71,
h: 72,
i: 73,
j: 74,
k: 75,
l: 76,
m: 77,
n: 78,
o: 79,
p: 80,
q: 81,
r: 82,
s: 83,
t: 84,
u: 85,
v: 86,
w: 87,
x: 88,
y: 89,
z: 90,
f1: 112,
f2: 113,
f3: 114,
f4: 115,
f5: 116,
f6: 117,
f7: 118,
f8: 119,
f9: 120,
f10: 121,
f11: 122,
f12: 123,
numLock: 144,
scrollLock: 145,
';': 186,
'=': 187,
',': 188,
'-': 189,
'.': 190,
'/': 191,
'`': 192,
'[': 219,
'\\': 220,
']': 221,
"'": 221
}
const includes = (array, item) => array.indexOf(item) > -1
export const shortway = (command, callback) => {
const keys = command.split('+')
const key = keys.filter(key => keyCodes[key])[0]
const keyCode = keyCodes[key]
const ctrl = keys.some(key => key === 'ctrl')
const shift = keys.some(key => key === 'shift')
const alt = keys.some(key => key === 'alt')
if (!keyCode) throw new Error(`can't find keycode for command ${command}`)
return function (e) {
if (e.ctrlKey === ctrl &&
e.shiftKey === shift &&
e.altKey === alt &&
e.keyCode === keyCode) {
callback(e)
return false
}
}
}
================================================
FILE: tsconfig.json
================================================
{
"compilerOptions": {
"outDir": "js",
"target": "es2017",
"module": "commonjs",
"sourceMap": true,
"lib": [
"dom",
"es2017",
"es2015.promise"
]
}
}
================================================
FILE: updates.json
================================================
{
"linux-x64-prod": {
"readme": "JumpFm",
"update": "https://github.com/JumpFm/jumpfm/releases/download/v1.0.5/JumpFm-1.0.5-x86_64.AppImage",
"install": "https://github.com/JumpFm/jumpfm/releases/download/v1.0.5/JumpFm-1.0.5-x86_64.AppImage",
"version": "1.0.5"
},
"win32-x64-prod": {
"readme": "JumpFm",
"update": "https://github.com/JumpFm/jumpfm/releases/download/v1.0.5/jumpfm-setup-1.0.5.exe",
"install": "https://github.com/JumpFm/jumpfm/releases/download/v1.0.5/jumpfm-setup-1.0.5.exe",
"version": "1.0.5"
},
"darwin-x64-prod": {
"readme": "JumpFm",
"update": "https://github.com/heywoodlh/jumpfm/releases/download/v1.0.2/jumpfm-1.0.2.dmg",
"install": "https://github.com/heywoodlh/jumpfm/releases/download/v1.0.2/jumpfm-1.0.2.dmg",
"version": "1.0.2"
}
}