├── .babelrc ├── .editorconfig ├── .electron-vue ├── build.js ├── dev-client.js ├── dev-runner.js ├── webpack.main.config.js ├── webpack.renderer.config.js └── webpack.web.config.js ├── .gitignore ├── .travis.yml ├── README.md ├── appveyor.yml ├── build └── icons │ ├── 256x256.png │ ├── icon.icns │ └── icon.ico ├── package.json ├── src ├── assets │ ├── fonts │ │ ├── browser │ │ │ ├── browser.css.hbs │ │ │ ├── browser.font.js │ │ │ └── sources │ │ │ │ ├── new-tab.svg │ │ │ │ └── tabs.svg │ │ └── roboto │ │ │ ├── Bold │ │ │ ├── RobotoBoldWebfont.ttf │ │ │ └── RobotoBoldWebfont.woff │ │ │ ├── Light │ │ │ ├── RobotoLightWebfont.ttf │ │ │ └── RobotoLightWebfont.woff │ │ │ ├── Medium │ │ │ ├── RobotoMediumWebfont.ttf │ │ │ └── RobotoMediumWebfont.woff │ │ │ ├── Regular │ │ │ ├── RobotoRegularWebfont.ttf │ │ │ └── RobotoRegularWebfont.woff │ │ │ └── Thin │ │ │ ├── RobotoThinWebfont.ttf │ │ │ └── RobotoThinWebfont.woff │ └── scss │ │ ├── _variables.scss │ │ ├── fonts │ │ ├── families │ │ │ └── _roboto.scss │ │ └── index.scss │ │ └── scrollbars │ │ └── _tabs.scss ├── index.ejs ├── main │ ├── index.dev.js │ └── index.js └── renderer │ ├── App.vue │ ├── components │ ├── side │ │ ├── Bar.vue │ │ ├── Controls.vue │ │ ├── Credentials.vue │ │ ├── Head.vue │ │ ├── index.js │ │ └── tabs │ │ │ ├── Tab.vue │ │ │ └── Tabs.vue │ └── view │ │ ├── controls │ │ ├── action.vue │ │ ├── bar.vue │ │ └── url.vue │ │ └── pages │ │ ├── Content.vue │ │ └── Empty.vue │ ├── main.js │ ├── store │ ├── index.js │ └── modules │ │ └── tabs.js │ └── utils │ └── hashing.js ├── static └── .gitkeep └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "comments": false, 3 | "env": { 4 | "main": { 5 | "presets": [ 6 | ["env", { 7 | "targets": { "node": 7 } 8 | }], 9 | "stage-0" 10 | ] 11 | }, 12 | "renderer": { 13 | "presets": [ 14 | ["env", { 15 | "modules": false 16 | }], 17 | "stage-0" 18 | ] 19 | }, 20 | "web": { 21 | "presets": [ 22 | ["env", { 23 | "modules": false 24 | }], 25 | "stage-0" 26 | ] 27 | } 28 | }, 29 | "plugins": ["transform-runtime"] 30 | } 31 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.electron-vue/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.NODE_ENV = 'production' 4 | 5 | const { say } = require('cfonts') 6 | const chalk = require('chalk') 7 | const del = require('del') 8 | const { spawn } = require('child_process') 9 | const webpack = require('webpack') 10 | const Multispinner = require('multispinner') 11 | 12 | 13 | const mainConfig = require('./webpack.main.config') 14 | const rendererConfig = require('./webpack.renderer.config') 15 | const webConfig = require('./webpack.web.config') 16 | 17 | const doneLog = chalk.bgGreen.white(' DONE ') + ' ' 18 | const errorLog = chalk.bgRed.white(' ERROR ') + ' ' 19 | const okayLog = chalk.bgBlue.white(' OKAY ') + ' ' 20 | const isCI = process.env.CI || false 21 | 22 | if (process.env.BUILD_TARGET === 'clean') clean() 23 | else if (process.env.BUILD_TARGET === 'web') web() 24 | else build() 25 | 26 | function clean () { 27 | del.sync(['build/*', '!build/icons', '!build/icons/icon.*']) 28 | console.log(`\n${doneLog}\n`) 29 | process.exit() 30 | } 31 | 32 | function build () { 33 | greeting() 34 | 35 | del.sync(['dist/electron/*', '!.gitkeep']) 36 | 37 | const tasks = ['main', 'renderer'] 38 | const m = new Multispinner(tasks, { 39 | preText: 'building', 40 | postText: 'process' 41 | }) 42 | 43 | let results = '' 44 | 45 | m.on('success', () => { 46 | process.stdout.write('\x1B[2J\x1B[0f') 47 | console.log(`\n\n${results}`) 48 | console.log(`${okayLog}take it away ${chalk.yellow('`electron-builder`')}\n`) 49 | process.exit() 50 | }) 51 | 52 | pack(mainConfig).then(result => { 53 | results += result + '\n\n' 54 | m.success('main') 55 | }).catch(err => { 56 | m.error('main') 57 | console.log(`\n ${errorLog}failed to build main process`) 58 | console.error(`\n${err}\n`) 59 | process.exit(1) 60 | }) 61 | 62 | pack(rendererConfig).then(result => { 63 | results += result + '\n\n' 64 | m.success('renderer') 65 | }).catch(err => { 66 | m.error('renderer') 67 | console.log(`\n ${errorLog}failed to build renderer process`) 68 | console.error(`\n${err}\n`) 69 | process.exit(1) 70 | }) 71 | } 72 | 73 | function pack (config) { 74 | return new Promise((resolve, reject) => { 75 | config.mode = 'production' 76 | webpack(config, (err, stats) => { 77 | if (err) reject(err.stack || err) 78 | else if (stats.hasErrors()) { 79 | let err = '' 80 | 81 | stats.toString({ 82 | chunks: false, 83 | colors: true 84 | }) 85 | .split(/\r?\n/) 86 | .forEach(line => { 87 | err += ` ${line}\n` 88 | }) 89 | 90 | reject(err) 91 | } else { 92 | resolve(stats.toString({ 93 | chunks: false, 94 | colors: true 95 | })) 96 | } 97 | }) 98 | }) 99 | } 100 | 101 | function web () { 102 | del.sync(['dist/web/*', '!.gitkeep']) 103 | webConfig.mode = 'production' 104 | webpack(webConfig, (err, stats) => { 105 | if (err || stats.hasErrors()) console.log(err) 106 | 107 | console.log(stats.toString({ 108 | chunks: false, 109 | colors: true 110 | })) 111 | 112 | process.exit() 113 | }) 114 | } 115 | 116 | function greeting () { 117 | const cols = process.stdout.columns 118 | let text = '' 119 | 120 | if (cols > 85) text = 'lets-build' 121 | else if (cols > 60) text = 'lets-|build' 122 | else text = false 123 | 124 | if (text && !isCI) { 125 | say(text, { 126 | colors: ['yellow'], 127 | font: 'simple3d', 128 | space: false 129 | }) 130 | } else console.log(chalk.yellow.bold('\n lets-build')) 131 | console.log() 132 | } -------------------------------------------------------------------------------- /.electron-vue/dev-client.js: -------------------------------------------------------------------------------- 1 | const hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') 2 | 3 | hotClient.subscribe(event => { 4 | /** 5 | * Reload browser when HTMLWebpackPlugin emits a new index.html 6 | * 7 | * Currently disabled until jantimon/html-webpack-plugin#680 is resolved. 8 | * https://github.com/SimulatedGREG/electron-vue/issues/437 9 | * https://github.com/jantimon/html-webpack-plugin/issues/680 10 | */ 11 | // if (event.action === 'reload') { 12 | // window.location.reload() 13 | // } 14 | 15 | /** 16 | * Notify `mainWindow` when `main` process is compiling, 17 | * giving notice for an expected reload of the `electron` process 18 | */ 19 | if (event.action === 'compiling') { 20 | document.body.innerHTML += ` 21 | 34 | 35 |
36 | Compiling Main Process... 37 |
38 | ` 39 | } 40 | }) 41 | -------------------------------------------------------------------------------- /.electron-vue/dev-runner.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const chalk = require('chalk') 4 | const electron = require('electron') 5 | const path = require('path') 6 | const { say } = require('cfonts') 7 | const { spawn } = require('child_process') 8 | const webpack = require('webpack') 9 | const WebpackDevServer = require('webpack-dev-server') 10 | const webpackHotMiddleware = require('webpack-hot-middleware') 11 | 12 | const mainConfig = require('./webpack.main.config') 13 | const rendererConfig = require('./webpack.renderer.config') 14 | 15 | let electronProcess = null 16 | let manualRestart = false 17 | let hotMiddleware 18 | 19 | function logStats (proc, data) { 20 | let log = '' 21 | 22 | log += chalk.yellow.bold(`┏ ${proc} Process ${new Array((19 - proc.length) + 1).join('-')}`) 23 | log += '\n\n' 24 | 25 | if (typeof data === 'object') { 26 | data.toString({ 27 | colors: true, 28 | chunks: false 29 | }).split(/\r?\n/).forEach(line => { 30 | log += ' ' + line + '\n' 31 | }) 32 | } else { 33 | log += ` ${data}\n` 34 | } 35 | 36 | log += '\n' + chalk.yellow.bold(`┗ ${new Array(28 + 1).join('-')}`) + '\n' 37 | 38 | console.log(log) 39 | } 40 | 41 | function startRenderer () { 42 | return new Promise((resolve, reject) => { 43 | rendererConfig.entry.renderer = [path.join(__dirname, 'dev-client')].concat(rendererConfig.entry.renderer) 44 | rendererConfig.mode = 'development' 45 | const compiler = webpack(rendererConfig) 46 | hotMiddleware = webpackHotMiddleware(compiler, { 47 | log: false, 48 | heartbeat: 2500 49 | }) 50 | 51 | compiler.hooks.compilation.tap('compilation', compilation => { 52 | compilation.hooks.htmlWebpackPluginAfterEmit.tapAsync('html-webpack-plugin-after-emit', (data, cb) => { 53 | hotMiddleware.publish({ action: 'reload' }) 54 | cb() 55 | }) 56 | }) 57 | 58 | compiler.hooks.done.tap('done', stats => { 59 | logStats('Renderer', stats) 60 | }) 61 | 62 | const server = new WebpackDevServer( 63 | compiler, 64 | { 65 | contentBase: path.join(__dirname, '../'), 66 | quiet: true, 67 | before (app, ctx) { 68 | app.use(hotMiddleware) 69 | ctx.middleware.waitUntilValid(() => { 70 | resolve() 71 | }) 72 | } 73 | } 74 | ) 75 | 76 | server.listen(9080) 77 | }) 78 | } 79 | 80 | function startMain () { 81 | return new Promise((resolve, reject) => { 82 | mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main) 83 | mainConfig.mode = 'development' 84 | const compiler = webpack(mainConfig) 85 | 86 | compiler.hooks.watchRun.tapAsync('watch-run', (compilation, done) => { 87 | logStats('Main', chalk.white.bold('compiling...')) 88 | hotMiddleware.publish({ action: 'compiling' }) 89 | done() 90 | }) 91 | 92 | compiler.watch({}, (err, stats) => { 93 | if (err) { 94 | console.log(err) 95 | return 96 | } 97 | 98 | logStats('Main', stats) 99 | 100 | if (electronProcess && electronProcess.kill) { 101 | manualRestart = true 102 | process.kill(electronProcess.pid) 103 | electronProcess = null 104 | startElectron() 105 | 106 | setTimeout(() => { 107 | manualRestart = false 108 | }, 5000) 109 | } 110 | 111 | resolve() 112 | }) 113 | }) 114 | } 115 | 116 | function startElectron () { 117 | electronProcess = spawn(electron, ['--inspect=5858', path.join(__dirname, '../dist/electron/main.js')]) 118 | 119 | electronProcess.stdout.on('data', data => { 120 | electronLog(data, 'blue') 121 | }) 122 | electronProcess.stderr.on('data', data => { 123 | electronLog(data, 'red') 124 | }) 125 | 126 | electronProcess.on('close', () => { 127 | if (!manualRestart) process.exit() 128 | }) 129 | } 130 | 131 | function electronLog (data, color) { 132 | let log = '' 133 | data = data.toString().split(/\r?\n/) 134 | data.forEach(line => { 135 | log += ` ${line}\n` 136 | }) 137 | if (/[0-9A-z]+/.test(log)) { 138 | console.log( 139 | chalk[color].bold('┏ Electron -------------------') + 140 | '\n\n' + 141 | log + 142 | chalk[color].bold('┗ ----------------------------') + 143 | '\n' 144 | ) 145 | } 146 | } 147 | 148 | function greeting () { 149 | const cols = process.stdout.columns 150 | let text = '' 151 | 152 | if (cols > 104) text = 'electron-vue' 153 | else if (cols > 76) text = 'electron-|vue' 154 | else text = false 155 | 156 | if (text) { 157 | say(text, { 158 | colors: ['yellow'], 159 | font: 'simple3d', 160 | space: false 161 | }) 162 | } else console.log(chalk.yellow.bold('\n electron-vue')) 163 | console.log(chalk.blue(' getting ready...') + '\n') 164 | } 165 | 166 | function init () { 167 | greeting() 168 | 169 | Promise.all([startRenderer(), startMain()]) 170 | .then(() => { 171 | startElectron() 172 | }) 173 | .catch(err => { 174 | console.error(err) 175 | }) 176 | } 177 | 178 | init() -------------------------------------------------------------------------------- /.electron-vue/webpack.main.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.BABEL_ENV = 'main' 4 | 5 | const path = require('path') 6 | const {dependencies} = require('../package.json') 7 | const webpack = require('webpack') 8 | 9 | const BabiliWebpackPlugin = require('babili-webpack-plugin') 10 | 11 | let mainConfig = { 12 | entry: { 13 | main: path.join(__dirname, '../src/main/index.js') 14 | }, 15 | externals: [ 16 | ...Object.keys(dependencies || {}) 17 | ], 18 | module: { 19 | rules: [ 20 | { 21 | test: /\.js$/, 22 | use: 'babel-loader', 23 | exclude: /node_modules/ 24 | }, 25 | { 26 | test: /\.node$/, 27 | use: 'node-loader' 28 | } 29 | ] 30 | }, 31 | node: { 32 | __dirname: process.env.NODE_ENV !== 'production', 33 | __filename: process.env.NODE_ENV !== 'production' 34 | }, 35 | output: { 36 | filename: '[name].js', 37 | libraryTarget: 'commonjs2', 38 | path: path.join(__dirname, '../dist/electron') 39 | }, 40 | plugins: [ 41 | new webpack.NoEmitOnErrorsPlugin() 42 | ], 43 | resolve: { 44 | extensions: ['.js', '.json', '.node'], 45 | }, 46 | target: 'electron-main' 47 | } 48 | 49 | /** 50 | * Adjust mainConfig for development settings 51 | */ 52 | if (process.env.NODE_ENV !== 'production') { 53 | mainConfig.plugins.push( 54 | new webpack.DefinePlugin({ 55 | '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"` 56 | }) 57 | ) 58 | } 59 | 60 | /** 61 | * Adjust mainConfig for production settings 62 | */ 63 | if (process.env.NODE_ENV === 'production') { 64 | mainConfig.plugins.push( 65 | new BabiliWebpackPlugin(), 66 | new webpack.DefinePlugin({ 67 | 'process.env.NODE_ENV': '"production"' 68 | }) 69 | ) 70 | } 71 | 72 | module.exports = mainConfig 73 | -------------------------------------------------------------------------------- /.electron-vue/webpack.renderer.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.BABEL_ENV = 'renderer' 4 | 5 | const path = require('path') 6 | const {dependencies} = require('../package.json') 7 | const webpack = require('webpack') 8 | 9 | const BabiliWebpackPlugin = require('babili-webpack-plugin') 10 | const CopyWebpackPlugin = require('copy-webpack-plugin') 11 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 12 | const HtmlWebpackPlugin = require('html-webpack-plugin') 13 | const {VueLoaderPlugin} = require('vue-loader') 14 | 15 | /** 16 | * List of node_modules to include in webpack bundle 17 | * 18 | * Required for specific packages like Vue UI libraries 19 | * that provide pure *.vue files that need compiling 20 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals 21 | */ 22 | let whiteListedModules = ['vue'] 23 | 24 | let rendererConfig = { 25 | devtool: '#cheap-module-eval-source-map', 26 | entry: { 27 | renderer: path.join(__dirname, '../src/renderer/main.js') 28 | }, 29 | externals: [ 30 | ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)) 31 | ], 32 | module: { 33 | rules: [ 34 | { 35 | test: /\.scss$/, 36 | use: ['vue-style-loader', 'css-loader', 'sass-loader'] 37 | }, 38 | { 39 | test: /\.sass$/, 40 | use: ['vue-style-loader', 'css-loader', 'sass-loader?indentedSyntax'] 41 | }, 42 | { 43 | test: /\.less$/, 44 | use: ['vue-style-loader', 'css-loader', 'less-loader'] 45 | }, 46 | { 47 | test: /\.css$/, 48 | use: ['vue-style-loader', 'css-loader'] 49 | }, 50 | { 51 | test: /\.html$/, 52 | use: 'vue-html-loader' 53 | }, 54 | { 55 | test: /\.js$/, 56 | use: 'babel-loader', 57 | exclude: /node_modules/ 58 | }, 59 | { 60 | test: /\.node$/, 61 | use: 'node-loader' 62 | }, 63 | { 64 | test: /\.vue$/, 65 | use: { 66 | loader: 'vue-loader', 67 | options: { 68 | extractCSS: process.env.NODE_ENV === 'production', 69 | loaders: { 70 | sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', 71 | scss: 'vue-style-loader!css-loader!sass-loader', 72 | less: 'vue-style-loader!css-loader!less-loader' 73 | } 74 | } 75 | } 76 | }, 77 | { 78 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 79 | use: { 80 | loader: 'url-loader', 81 | query: { 82 | limit: 10000, 83 | name: 'imgs/[name]--[folder].[ext]' 84 | } 85 | } 86 | }, 87 | { 88 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 89 | loader: 'url-loader', 90 | options: { 91 | limit: 10000, 92 | name: 'media/[name]--[folder].[ext]' 93 | } 94 | }, 95 | { 96 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 97 | use: { 98 | loader: 'url-loader', 99 | query: { 100 | limit: 10000, 101 | name: 'fonts/[name]--[folder].[ext]' 102 | } 103 | }, 104 | }, 105 | { 106 | test: /\.font\.js/, 107 | use: [ 108 | 'style-loader', 109 | 'css-loader', 110 | { 111 | loader: 'webfonts-loader', 112 | } 113 | ] 114 | }, 115 | ] 116 | }, 117 | node: { 118 | __dirname: process.env.NODE_ENV !== 'production', 119 | __filename: process.env.NODE_ENV !== 'production' 120 | }, 121 | plugins: [ 122 | new VueLoaderPlugin(), 123 | new MiniCssExtractPlugin({filename: 'styles.css'}), 124 | new HtmlWebpackPlugin({ 125 | filename: 'index.html', 126 | template: path.resolve(__dirname, '../src/index.ejs'), 127 | minify: { 128 | collapseWhitespace: true, 129 | removeAttributeQuotes: true, 130 | removeComments: true 131 | }, 132 | nodeModules: process.env.NODE_ENV !== 'production' 133 | ? path.resolve(__dirname, '../node_modules') 134 | : false 135 | }), 136 | new webpack.HotModuleReplacementPlugin(), 137 | new webpack.NoEmitOnErrorsPlugin() 138 | ], 139 | output: { 140 | filename: '[name].js', 141 | libraryTarget: 'commonjs2', 142 | path: path.join(__dirname, '../dist/electron') 143 | }, 144 | resolve: { 145 | alias: { 146 | '@': path.join(__dirname, '../src/renderer'), 147 | '@scss': path.join(__dirname, '../src/assets/scss'), 148 | 'vue$': 'vue/dist/vue.esm.js', 149 | 150 | }, 151 | extensions: ['.js', '.vue', '.json', '.css', '.node'] 152 | }, 153 | target: 'electron-renderer' 154 | } 155 | 156 | /** 157 | * Adjust rendererConfig for development settings 158 | */ 159 | if (process.env.NODE_ENV !== 'production') { 160 | rendererConfig.plugins.push( 161 | new webpack.DefinePlugin({ 162 | '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"` 163 | }) 164 | ) 165 | } 166 | 167 | /** 168 | * Adjust rendererConfig for production settings 169 | */ 170 | if (process.env.NODE_ENV === 'production') { 171 | rendererConfig.devtool = '' 172 | 173 | rendererConfig.plugins.push( 174 | new BabiliWebpackPlugin(), 175 | new CopyWebpackPlugin([ 176 | { 177 | from: path.join(__dirname, '../static'), 178 | to: path.join(__dirname, '../dist/electron/static'), 179 | ignore: ['.*'] 180 | } 181 | ]), 182 | new webpack.DefinePlugin({ 183 | 'process.env.NODE_ENV': '"production"' 184 | }), 185 | new webpack.LoaderOptionsPlugin({ 186 | minimize: true 187 | }) 188 | ) 189 | } 190 | 191 | module.exports = rendererConfig 192 | -------------------------------------------------------------------------------- /.electron-vue/webpack.web.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | process.env.BABEL_ENV = 'web' 4 | 5 | const path = require('path') 6 | const webpack = require('webpack') 7 | 8 | const BabiliWebpackPlugin = require('babili-webpack-plugin') 9 | const CopyWebpackPlugin = require('copy-webpack-plugin') 10 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 11 | const HtmlWebpackPlugin = require('html-webpack-plugin') 12 | const {VueLoaderPlugin} = require('vue-loader') 13 | 14 | let webConfig = { 15 | devtool: '#cheap-module-eval-source-map', 16 | entry: { 17 | web: path.join(__dirname, '../src/renderer/main.js') 18 | }, 19 | module: { 20 | rules: [ 21 | { 22 | test: /\.scss$/, 23 | use: ['vue-style-loader', 'css-loader', 'sass-loader'] 24 | }, 25 | { 26 | test: /\.sass$/, 27 | use: ['vue-style-loader', 'css-loader', 'sass-loader?indentedSyntax'] 28 | }, 29 | { 30 | test: /\.less$/, 31 | use: ['vue-style-loader', 'css-loader', 'less-loader'] 32 | }, 33 | { 34 | test: /\.css$/, 35 | use: ['vue-style-loader', 'css-loader'] 36 | }, 37 | { 38 | test: /\.html$/, 39 | use: 'vue-html-loader' 40 | }, 41 | { 42 | test: /\.js$/, 43 | use: 'babel-loader', 44 | include: [path.resolve(__dirname, '../src/renderer')], 45 | exclude: /node_modules/ 46 | }, 47 | { 48 | test: /\.vue$/, 49 | use: { 50 | loader: 'vue-loader', 51 | options: { 52 | extractCSS: true, 53 | loaders: { 54 | sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', 55 | scss: 'vue-style-loader!css-loader!sass-loader', 56 | less: 'vue-style-loader!css-loader!less-loader' 57 | } 58 | } 59 | } 60 | }, 61 | { 62 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 63 | use: { 64 | loader: 'url-loader', 65 | query: { 66 | limit: 10000, 67 | name: 'imgs/[name].[ext]' 68 | } 69 | } 70 | }, 71 | { 72 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 73 | use: { 74 | loader: 'url-loader', 75 | query: { 76 | limit: 10000, 77 | name: 'fonts/[name].[ext]' 78 | } 79 | } 80 | } 81 | ] 82 | }, 83 | plugins: [ 84 | new VueLoaderPlugin(), 85 | new MiniCssExtractPlugin({filename: 'styles.css'}), 86 | new HtmlWebpackPlugin({ 87 | filename: 'index.html', 88 | template: path.resolve(__dirname, '../src/index.ejs'), 89 | minify: { 90 | collapseWhitespace: true, 91 | removeAttributeQuotes: true, 92 | removeComments: true 93 | }, 94 | nodeModules: false 95 | }), 96 | new webpack.DefinePlugin({ 97 | 'process.env.IS_WEB': 'true' 98 | }), 99 | new webpack.HotModuleReplacementPlugin(), 100 | new webpack.NoEmitOnErrorsPlugin() 101 | ], 102 | output: { 103 | filename: '[name].js', 104 | path: path.join(__dirname, '../dist/web') 105 | }, 106 | resolve: { 107 | alias: { 108 | '@': path.join(__dirname, '../src/renderer'), 109 | '@scss': path.join(__dirname, '../src/assets/scss'), 110 | 'vue$': 'vue/dist/vue.esm.js' 111 | }, 112 | extensions: ['.js', '.vue', '.json', '.css'] 113 | }, 114 | target: 'web' 115 | } 116 | 117 | /** 118 | * Adjust webConfig for production settings 119 | */ 120 | if (process.env.NODE_ENV === 'production') { 121 | webConfig.devtool = '' 122 | 123 | webConfig.plugins.push( 124 | new BabiliWebpackPlugin(), 125 | new CopyWebpackPlugin([ 126 | { 127 | from: path.join(__dirname, '../static'), 128 | to: path.join(__dirname, '../dist/web/static'), 129 | ignore: ['.*'] 130 | } 131 | ]), 132 | new webpack.DefinePlugin({ 133 | 'process.env.NODE_ENV': '"production"' 134 | }), 135 | new webpack.LoaderOptionsPlugin({ 136 | minimize: true 137 | }) 138 | ) 139 | } 140 | 141 | module.exports = webConfig 142 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | dist/electron/* 3 | dist/web/* 4 | build/* 5 | !build/icons 6 | node_modules/ 7 | npm-debug.log 8 | npm-debug.log.* 9 | thumbs.db 10 | !.gitkeep 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode8.3 2 | sudo: required 3 | dist: trusty 4 | language: c 5 | matrix: 6 | include: 7 | - os: osx 8 | - os: linux 9 | env: CC=clang CXX=clang++ npm_config_clang=1 10 | compiler: clang 11 | cache: 12 | directories: 13 | - node_modules 14 | - "$HOME/.electron" 15 | - "$HOME/.cache" 16 | addons: 17 | apt: 18 | packages: 19 | - libgnome-keyring-dev 20 | - icnsutils 21 | before_install: 22 | - mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v1.2.1/git-lfs-$([ 23 | "$TRAVIS_OS_NAME" == "linux" ] && echo "linux" || echo "darwin")-amd64-1.2.1.tar.gz 24 | | tar -xz -C /tmp/git-lfs --strip-components 1 && /tmp/git-lfs/git-lfs pull 25 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils; fi 26 | install: 27 | - nvm install 7 28 | - curl -o- -L https://yarnpkg.com/install.sh | bash 29 | - source ~/.bashrc 30 | - npm install -g xvfb-maybe 31 | - yarn 32 | script: 33 | - yarn run build 34 | branches: 35 | only: 36 | - master 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multi Session Browser 2 | 3 | This is a simple multi session browser 4 | Now you can be online in all your accounts and switch them with one click! 5 | You can use it to control all yours Facebook, Twitter, Instagram, Vk, etc accounts 6 | 7 | ![Demo](https://raw.githubusercontent.com/PavelShar/multi-session-browser/assets/demo.gif) 8 | 9 | 10 | #### Features: 11 | * Isolated: each tab has its own cookies, storage, etc 12 | * Memory: all tabs are saved and if you relaunch browser — it will restore its state 13 | * Portable: you can use application with your USB flash drive and take it with yourself or just run it on your computer without installation 14 | * Platform: [available](https://github.com/PavelShar/multi-session-browser/releases) for Windows, Linux and MacOS 15 | 16 | 17 | 18 | #### Technologies 19 | * electron 20 | * [electron-vue](https://github.com/SimulatedGREG/electron-vue) 21 | * vue 22 | * vuex 23 | * [vuex-persistedstate](https://github.com/robinvdvleuten/vuex-persistedstate) 24 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.1.{build} 2 | 3 | branches: 4 | only: 5 | - master 6 | 7 | image: Visual Studio 2017 8 | platform: 9 | - x64 10 | 11 | cache: 12 | - node_modules 13 | - '%APPDATA%\npm-cache' 14 | - '%USERPROFILE%\.electron' 15 | - '%USERPROFILE%\AppData\Local\Yarn\cache' 16 | 17 | init: 18 | - git config --global core.autocrlf input 19 | 20 | install: 21 | - ps: Install-Product node 8 x64 22 | - git reset --hard HEAD 23 | - yarn 24 | - node --version 25 | 26 | build_script: 27 | - yarn build 28 | 29 | test: off 30 | -------------------------------------------------------------------------------- /build/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavloniym/multi-session-browser/3aed5293b04bd7dd31624c45d34f9a18e6e28180/build/icons/256x256.png -------------------------------------------------------------------------------- /build/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavloniym/multi-session-browser/3aed5293b04bd7dd31624c45d34f9a18e6e28180/build/icons/icon.icns -------------------------------------------------------------------------------- /build/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavloniym/multi-session-browser/3aed5293b04bd7dd31624c45d34f9a18e6e28180/build/icons/icon.ico -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "multi-session-browser", 3 | "version": "1.0.0", 4 | "author": "Pavel Sharypov ", 5 | "description": "Multi Session Browser", 6 | "license": null, 7 | "private": true, 8 | "main": "./dist/electron/main.js", 9 | "scripts": { 10 | "build": "node .electron-vue/build.js && electron-builder -mwl", 11 | "build:dir": "node .electron-vue/build.js && electron-builder --dir", 12 | "build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js", 13 | "build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js", 14 | "dev": "node .electron-vue/dev-runner.js", 15 | "pack": "npm run pack:main && npm run pack:renderer", 16 | "pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js", 17 | "pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js", 18 | "postinstall": "" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/PavelShar/multi-session-browser" 23 | }, 24 | "build": { 25 | "productName": "Multi Session Browser", 26 | "appId": "com.pavelshar.multi-session-browser", 27 | "directories": { 28 | "output": "build" 29 | }, 30 | "files": [ 31 | "dist/electron/**/*" 32 | ], 33 | "dmg": { 34 | "contents": [ 35 | { 36 | "x": 410, 37 | "y": 150, 38 | "type": "link", 39 | "path": "/Applications" 40 | }, 41 | { 42 | "x": 130, 43 | "y": 150, 44 | "type": "file" 45 | } 46 | ] 47 | }, 48 | "mac": { 49 | "icon": "build/icons/icon.icns" 50 | }, 51 | "win": { 52 | "icon": "build/icons/icon.ico" 53 | }, 54 | "linux": { 55 | "icon": "build/icons" 56 | } 57 | }, 58 | "dependencies": { 59 | "electron-context-menu": "^0.10.0", 60 | "font-awesome": "^4.7.0", 61 | "lodash": "^4.17.10", 62 | "moment": "^2.22.2", 63 | "urijs": "^1.19.1", 64 | "vue": "^2.5.16", 65 | "vue-electron": "^1.0.6", 66 | "vue-nprogress": "^0.1.5", 67 | "vuex": "^3.0.1", 68 | "vuex-persistedstate": "^2.5.4" 69 | }, 70 | "devDependencies": { 71 | "ajv": "^6.5.0", 72 | "babel-core": "^6.26.3", 73 | "babel-loader": "^7.1.4", 74 | "babel-plugin-transform-runtime": "^6.23.0", 75 | "babel-preset-env": "^1.7.0", 76 | "babel-preset-stage-0": "^6.24.1", 77 | "babel-register": "^6.26.0", 78 | "babili-webpack-plugin": "^0.1.2", 79 | "cfonts": "^2.1.2", 80 | "chalk": "^2.4.1", 81 | "copy-webpack-plugin": "^4.5.1", 82 | "cross-env": "^5.1.6", 83 | "css-loader": "^0.28.11", 84 | "del": "^3.0.0", 85 | "devtron": "^1.4.0", 86 | "electron": "^2.0.4", 87 | "electron-builder": "^20.19.2", 88 | "electron-debug": "^1.5.0", 89 | "electron-devtools-installer": "^2.2.4", 90 | "file-loader": "^1.1.11", 91 | "html-webpack-plugin": "^3.2.0", 92 | "mini-css-extract-plugin": "0.4.0", 93 | "multispinner": "^0.2.1", 94 | "node-loader": "^0.6.0", 95 | "node-sass": "^4.9.2", 96 | "sass-loader": "^7.0.3", 97 | "style-loader": "^0.21.0", 98 | "url-loader": "^1.0.1", 99 | "vue-html-loader": "^1.2.4", 100 | "vue-loader": "^15.2.4", 101 | "vue-style-loader": "^4.1.0", 102 | "vue-template-compiler": "^2.5.16", 103 | "webfonts-loader": "^4.1.0", 104 | "webpack": "^4.15.1", 105 | "webpack-cli": "^3.0.8", 106 | "webpack-dev-server": "^3.1.4", 107 | "webpack-hot-middleware": "^2.22.2", 108 | "webpack-merge": "^4.1.3" 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/assets/fonts/browser/browser.css.hbs: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "{{fontName}}"; 3 | src: {{{src}}}; 4 | } 5 | 6 | {{baseSelector}} { 7 | line-height: normal; 8 | display: flex; 9 | align-items:center; 10 | } 11 | 12 | {{baseSelector}}:before { 13 | font-family: {{fontName}} !important; 14 | font-style: normal; 15 | font-weight: normal !important; 16 | } 17 | 18 | {{#each codepoints}} 19 | .{{../classPrefix}}{{@key}}:before { 20 | content: "\\{{this}}"; 21 | } 22 | {{/each}} 23 | -------------------------------------------------------------------------------- /src/assets/fonts/browser/browser.font.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const fontName = 'BrowserIcons'; 4 | const fontTemplate = './browser.css.hbs'; 5 | const fontSVGFolder = path.join(__dirname, '.', 'sources'); 6 | 7 | module.exports = { 8 | 9 | files: [`${fontSVGFolder}/**/*.svg`], 10 | fontName: fontName.toLowerCase(), 11 | classPrefix: 'b-', 12 | baseSelector: '.b', 13 | types: ['eot', 'woff', 'woff2', 'ttf', 'svg'], 14 | fixedWidth: true, 15 | embed: false, 16 | cssTemplate: fontTemplate, 17 | formatOptions: { 18 | svg: { 19 | centerHorizontally: true, 20 | normalize: true, 21 | metadata: 'Pavel Sharypov' 22 | } 23 | }, 24 | 25 | /* 26 | * Rename icons 27 | * Use folder in names like [folder]-[folder]-[filename] 28 | */ 29 | rename: function(name){ 30 | 31 | let basepath = name; 32 | let folder = basepath.slice(basepath.indexOf(fontSVGFolder) + fontSVGFolder.length + 1); 33 | return folder.split('/').join('-').replace(/\.[^/.]+$/, ""); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /src/assets/fonts/browser/sources/new-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/assets/fonts/browser/sources/tabs.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/fonts/roboto/Bold/RobotoBoldWebfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavloniym/multi-session-browser/3aed5293b04bd7dd31624c45d34f9a18e6e28180/src/assets/fonts/roboto/Bold/RobotoBoldWebfont.ttf -------------------------------------------------------------------------------- /src/assets/fonts/roboto/Bold/RobotoBoldWebfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavloniym/multi-session-browser/3aed5293b04bd7dd31624c45d34f9a18e6e28180/src/assets/fonts/roboto/Bold/RobotoBoldWebfont.woff -------------------------------------------------------------------------------- /src/assets/fonts/roboto/Light/RobotoLightWebfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavloniym/multi-session-browser/3aed5293b04bd7dd31624c45d34f9a18e6e28180/src/assets/fonts/roboto/Light/RobotoLightWebfont.ttf -------------------------------------------------------------------------------- /src/assets/fonts/roboto/Light/RobotoLightWebfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavloniym/multi-session-browser/3aed5293b04bd7dd31624c45d34f9a18e6e28180/src/assets/fonts/roboto/Light/RobotoLightWebfont.woff -------------------------------------------------------------------------------- /src/assets/fonts/roboto/Medium/RobotoMediumWebfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavloniym/multi-session-browser/3aed5293b04bd7dd31624c45d34f9a18e6e28180/src/assets/fonts/roboto/Medium/RobotoMediumWebfont.ttf -------------------------------------------------------------------------------- /src/assets/fonts/roboto/Medium/RobotoMediumWebfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavloniym/multi-session-browser/3aed5293b04bd7dd31624c45d34f9a18e6e28180/src/assets/fonts/roboto/Medium/RobotoMediumWebfont.woff -------------------------------------------------------------------------------- /src/assets/fonts/roboto/Regular/RobotoRegularWebfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavloniym/multi-session-browser/3aed5293b04bd7dd31624c45d34f9a18e6e28180/src/assets/fonts/roboto/Regular/RobotoRegularWebfont.ttf -------------------------------------------------------------------------------- /src/assets/fonts/roboto/Regular/RobotoRegularWebfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavloniym/multi-session-browser/3aed5293b04bd7dd31624c45d34f9a18e6e28180/src/assets/fonts/roboto/Regular/RobotoRegularWebfont.woff -------------------------------------------------------------------------------- /src/assets/fonts/roboto/Thin/RobotoThinWebfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavloniym/multi-session-browser/3aed5293b04bd7dd31624c45d34f9a18e6e28180/src/assets/fonts/roboto/Thin/RobotoThinWebfont.ttf -------------------------------------------------------------------------------- /src/assets/fonts/roboto/Thin/RobotoThinWebfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavloniym/multi-session-browser/3aed5293b04bd7dd31624c45d34f9a18e6e28180/src/assets/fonts/roboto/Thin/RobotoThinWebfont.woff -------------------------------------------------------------------------------- /src/assets/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * ============= 4 | * Fonts 5 | * ============= 6 | */ 7 | 8 | $font: -apple-system, BlinkMacSystemFont, "Roboto", "Helvetica Neue", "Helvetica", "Arial", sans-serif; 9 | -------------------------------------------------------------------------------- /src/assets/scss/fonts/families/_roboto.scss: -------------------------------------------------------------------------------- 1 | $folder: './../../fonts/Roboto'; 2 | $thin: $folder + '/Thin/RobotoThinWebfont'; 3 | $light: $folder + '/Light/RobotoLightWebfont'; 4 | $regular: $folder + '/Regular/RobotoRegularWebfont'; 5 | $medium: $folder + '/Medium/RobotoMediumWebfont'; 6 | $bold: $folder + '/Bold/RobotoBoldWebfont'; 7 | 8 | @font-face { 9 | font-family: "Roboto"; 10 | font-weight: 200; 11 | src: url($thin + '.woff') format('woff'), url($thin + '.ttf') format('ttf'); 12 | } 13 | 14 | @font-face { 15 | font-family: "Roboto"; 16 | font-weight: 300; 17 | src: url($light + '.woff') format('woff'), url($light + '.ttf') format('ttf'); 18 | 19 | } 20 | 21 | @font-face { 22 | font-family: "Roboto"; 23 | font-weight: 400; 24 | src: url($regular + '.woff') format('woff'), url($regular + '.ttf') format('ttf'); 25 | } 26 | 27 | @font-face { 28 | font-family: "Roboto"; 29 | font-weight: 500; 30 | src: url($medium + '.woff') format('woff'), url($medium + '.ttf') format('ttf'); 31 | } 32 | 33 | @font-face { 34 | font-family: "Roboto"; 35 | font-weight: 700; 36 | src: url($bold + '.woff') format('woff'), url($bold + '.ttf') format('ttf'); 37 | } 38 | -------------------------------------------------------------------------------- /src/assets/scss/fonts/index.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * Fonts 3 | * ======== 4 | * 5 | * Include fonts 6 | */ 7 | 8 | @import "families/roboto"; 9 | -------------------------------------------------------------------------------- /src/assets/scss/scrollbars/_tabs.scss: -------------------------------------------------------------------------------- 1 | ::-webkit-scrollbar-thumb { 2 | background-color: rgb(123, 123, 123); 3 | } 4 | 5 | ::-webkit-scrollbar { 6 | width: 5px; 7 | background-color: #4a4949; 8 | } 9 | -------------------------------------------------------------------------------- /src/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Multi Session Browser 6 | <% if (htmlWebpackPlugin.options.nodeModules) { %> 7 | 8 | 11 | <% } %> 12 | 13 | 14 |
15 | 16 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/index.dev.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is used specifically and only for development. It installs 3 | * `electron-debug` & `vue-devtools`. There shouldn't be any need to 4 | * modify this file, but it can be used to extend your development 5 | * environment. 6 | */ 7 | 8 | /* eslint-disable */ 9 | 10 | // Install `electron-debug` with `devtron` 11 | require('electron-debug')({ showDevTools: true }) 12 | 13 | // Install `vue-devtools` 14 | require('electron').app.on('ready', () => { 15 | let installExtension = require('electron-devtools-installer') 16 | installExtension.default(installExtension.VUEJS_DEVTOOLS) 17 | .then(() => {}) 18 | .catch(err => { 19 | console.log('Unable to install `vue-devtools`: \n', err) 20 | }) 21 | }) 22 | 23 | // Require `main` process to boot app 24 | require('./index') -------------------------------------------------------------------------------- /src/main/index.js: -------------------------------------------------------------------------------- 1 | import {app, BrowserWindow} from 'electron' 2 | 3 | const fs = require('fs'); 4 | const path = require ('path'); 5 | 6 | 7 | 8 | 9 | /** 10 | * Set `__static` path to static files in production 11 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html 12 | */ 13 | if (process.env.NODE_ENV !== 'development') { 14 | global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\') 15 | } 16 | 17 | let mainWindow 18 | const winURL = process.env.NODE_ENV === 'development' 19 | ? `http://localhost:9080` 20 | : `file://${__dirname}/index.html` 21 | 22 | function createWindow() { 23 | /** 24 | * Initial window options 25 | */ 26 | mainWindow = new BrowserWindow({ 27 | height: 563, 28 | width: 1300, 29 | frame: false 30 | }); 31 | 32 | mainWindow.loadURL(winURL) 33 | 34 | mainWindow.on('closed', () => { 35 | mainWindow = null 36 | }); 37 | 38 | 39 | 40 | 41 | } 42 | 43 | 44 | function setUserDataFolder() { 45 | 46 | const dir = path.dirname(app.getPath ('exe')); 47 | 48 | //try { fs.writeFileSync(path.join(process.cwd(), "path.txt"), dir, 'utf-8'); } 49 | //catch(e) { alert('Failed to save the file !'); } 50 | 51 | app.setPath('userData', `${dir}/msb_user_data/`); 52 | } 53 | 54 | 55 | setUserDataFolder(); 56 | 57 | app.on('ready', () => { 58 | 59 | createWindow() 60 | }) 61 | 62 | app.on('window-all-closed', () => app.quit()) 63 | 64 | app.on('activate', () => { 65 | if (mainWindow === null) { 66 | setUserDataFolder() 67 | createWindow() 68 | } 69 | }) 70 | 71 | /** 72 | * Auto Updater 73 | * 74 | * Uncomment the following code below and install `electron-updater` to 75 | * support auto updating. Code Signing with a valid certificate is required. 76 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-electron-builder.html#auto-updating 77 | */ 78 | 79 | /* 80 | import { autoUpdater } from 'electron-updater' 81 | 82 | autoUpdater.on('update-downloaded', () => { 83 | autoUpdater.quitAndInstall() 84 | }) 85 | 86 | app.on('ready', () => { 87 | if (process.env.NODE_ENV === 'production') autoUpdater.checkForUpdates() 88 | }) 89 | */ 90 | -------------------------------------------------------------------------------- /src/renderer/App.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 155 | 156 | 202 | -------------------------------------------------------------------------------- /src/renderer/components/side/Bar.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 23 | -------------------------------------------------------------------------------- /src/renderer/components/side/Controls.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 63 | 64 | 100 | -------------------------------------------------------------------------------- /src/renderer/components/side/Credentials.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 64 | 65 | 94 | -------------------------------------------------------------------------------- /src/renderer/components/side/Head.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 28 | -------------------------------------------------------------------------------- /src/renderer/components/side/index.js: -------------------------------------------------------------------------------- 1 | import Bar from './Bar' 2 | import Head from './Head' 3 | 4 | import Tabs from './tabs/Tabs' 5 | import Tab from './tabs/Tab' 6 | 7 | import Credentials from './Credentials' 8 | import Controls from './Controls' 9 | 10 | export { 11 | Bar, 12 | Head, 13 | Tabs, 14 | Tab, 15 | Credentials, 16 | Controls 17 | } 18 | -------------------------------------------------------------------------------- /src/renderer/components/side/tabs/Tab.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 34 | 35 | 100 | -------------------------------------------------------------------------------- /src/renderer/components/side/tabs/Tabs.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 28 | -------------------------------------------------------------------------------- /src/renderer/components/view/controls/action.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 28 | 29 | 60 | -------------------------------------------------------------------------------- /src/renderer/components/view/controls/bar.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 30 | -------------------------------------------------------------------------------- /src/renderer/components/view/controls/url.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 41 | 42 | 55 | -------------------------------------------------------------------------------- /src/renderer/components/view/pages/Content.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 234 | 235 | 260 | -------------------------------------------------------------------------------- /src/renderer/components/view/pages/Empty.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 40 | -------------------------------------------------------------------------------- /src/renderer/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | import App from './App' 4 | import store from './store' 5 | 6 | // Load fonts 7 | import 'font-awesome/css/font-awesome.min.css' 8 | import './../assets/fonts/browser/browser.font' 9 | import './../assets/scss/fonts/index.scss' 10 | 11 | 12 | if (!process.env.IS_WEB) Vue.use(require('vue-electron')) 13 | Vue.config.productionTip = false 14 | 15 | 16 | /* eslint-disable no-new */ 17 | new Vue({ 18 | 19 | components: {App}, 20 | store, 21 | template: '' 22 | 23 | }).$mount('#app') 24 | -------------------------------------------------------------------------------- /src/renderer/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import tabs from './modules/tabs' 4 | 5 | import createPersistedState from 'vuex-persistedstate' 6 | 7 | Vue.use(Vuex); 8 | 9 | export default new Vuex.Store({ 10 | modules: { 11 | tabs 12 | }, 13 | strict: process.env.NODE_ENV !== 'production', 14 | plugins: [createPersistedState({ 15 | key: 'multisession-browser-storage', 16 | })] 17 | }) 18 | -------------------------------------------------------------------------------- /src/renderer/store/modules/tabs.js: -------------------------------------------------------------------------------- 1 | import set from 'lodash/set' 2 | import moment from 'moment' 3 | 4 | 5 | export default { 6 | 7 | namespaced: true, 8 | state: { 9 | tab: null, 10 | tabs: [] 11 | }, 12 | 13 | 14 | mutations: { 15 | 16 | 17 | /** 18 | * Update tabs data 19 | * 20 | * @param s 21 | * @param k 22 | * @param v 23 | * @returns {Object} 24 | */ 25 | update: (s, {k, v}) => set(s, k, v), 26 | 27 | 28 | /** 29 | * Add new tab to stack 30 | * 31 | * @param s 32 | * @param session 33 | * @returns {number} 34 | */ 35 | add: (s, {session}) => s.tabs.push({ 36 | favicon: null, 37 | hash: moment().format('x'), 38 | url: 'https://google.com', 39 | session 40 | }), 41 | 42 | 43 | remove: (s, index) => s.tabs.splice(index, 1), 44 | }, 45 | 46 | 47 | getters: { 48 | tab: (s) => s.tab, 49 | tabs: (s) => s.tabs, 50 | } 51 | 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/renderer/utils/hashing.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Hashing object to b64 string 4 | * 5 | * @param object 6 | * @returns {string} 7 | */ 8 | const hashing = (object) => btoa(JSON.stringify(object)); 9 | 10 | 11 | /** 12 | * Parse hashed string to object 13 | * 14 | * @param string 15 | * @returns {any} 16 | */ 17 | const parsing = (string) => JSON.parse(atob(string)); 18 | 19 | 20 | 21 | export { 22 | hashing, 23 | parsing 24 | } 25 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pavloniym/multi-session-browser/3aed5293b04bd7dd31624c45d34f9a18e6e28180/static/.gitkeep --------------------------------------------------------------------------------