├── .github └── preview.png ├── .gitignore ├── README.md ├── assets ├── icon.icns ├── icon.ico ├── icon.png └── icon.svg ├── package.json ├── src ├── main │ ├── app.js │ └── utils │ │ ├── assets-path.js │ │ └── check-url.js └── renderer │ ├── modules │ └── zen-mode │ │ ├── index.js │ │ └── style.css │ ├── preload.js │ ├── styles │ └── style.css │ └── utils │ └── inject-css.js ├── webpack ├── main.webpack.js ├── renderer.webpack.js └── rules.webpack.js └── yarn.lock /.github/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maykbrito/electron-codepen-omni/9ee105230fe3e7187c0ab795da60053c8a62ba9c/.github/preview.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | packages/ 2 | out/ 3 | node_modules/ 4 | .DS_Store 5 | .webpack -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Codepen.io Omni 2 | 3 | With ElectronJS, let's inject some CSS to customize Codepen ;) 4 | 5 |

6 | How to build     7 | Contact 8 |      9 |

10 | 11 | ![Preview](.github/preview.png) 12 | 13 | The preview above are in `zen mode` (only works with JS tab + console). 14 | 15 | To toggle it, just press: `Alt+Shift+control+r` 16 | 17 | --- 18 | 19 | ## How to build 20 | 21 | 1. Install 22 | 23 | ```sh 24 | yarn 25 | ``` 26 | 27 | 2. Build 28 | 29 | ```sh 30 | yarn package 31 | ``` 32 | 33 | 3. Run your new app. 34 | _It will be at dir ./out_ 35 | 36 | # Contact 37 | 38 | Mayk Brito 39 | 40 | - [maykbrito.dev](https://maykbrito.dev) 41 | 42 | --- 43 | 44 | If this project helps you, please leave your star 🌟 Thank you 💛 45 | -------------------------------------------------------------------------------- /assets/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maykbrito/electron-codepen-omni/9ee105230fe3e7187c0ab795da60053c8a62ba9c/assets/icon.icns -------------------------------------------------------------------------------- /assets/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maykbrito/electron-codepen-omni/9ee105230fe3e7187c0ab795da60053c8a62ba9c/assets/icon.ico -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maykbrito/electron-codepen-omni/9ee105230fe3e7187c0ab795da60053c8a62ba9c/assets/icon.png -------------------------------------------------------------------------------- /assets/icon.svg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maykbrito/electron-codepen-omni/9ee105230fe3e7187c0ab795da60053c8a62ba9c/assets/icon.svg -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "codepen-omni", 3 | "version": "1.0.0", 4 | "description": "Run codepen.io custom theme with this App", 5 | "main": "./.webpack/main/index.js", 6 | "scripts": { 7 | "start": "electron-forge start", 8 | "package": "electron-forge package", 9 | "make": "electron-forge make" 10 | }, 11 | "devDependencies": { 12 | "@electron-forge/cli": "^6.0.0-beta.61", 13 | "@electron-forge/maker-deb": "^6.0.0-beta.61", 14 | "@electron-forge/maker-rpm": "^6.0.0-beta.61", 15 | "@electron-forge/maker-squirrel": "^6.0.0-beta.61", 16 | "@electron-forge/maker-zip": "^6.0.0-beta.61", 17 | "@electron-forge/plugin-webpack": "^6.0.0-beta.61", 18 | "@marshallofsound/webpack-asset-relocator-loader": "^0.5.0", 19 | "electron": "^15.3.0", 20 | "node-loader": "^2.0.0", 21 | "webpack": "^5.61.0", 22 | "webpack-cli": "^4.9.1" 23 | }, 24 | "keywords": [ 25 | "Editor", 26 | "Front-end", 27 | "HTML", 28 | "CSS", 29 | "JavaScript" 30 | ], 31 | "author": "Mayk Brito ", 32 | "license": "MIT", 33 | "dependencies": { 34 | "electron-squirrel-startup": "^1.0.0" 35 | }, 36 | "config": { 37 | "forge": { 38 | "packagerConfig": { 39 | "name": "codepen-omni", 40 | "executableName": "codepen-omni", 41 | "icon": "assets/icon", 42 | "extraResource": [ 43 | "assets", 44 | "src" 45 | ] 46 | }, 47 | "plugins": [ 48 | [ 49 | "@electron-forge/plugin-webpack", 50 | { 51 | "mainConfig": "./webpack/main.webpack.js", 52 | "renderer": { 53 | "config": "./webpack/renderer.webpack.js", 54 | "entryPoints": [ 55 | { 56 | "js": "./src/renderer/preload.js", 57 | "name": "main_window", 58 | "preload": { 59 | "js": "./src/renderer/preload.js" 60 | } 61 | } 62 | ] 63 | } 64 | } 65 | ] 66 | ], 67 | "makers": [ 68 | { 69 | "name": "@electron-forge/maker-squirrel", 70 | "config": { 71 | "name": "codepen-omni", 72 | "setupExe": "${name}-${version}-setup.exe", 73 | "setupIcon": "./assets/icon.ico", 74 | "iconUrl": "https://raw.githubusercontent.com/maykbrito/codepen-omni/assets/icon.ico" 75 | } 76 | }, 77 | { 78 | "name": "@electron-forge/maker-zip", 79 | "platforms": [ 80 | "darwin" 81 | ] 82 | }, 83 | { 84 | "name": "@electron-forge/maker-deb", 85 | "config": {} 86 | }, 87 | { 88 | "name": "@electron-forge/maker-rpm", 89 | "config": {} 90 | } 91 | ], 92 | "publishers": [ 93 | { 94 | "name": "@electron-forge/publisher-github", 95 | "config": { 96 | "repository": { 97 | "owner": "maykbrito", 98 | "name": "codepen-omni" 99 | }, 100 | "draft": true 101 | } 102 | } 103 | ] 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/app.js: -------------------------------------------------------------------------------- 1 | import { 2 | app, 3 | BrowserWindow, 4 | screen, 5 | nativeImage, 6 | globalShortcut 7 | } from 'electron' 8 | import path from 'path' 9 | 10 | import { assetsPath } from './utils/assets-path' 11 | import { checkerURL } from './utils/check-url' 12 | 13 | let win = null 14 | app.allowRendererProcessReuse = true 15 | 16 | async function createWindow() { 17 | win = new BrowserWindow({ 18 | icon: nativeImage.createFromPath( 19 | path.join(assetsPath, 'assets', 'icon.png') 20 | ), 21 | frame: false, 22 | // titleBarStyle: 'customButtonsOnHover', 23 | webPreferences: { 24 | nodeIntegration: true, 25 | contextIsolation: true, 26 | preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY 27 | } 28 | }) 29 | 30 | adjustWindow(win) 31 | 32 | win.loadURL('https://codepen.io/pen/?editors=0012') 33 | 34 | win.webContents.on('new-window', checkerURL) // add event listener for URL check 35 | } 36 | 37 | function adjustWindow(win) { 38 | const { size } = screen.getPrimaryDisplay() 39 | const { width, height } = size 40 | 41 | win.setPosition(0, 0) 42 | win.setSize(width, height) 43 | } 44 | 45 | function createShortcuts() { 46 | const toggleHide = 'Alt+Shift+control+r' 47 | globalShortcut.register(toggleHide, () => { 48 | win.webContents.send('toggleHide') 49 | }) 50 | } 51 | 52 | //Verifica se o app já foi iniciado 53 | const isUnicInstance = app.requestSingleInstanceLock() 54 | 55 | if (!isUnicInstance) { 56 | app.quit() 57 | } else { 58 | // This method will be called when Electron has finished 59 | // initialization and is ready to create browser windows. 60 | // Some APIs can only be used after this event occurs. 61 | app 62 | .whenReady() 63 | .then(createWindow) 64 | .then(() => createShortcuts()) 65 | .catch(e => console.error(e)) 66 | } 67 | 68 | // Faz com que o programa não inicie várias vezes durante a instalação no windows 69 | if (require('electron-squirrel-startup')) { 70 | app.quit() 71 | } 72 | 73 | app.on('second-instance', () => { 74 | const win = BrowserWindow.getAllWindows()[0] 75 | if (win.isMinimized()) { 76 | win.restore() 77 | } 78 | win.focus() 79 | }) 80 | 81 | // Quit when all windows are closed. 82 | app.on('window-all-closed', () => { 83 | // On macOS it is common for applications and their menu bar 84 | // to stay active until the user quits explicitly with Cmd + Q 85 | if (process.platform !== 'darwin') { 86 | app.quit() 87 | } 88 | }) 89 | 90 | app.on('activate', recreateWindow) 91 | 92 | function recreateWindow() { 93 | // On macOS it's common to re-create a window in the app when the 94 | // dock icon is clicked and there are no other windows open. 95 | if (BrowserWindow.getAllWindows().length === 0) { 96 | setTimeout(createWindow, 200) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/utils/assets-path.js: -------------------------------------------------------------------------------- 1 | import { app, ipcMain } from 'electron' 2 | 3 | const assetsPath = 4 | process.env.NODE_ENV === 'production' 5 | ? process.resourcesPath 6 | : app.getAppPath() 7 | 8 | ipcMain.on('request-app-path', (event, arg) => { 9 | event.returnValue = assetsPath 10 | }) 11 | 12 | export { assetsPath } 13 | -------------------------------------------------------------------------------- /src/main/utils/check-url.js: -------------------------------------------------------------------------------- 1 | import { shell } from 'electron' 2 | /** 3 | * This function is used electron's new-window event 4 | * It allows non-electron links to be opened with the computer's default browser 5 | * Keep opening pop-ups for google login for example 6 | * @param {NewWindowEvent} e 7 | * @param {String} url 8 | */ 9 | function checkerURL(e, url) { 10 | const isNotUrlOfTheNotion = !url.match('/ecstatic-shaw-7ceb62.netlify.app/') 11 | 12 | if (isNotUrlOfTheNotion) { 13 | e.preventDefault() 14 | shell.openExternal(url) 15 | } 16 | } 17 | 18 | export { checkerURL } 19 | -------------------------------------------------------------------------------- /src/renderer/modules/zen-mode/index.js: -------------------------------------------------------------------------------- 1 | const { ipcRenderer } = require('electron') 2 | 3 | console.log('zen-mode Loaded') 4 | 5 | const injectCSS = require('../../utils/inject-css') 6 | 7 | /* Hide Elements */ 8 | ipcRenderer.on('toggleHide', () => toggleHideElements()) 9 | 10 | let hide = false 11 | function toggleHideElements() { 12 | hide 13 | ? document.body.classList.add('zen') 14 | : document.body.classList.remove('zen') 15 | 16 | hide = !hide 17 | } 18 | 19 | toggleHideElements() 20 | 21 | module.exports = () => { 22 | injectCSS('src', 'renderer', 'modules', 'zen-mode', 'style.css') 23 | } 24 | -------------------------------------------------------------------------------- /src/renderer/modules/zen-mode/style.css: -------------------------------------------------------------------------------- 1 | /* zen mode ONLY ALLOW JS */ 2 | body.zen { 3 | padding: 0 !important; 4 | } 5 | 6 | body.zen .unsaved-editor-message, 7 | body.zen #main-header, 8 | body.zen #box-css, 9 | body.zen #box-html, 10 | body.zen #react-pen-footer { 11 | display: none !important; 12 | } 13 | 14 | body.zen #box-js { 15 | height: 100% !important; 16 | } 17 | -------------------------------------------------------------------------------- /src/renderer/preload.js: -------------------------------------------------------------------------------- 1 | const injectCSS = require('./utils/inject-css') 2 | 3 | window.addEventListener('DOMContentLoaded', () => { 4 | injectCSS('src', 'renderer', 'styles', 'style.css') 5 | 6 | /** zen mode */ 7 | require('./modules/zen-mode/index')() 8 | }) 9 | -------------------------------------------------------------------------------- /src/renderer/styles/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /* Dark */ 3 | --dark-hue: 253; 4 | --dark: hsl(var(--dark-hue), 24%, 10%); 5 | --dark-1: hsl(var(--dark-hue), 24%, 14%); 6 | --dark-2: hsl(var(--dark-hue), 24%, 18%); 7 | --dark-3: hsl(var(--dark-hue), 24%, 22%); 8 | --dark-4: hsl(var(--dark-hue), 24%, 26%); 9 | --dark-5: hsl(var(--dark-hue), 24%, 30%); 10 | /* Primary */ 11 | --primary-hue: 132; 12 | --primary: hsl(var(--primary-hue), 70%, 65%); 13 | --primary-1: hsl(var(--primary-hue), 70%, 55%); 14 | --primary-2: hsl(var(--primary-hue), 70%, 45%); 15 | --primary-3: hsl(var(--primary-hue), 70%, 35%); 16 | --primary-4: hsl(var(--primary-hue), 70%, 25%); 17 | --primary-5: hsl(var(--primary-hue), 70%, 15%); 18 | 19 | /* colors */ 20 | --pink: hsl(326, 100%, 74%); 21 | --green: hsl(132, 70%, 65%); 22 | --purple: hsl(253, 35%, 66%); 23 | --purple-dark: hsl(257, 26%, 40%); 24 | --yellow: hsl(55, 70%, 69%); 25 | --blue: hsl(189, 64%, 68%); 26 | --white: hsl(240, 9%, 89%); 27 | --orange: hsl(26, 74%, 65%); 28 | 29 | /* codepen */ 30 | --tab-bg: var(--dark); 31 | --tab-border-color: var(--dark); 32 | --editor-top-bar-background: var(--dark); 33 | --resizer-border: var(--dark-3); 34 | 35 | --cp-color-12: var(--dark-1); 36 | --cp-color-13: var(--dark-2); 37 | --cp-color-14: var(--dark-3); 38 | --cp-color-15: var(--dark-4); 39 | } 40 | 41 | /* */ 42 | .box.box.box, 43 | .editor .top-boxes, 44 | .CodeMirror-gutter-wrapper, 45 | body.project .editor-pane, 46 | body.project .editor { 47 | background: var(--dark); 48 | } 49 | 50 | .CodeMirror-simplescroll-horizontal div, 51 | .CodeMirror-simplescroll-vertical div { 52 | background: var(--green); 53 | } 54 | 55 | .CodeMirror, 56 | .cm-s-default .CodeMirror-gutters { 57 | background-color: var(--dark) !important; 58 | color: #f8f8f2 !important; 59 | border: none; 60 | } 61 | 62 | .console-wrap { 63 | background-color: var(--dark) !important; 64 | } 65 | 66 | .console-command-line { 67 | background: var(--dark-1); 68 | } 69 | 70 | .CodeMirror-gutters { 71 | color: var(--dark) !important; 72 | } 73 | .CodeMirror-cursor { 74 | border-left: solid thin #f8f8f0; 75 | } 76 | .CodeMirror-linenumber { 77 | color: var(--purple-dark); 78 | } 79 | .CodeMirror-selected { 80 | background: rgba(255, 255, 255, 0.1); 81 | } 82 | .CodeMirror-line::selection, 83 | .CodeMirror-line > span::selection, 84 | .CodeMirror-line > span > span::selection { 85 | background: rgba(255, 255, 255, 0.1); 86 | } 87 | .CodeMirror-line::-moz-selection, 88 | .CodeMirror-line > span::-moz-selection, 89 | .CodeMirror-line > span > span::-moz-selection { 90 | background: rgba(255, 255, 255, 0.1); 91 | } 92 | 93 | span.cm-comment { 94 | color: var(--purple-dark); 95 | } 96 | span.cm-string, 97 | span.cm-string-2 { 98 | color: var(--yellow); 99 | } 100 | span.cm-number { 101 | color: var(--blue); 102 | } 103 | span.cm-variable { 104 | color: var(--white); 105 | } 106 | span.cm-variable-2 { 107 | color: var(--white); 108 | } 109 | span.cm-def { 110 | color: var(--white); 111 | } 112 | span.cm-operator { 113 | color: var(--pink); 114 | } 115 | span.cm-keyword { 116 | color: var(--pink); 117 | } 118 | span.cm-atom { 119 | color: #bd93f9; 120 | } 121 | span.cm-meta { 122 | color: #f8f8f2; 123 | } 124 | span.cm-tag { 125 | color: var(--pink); 126 | } 127 | span.cm-attribute { 128 | color: var(--green); 129 | } 130 | span.cm-qualifier { 131 | color: var(--green); 132 | } 133 | span.cm-property { 134 | color: var(--green); 135 | } 136 | span.cm-builtin { 137 | color: var(--green); 138 | } 139 | span.cm-variable-3, 140 | span.cm-type { 141 | color: var(--orange); 142 | } 143 | 144 | .CodeMirror-activeline-background { 145 | background: rgba(255, 255, 255, 0.1); 146 | } 147 | .CodeMirror-matchingbracket { 148 | text-decoration: underline; 149 | color: white !important; 150 | } 151 | -------------------------------------------------------------------------------- /src/renderer/utils/inject-css.js: -------------------------------------------------------------------------------- 1 | const { ipcRenderer } = require('electron') 2 | const path = require('path') 3 | const appPath = ipcRenderer.sendSync('request-app-path') 4 | const fs = require('fs') 5 | 6 | function injectCSS(...cssPathSegments) { 7 | const cssPath = path.resolve(appPath, ...cssPathSegments) 8 | 9 | console.log(cssPath) 10 | const cssContent = fs.readFileSync(cssPath) 11 | const styleEl = document.createElement('style') 12 | styleEl.innerHTML = cssContent 13 | document.head.append(styleEl) 14 | } 15 | 16 | module.exports = injectCSS 17 | -------------------------------------------------------------------------------- /webpack/main.webpack.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | resolve: { 3 | extensions: ['.js'] 4 | }, 5 | entry: './src/main/app.js', 6 | module: { 7 | rules: require('./rules.webpack') 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /webpack/renderer.webpack.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | resolve: { 3 | extensions: ['.js', '.css'], 4 | fallback: { 5 | path: false, 6 | fs: false, 7 | util: false, 8 | assert: false, 9 | stream: false, 10 | constants: false 11 | } 12 | }, 13 | target: 'electron-renderer', 14 | module: { 15 | rules: require('./rules.webpack') 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /webpack/rules.webpack.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | test: /\.node$/, 4 | use: 'node-loader' 5 | }, 6 | { 7 | test: /\.(m?js|node)$/, 8 | parser: { amd: false }, 9 | use: { 10 | loader: '@marshallofsound/webpack-asset-relocator-loader', 11 | options: { 12 | outputAssetBase: 'native_modules' 13 | } 14 | } 15 | } 16 | ] 17 | --------------------------------------------------------------------------------