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 ================================================ [![Build Status](https://travis-ci.org/Gilad-Kutiel-App/jumpfm.svg?branch=master)](https://travis-ci.org/Gilad-Kutiel-App/jumpfm) [![Build status](https://ci.appveyor.com/api/projects/status/g9ggpk5578fq56x2?svg=true)](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: ![](/misc/dev.png) ================================================ 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" } }