├── .gitignore ├── LICENSE ├── README.md ├── _data └── demo.csv ├── babel.config.js ├── build ├── README_BUILD.md ├── icon.png └── icon_src │ ├── cat.png │ └── icon.xcf ├── changelog.md ├── demo.gif ├── notes.md ├── package-lock.json ├── package.json ├── public └── index.html ├── screenshot.png ├── src ├── main │ ├── background.js │ └── menus │ │ ├── _darwin.js │ │ ├── _file.js │ │ ├── _info.js │ │ ├── _view.js │ │ └── menu.js ├── renderer │ ├── App.vue │ ├── assets │ │ ├── angle-right.svg │ │ ├── cat.jpg │ │ ├── cat.png │ │ ├── file-download.svg │ │ ├── file-import-light.svg │ │ ├── file-import.svg │ │ ├── file-plus.svg │ │ ├── info-circle.svg │ │ ├── logo.png │ │ ├── long-arrow-right.svg │ │ ├── plus-circle.svg │ │ ├── plus-square.svg │ │ ├── plus.svg │ │ ├── redo-alt.svg │ │ ├── redo.svg │ │ └── tasks.svg │ ├── components │ │ ├── Form │ │ │ ├── TheFileSelector.vue │ │ │ ├── TheFileSelectorModal.vue │ │ │ ├── TheForm.vue │ │ │ ├── TheHeadersSelector.vue │ │ │ ├── TheOutput.vue │ │ │ └── TheResetBtn.vue │ │ ├── TheFooter.vue │ │ ├── TheHeader.vue │ │ └── TheProgressBar.vue │ └── main.js └── shared │ └── fileHelper.js └── vue.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | *.env 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw* 23 | 24 | #Electron-builder output 25 | /dist_electron -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2019 Brian Zelip 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # concats 2 | 3 | Cross platform desktop app for outputting a single-column csv file of rows of concatenated fields from an input delimiter-separated values file. 4 | 5 | Accepts .csv, .tsv, and extension-less files (_data.csv_, _data.tsv_, _data_) as input. 6 | 7 | ![demo](demo.gif) 8 | 9 | ## Download 10 | 11 | v1.13.3 12 | 13 | - [macOS](https://github.com/brianzelip/concats/releases/download/v1.13.13/concats-1.13.13.dmg) (46.9 MB) 14 | - [Windows](https://github.com/brianzelip/concats/releases/download/v1.13.13/concats.Setup.1.13.13.exe) (34.8 MB) 15 | 16 | ## Made with 17 | 18 | - [electron](https://github.com/electron/electron) 19 | - [vue](https://github.com/vuejs/vue) 20 | - [vue-cli-plugin-electron-builder](https://github.com/nklayman/vue-cli-plugin-electron-builder) 21 | 22 | ## Development 23 | 24 | To run concats locally (_requires [Node.js](https://nodejs.org) v8.9 or above_): 25 | 26 | ```bash 27 | 28 | git clone git@github.com/brianzelip/concats.git 29 | 30 | cd concats 31 | 32 | npm install 33 | 34 | npm run electron:serve 35 | 36 | ``` 37 | 38 | ## Build binaries 39 | 40 | To build the executable binary for `$PLATFORM`, you must have [Node.js](https://nodejs.org) v8.9 or above on `$PLATFORM`, and run: 41 | 42 | ```bash 43 | git clone git@github.com/brianzelip/concats.git 44 | 45 | cd concats 46 | 47 | npm install 48 | 49 | npm run electron:build 50 | ``` 51 | -------------------------------------------------------------------------------- /_data/demo.csv: -------------------------------------------------------------------------------- 1 | "Date","Meeting ID","Group Name","Meeting Rep Name","Meeting Rep Phone" 2 | "1/6/2019","102","BBB","Rob","5559387654" 3 | "1/13/2019","102","CYE","Tara","9876554321" 4 | "1/20/2019","102","UYT","Glen","3437654652" 5 | "1/27/2019","102","OEK","Jeff","4542445652" 6 | "1/6/2019","103","WWB","Paul","3159396652" 7 | "1/13/2019","103","KNR","Lottie","6925914652" 8 | "1/20/2019","103","PLL","Patti","3987074652" 9 | "1/27/2019","103","MWW","Dominic","4317981652" 10 | "1/13/2019","104","QPD","Sonya","7158055652" 11 | "1/13/2019","101","IOA","Hara","7158055652" -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /build/README_BUILD.md: -------------------------------------------------------------------------------- 1 | # README for build/ 2 | 3 | This directory exists solely for generating an app icon (under _most_ scenarios), as suggested via the [electron-builder docs](https://www.electron.build/icons#macos). 4 | 5 | See ./icon_src/ for the native GIMP logo file. 6 | -------------------------------------------------------------------------------- /build/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brianzelip/concats/d762a4694274048f3c7dd9ad162018521f02c58e/build/icon.png -------------------------------------------------------------------------------- /build/icon_src/cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brianzelip/concats/d762a4694274048f3c7dd9ad162018521f02c58e/build/icon_src/cat.png -------------------------------------------------------------------------------- /build/icon_src/icon.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brianzelip/concats/d762a4694274048f3c7dd9ad162018521f02c58e/build/icon_src/icon.xcf -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # changelog 2 | 3 | _started as of v1.4.0, on the concats feature branch_ 4 | 5 | ## `components` branch, starting at v1.4.0 6 | 7 | Breaking up into components means whittling away at `TheInput.vue`. 8 | 9 | This branch impacted the development experience, not the app user experience. 10 | 11 | Basically, this feature branch work turned this directory structure: 12 | 13 | ``` 14 | src/components 15 | ├── global 16 | │ └── TheHeader.vue 17 | ├── Input 18 | │ └── TheInput.vue 19 | └── Output 20 | └── TheOutput.vue 21 | ``` 22 | 23 | into this: 24 | 25 | ``` 26 | src/components 27 | ├── Form 28 | │ ├── TheControls.vue 29 | │ ├── TheFileSelector.vue 30 | │ ├── TheForm.vue 31 | │ ├── TheHeadersSelection.vue 32 | │ ├── TheHeadersSelector.vue 33 | │ └── TheOutput.vue 34 | ├── TheFooter.vue 35 | └── TheHeader.vue 36 | ``` 37 | 38 | This feature branch created the minor bump to v1.5.0. 39 | 40 | ## `app-steps-transitions` branch, starting at v1.5.0 41 | 42 | Design the app's user steps so that, instead of scrolling down the page as steps are completed, each completed step transitions away to make room for the next step. So in this scenario, there is no scrolling beyond the size of the app window. 43 | 44 | ## `redesign` branch, starting at v1.6.2 45 | 46 | - replace header with thin icon progress bar showing the app workflow steps in total and which step the user is currently on 47 | - add reset button to this progress bar area 48 | - reconsider metadata from header and footer, make it all available via OS native menus, NOT via app UI 49 | - expand the drop zone, and the click area for user to select input file 50 | - make app ui more minimal centered around app workflow 51 | 52 | ## `app-metdata` feature branch, starting at v1.7.0 53 | 54 | - removes footer 55 | - adds completed metadata fields in package.json 56 | 57 | ## `menus` feature branch, starting at v1.7.4 58 | 59 | - implements native OS menu system, including: 60 | - darwin menu if on darwin build 61 | - file 62 | - view 63 | - info - including an external link to app source repo and About concats pop up dialog on windows builds 64 | - new app icon 65 | - I read through the source code for Hyper by Zeit, and used their module approach to the menu organization 66 | 67 | ## `progress-bar-ui` feature branch, starting at v1.8.4 68 | 69 | - adds step number underneath each step icon in progress bar via ::after psuedo element, opacity, and props on div wrapper around each svg 70 | 71 | ## `input-file-type` feature branch, starting at v1.9.1 72 | 73 | - shows temporary modal when invalid file types are dropped onto TheFileSelector dropzone 74 | - adds helpful text about valid file types to TheFileSelector 75 | - here's [the !SO answer](https://stackoverflow.com/a/48481398/2145103) that helped me achieve the temporary modal solution by using the mounted() hook in the modal component to implement the `setTimeout()` 76 | 77 | ## `progress-svg-opacity` feature branch, starting at v1.10.0 78 | 79 | - change the opacity transition of the angle arrows in the progress bar to point the user to the next step 80 | - gets rid of all `.iscomplete` classes in favor of `.iscurrent` 81 | 82 | ## `maintenance` feature branch, starting at v1.11.0 83 | 84 | - refactor src/\* into main/ and renderer/ 85 | - add README to build/ 86 | 87 | ## `allow-plain-text` feature branch, starting at v1.12.0 88 | 89 | - allow user to input an extension-less file or a csv/tsv file 90 | - introduces a directory of shared modules between `src/main/` and `src/renderer/` 91 | 92 | ## `progress-bar-update` feature branch, starting at v1.13.1 93 | 94 | - shows progress bar step numbers by default, and only the current step in bold 95 | - makes LICENSE current 96 | - cleans up readme 97 | 98 | ## `demo` feature branch, starting at v1.13.5 99 | 100 | - create animated gif from screenshot video of using concats 101 | - updates README with demo 102 | - cleans up README to close some issues 103 | 104 | ## [1.13.11] - 2019-05-30 105 | 106 | - starting point: v1.13.10 107 | - ending point: v1.13.11 108 | - branch name: security 109 | 110 | - steps: 111 | - run `npm audit fix` 112 | - install 1/2 peer dependencies (see 26ec9498d4 commit message) 113 | 114 | ## [1.13.12] - 2019-07-27 115 | 116 | - starting point: v1.13.11 117 | - ending point: v1.13.12 118 | - branch name: sec2019-07-27 119 | 120 | - steps: 121 | - run `npm audit fix` 122 | - install earlier version of ajv module as peer dependency via npm warning 123 | 124 | ## [1.13.13] - 2019-08-16 125 | 126 | ### Meta 127 | 128 | - branch: gh-release 129 | - description: I want to make use of GitHub's "release" feature to include of build binary files for mac and windows concats apps. See https://help.github.com/en/articles/creating-releases 130 | 131 | ### Updated 132 | 133 | - public/index.html: Remove favicon.ico import 134 | 135 | ### Deleted 136 | 137 | - public/favicon.ico 138 | 139 | ## [1.13.14] - 2020-01-25 140 | 141 | - branch: vuln-patch 142 | - description: Run `npm audit fix` to fix vulnerabilities 143 | 144 | ### Updated 145 | 146 | - package-lock.json 147 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brianzelip/concats/d762a4694274048f3c7dd9ad162018521f02c58e/demo.gif -------------------------------------------------------------------------------- /notes.md: -------------------------------------------------------------------------------- 1 | ## User workflow: 2 | 3 | 1. Select file 4 | 2. Select headers 5 | 3. Output 6 | 4. Reset 7 | 8 | ## Components 9 | 10 | - Input 11 | - FileSelector 12 | - HeadersSelector 13 | - Submit 14 | - Reset 15 | - Output 16 | - Output 17 | 18 | ## Menu 19 | 20 | what fundamental app behaviors should be available in the menus? 21 | 22 | - load file 23 | - reset app 24 | - view dev tools 25 | - get info about the app 26 | 27 | The following are behavior of secondary importance: 28 | 29 | - view full screen 30 | - maximize 31 | - minimize 32 | - close 33 | - all the apple-specific stuff like, hide, hide others, etc. 34 | - 35 | 36 | There could be: 37 | 38 | - preferences, whereby the user sets default input and output directories 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "concats", 3 | "description": "Output a single-column csv file containing rows of concatenated fields from an input delimiter-separated values file. Desktop app built using Electron.js and Vue.js.", 4 | "version": "1.13.14", 5 | "private": false, 6 | "author": { 7 | "name": "Brian Zelip" 8 | }, 9 | "license": "MIT", 10 | "homepage": "https://github.com/brianzelip/concats", 11 | "repository": "https://github.com/brianzelip/concats", 12 | "scripts": { 13 | "serve": "vue-cli-service serve", 14 | "build": "vue-cli-service build", 15 | "lint": "vue-cli-service lint", 16 | "electron:build": "vue-cli-service electron:build", 17 | "electron:serve": "vue-cli-service electron:serve", 18 | "postinstall": "electron-builder install-app-deps" 19 | }, 20 | "dependencies": { 21 | "ajv": "^5.5.2", 22 | "csvtojson": "^2.0.8", 23 | "vue": "^2.5.21" 24 | }, 25 | "devDependencies": { 26 | "@vue/cli-plugin-babel": "^3.2.0", 27 | "@vue/cli-plugin-eslint": "^3.2.0", 28 | "@vue/cli-service": "^3.8.0", 29 | "babel-eslint": "^10.0.1", 30 | "electron": "^2.0.0", 31 | "eslint": "^5.8.0", 32 | "eslint-plugin-vue": "^5.0.0-0", 33 | "vue-cli-plugin-electron-builder": "^1.3.4", 34 | "vue-svg-loader": "^0.11.0", 35 | "vue-template-compiler": "^2.5.21" 36 | }, 37 | "eslintConfig": { 38 | "root": true, 39 | "env": { 40 | "node": true 41 | }, 42 | "extends": [ 43 | "plugin:vue/essential", 44 | "eslint:recommended" 45 | ], 46 | "rules": {}, 47 | "parserOptions": { 48 | "parser": "babel-eslint" 49 | } 50 | }, 51 | "postcss": { 52 | "plugins": { 53 | "autoprefixer": {} 54 | } 55 | }, 56 | "browserslist": [ 57 | "> 1%", 58 | "last 2 versions", 59 | "not ie <= 8" 60 | ], 61 | "main": "background.js" 62 | } 63 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | concats 8 | 15 | 16 | 17 | 23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brianzelip/concats/d762a4694274048f3c7dd9ad162018521f02c58e/screenshot.png -------------------------------------------------------------------------------- /src/main/background.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { app, protocol, BrowserWindow, Menu } from 'electron'; 4 | import { 5 | createProtocol, 6 | installVueDevtools 7 | } from 'vue-cli-plugin-electron-builder/lib'; 8 | 9 | import appMenu from './menus/menu.js'; 10 | 11 | const isDevelopment = process.env.NODE_ENV !== 'production'; 12 | 13 | // Keep a global reference of the window object, if you don't, the window will 14 | // be closed automatically when the JavaScript object is garbage collected. 15 | let win; 16 | 17 | // Standard scheme must be registered before the app is ready 18 | protocol.registerStandardSchemes(['app'], { secure: true }); 19 | function createWindow() { 20 | // Create the browser window. 21 | win = new BrowserWindow({ 22 | width: 1000, 23 | height: 800, 24 | show: false 25 | }); 26 | 27 | // show window gracefully 28 | // via https://electronjs.org/docs/api/browser-window#showing-window-gracefully 29 | win.once('ready-to-show', () => { 30 | win.show(); 31 | }); 32 | 33 | if (isDevelopment || process.env.IS_TEST) { 34 | // Load the url of the dev server if in development mode 35 | win.loadURL(process.env.WEBPACK_DEV_SERVER_URL); 36 | if (!process.env.IS_TEST) win.webContents.openDevTools(); 37 | } else { 38 | createProtocol('app'); 39 | // Load the index.html when not in development 40 | win.loadURL('app://./index.html'); 41 | } 42 | 43 | win.on('closed', () => { 44 | win = null; 45 | }); 46 | } 47 | 48 | // Quit when all windows are closed. 49 | app.on('window-all-closed', () => { 50 | // On macOS it is common for applications and their menu bar 51 | // to stay active until the user quits explicitly with Cmd + Q 52 | if (process.platform !== 'darwin') { 53 | app.quit(); 54 | } 55 | }); 56 | 57 | app.on('activate', () => { 58 | // On macOS it's common to re-create a window in the app when the 59 | // dock icon is clicked and there are no other windows open. 60 | if (win === null) { 61 | createWindow(); 62 | } 63 | }); 64 | 65 | // This method will be called when Electron has finished 66 | // initialization and is ready to create browser windows. 67 | // Some APIs can only be used after this event occurs. 68 | app.on('ready', async () => { 69 | if (isDevelopment && !process.env.IS_TEST) { 70 | // Install Vue Devtools 71 | await installVueDevtools(); 72 | } 73 | createWindow(); 74 | Menu.setApplicationMenu(appMenu(win)); 75 | }); 76 | 77 | // Exit cleanly on request from parent process in development mode. 78 | if (isDevelopment) { 79 | if (process.platform === 'win32') { 80 | process.on('message', data => { 81 | if (data === 'graceful-exit') { 82 | app.quit(); 83 | } 84 | }); 85 | } else { 86 | process.on('SIGTERM', () => { 87 | app.quit(); 88 | }); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/menus/_darwin.js: -------------------------------------------------------------------------------- 1 | import { app } from 'electron'; 2 | 3 | export default showAbout => { 4 | return { 5 | label: app.getName(), 6 | submenu: [ 7 | { 8 | label: 'About concats', 9 | click() { 10 | showAbout(); 11 | } 12 | }, 13 | { type: 'separator' }, 14 | { role: 'services' }, 15 | { type: 'separator' }, 16 | { role: 'hide' }, 17 | { role: 'hideothers' }, 18 | { role: 'unhide' }, 19 | { type: 'separator' }, 20 | { role: 'quit' } 21 | ] 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /src/main/menus/_file.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | 3 | import { dialog } from 'electron'; 4 | 5 | import { fCheck, openDialogOptions } from '../../shared/fileHelper.js'; 6 | 7 | export default function(BrowserWindow) { 8 | const fileMenu = { 9 | label: 'File', 10 | submenu: [ 11 | { 12 | label: 'Open', 13 | accelerator: 'CmdOrCtrl+O', 14 | click() { 15 | dialog.showOpenDialog(openDialogOptions, filePaths => { 16 | if (filePaths != undefined) { 17 | fCheck.fileIsValid(filePaths[0]) 18 | ? fs.readFile(filePaths[0], 'utf-8', function( 19 | err, 20 | fileAsString 21 | ) { 22 | if (err) { 23 | throw err; 24 | } 25 | BrowserWindow.webContents.send('file-input', fileAsString); 26 | }) 27 | : console.log(fCheck.errorMsg(filePaths[0])); 28 | } 29 | }); 30 | } 31 | }, 32 | { 33 | label: 'Reset', 34 | click() { 35 | BrowserWindow.webContents.send('reset-app'); 36 | }, 37 | accelerator: 'CmdOrCtrl+R' 38 | } 39 | ] 40 | }; 41 | 42 | if (process.platform !== 'darwin') { 43 | fileMenu.submenu.push( 44 | { type: 'separator' }, 45 | { role: 'quit', accelerator: 'Ctrl+Q' } 46 | ); 47 | } 48 | 49 | return fileMenu; 50 | } 51 | -------------------------------------------------------------------------------- /src/main/menus/_info.js: -------------------------------------------------------------------------------- 1 | import { app, shell } from 'electron'; 2 | 3 | import { homepage } from '../../../package.json'; 4 | 5 | export default showAbout => { 6 | const submenu = [ 7 | { 8 | label: 'Go to concats homepage →', 9 | click() { 10 | shell.openExternal(homepage); 11 | } 12 | } 13 | ]; 14 | 15 | if (process.platform !== 'darwin') { 16 | submenu.unshift({ 17 | label: `About ${app.getName()}`, 18 | click() { 19 | showAbout(); 20 | } 21 | }); 22 | } 23 | 24 | return { 25 | label: 'Info', 26 | submenu 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /src/main/menus/_view.js: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return { 3 | label: 'View', 4 | submenu: [{ role: 'toggledevtools' }, { role: 'togglefullscreen' }] 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /src/main/menus/menu.js: -------------------------------------------------------------------------------- 1 | import { app, Menu, dialog } from 'electron'; 2 | 3 | import fileMenu from './_file'; 4 | import viewMenu from './_view'; 5 | import infoMenu from './_info'; 6 | import darwinMenu from './_darwin'; 7 | 8 | export default BrowserWindow => { 9 | const currentYear = new Date().getFullYear(); 10 | 11 | const showAbout = () => { 12 | dialog.showMessageBox({ 13 | type: 'info', 14 | title: `About ${app.getName()}`, 15 | message: `${app.getName()} v${app.getVersion()}`, 16 | detail: `Made with Electron.js and Vue.js.\nSee the Info menu for a link to the source code.\n\nCopyright © 2018-${currentYear} Brian Zelip`, 17 | buttons: [] 18 | }); 19 | }; 20 | 21 | const template = [ 22 | ...(process.platform === 'darwin' ? [darwinMenu(showAbout)] : []), 23 | fileMenu(BrowserWindow), 24 | viewMenu(), 25 | infoMenu(showAbout) 26 | ]; 27 | 28 | return Menu.buildFromTemplate(template); 29 | }; 30 | -------------------------------------------------------------------------------- /src/renderer/App.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 51 | 52 | 70 | -------------------------------------------------------------------------------- /src/renderer/assets/angle-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brianzelip/concats/d762a4694274048f3c7dd9ad162018521f02c58e/src/renderer/assets/cat.jpg -------------------------------------------------------------------------------- /src/renderer/assets/cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brianzelip/concats/d762a4694274048f3c7dd9ad162018521f02c58e/src/renderer/assets/cat.png -------------------------------------------------------------------------------- /src/renderer/assets/file-download.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/file-import-light.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/file-import.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/file-plus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/info-circle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brianzelip/concats/d762a4694274048f3c7dd9ad162018521f02c58e/src/renderer/assets/logo.png -------------------------------------------------------------------------------- /src/renderer/assets/long-arrow-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/plus-circle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/plus-square.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/plus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/redo-alt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/redo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/assets/tasks.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/renderer/components/Form/TheFileSelector.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 72 | 73 | 120 | -------------------------------------------------------------------------------- /src/renderer/components/Form/TheFileSelectorModal.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 27 | 28 | 58 | -------------------------------------------------------------------------------- /src/renderer/components/Form/TheForm.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 160 | 161 | 180 | -------------------------------------------------------------------------------- /src/renderer/components/Form/TheHeadersSelector.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 63 | 64 | 144 | -------------------------------------------------------------------------------- /src/renderer/components/Form/TheOutput.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 23 | 24 | -------------------------------------------------------------------------------- /src/renderer/components/Form/TheResetBtn.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 69 | 70 | -------------------------------------------------------------------------------- /src/renderer/components/TheFooter.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 35 | 36 | 45 | -------------------------------------------------------------------------------- /src/renderer/components/TheHeader.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | -------------------------------------------------------------------------------- /src/renderer/components/TheProgressBar.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 68 | 69 | 124 | -------------------------------------------------------------------------------- /src/renderer/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import App from './App.vue'; 3 | 4 | Vue.config.productionTip = false; 5 | 6 | new Vue({ 7 | render: h => h(App) 8 | }).$mount('#app'); 9 | -------------------------------------------------------------------------------- /src/shared/fileHelper.js: -------------------------------------------------------------------------------- 1 | const fCheck = { 2 | fileIsValid(file) { 3 | // analyze file extension approach via https://stackoverflow.com/a/22864057/2145103 4 | function hasExtension(file) { 5 | return ( 6 | file 7 | .split('/') 8 | .pop() 9 | .split('.').length > 1 10 | ); 11 | } 12 | function getExtension(file) { 13 | return file 14 | .split('/') 15 | .pop() 16 | .split('.') 17 | .pop(); 18 | } 19 | function isExtensionWeLike(ext) { 20 | return ext === 'csv' || ext === 'tsv'; 21 | } 22 | return (hasExtension(file) && isExtensionWeLike(getExtension(file))) || 23 | !hasExtension(file) 24 | ? true 25 | : false; 26 | }, 27 | errorMsg(fileName) { 28 | return `File selected had wrong extension (${fileName}); please select a csv/tsv file or a data file with no extension.`; 29 | } 30 | }; 31 | 32 | const openDialogOptions = { 33 | title: 'Select a data file', 34 | properties: ['openFile'] 35 | }; 36 | 37 | export { fCheck, openDialogOptions }; 38 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | chainWebpack: config => { 3 | // svgRule code via 4 | // https://cli.vuejs.org/guide/webpack.html#replacing-loaders-of-a-rule 5 | const svgRule = config.module.rule('svg'); 6 | 7 | // clear all existing loaders. 8 | // if you don't do this, the loader below will be appended to 9 | // existing loaders of the rule. 10 | svgRule.uses.clear(); 11 | 12 | // add replacement loader(s) 13 | svgRule.use('vue-svg-loader').loader('vue-svg-loader'); 14 | 15 | // change webpack entry point from src/main.js 16 | // code via https://stackoverflow.com/a/52773981/2145103 17 | // ...figured it out after a bit of trials and trib, 18 | // ...plus `vue inspect` on the cli 19 | 20 | // clear the existing entry point 21 | config.entry('app').clear(); 22 | // add your custom entry point 23 | config.entry('app').add('./src/renderer/main.js'); 24 | }, 25 | pluginOptions: { 26 | electronBuilder: { 27 | mainProcessFile: './src/main/background.js' 28 | } 29 | } 30 | }; 31 | --------------------------------------------------------------------------------