├── .gitignore ├── README.md ├── forge.config.js ├── images ├── OpenAI_Logo.svg ├── icon.icns ├── icon.png ├── icon@2x.icns ├── icon@2x.png ├── newiconTemplate.png ├── newiconTemplate@2x.png └── screenshot.jpeg ├── index.css ├── index.html ├── index.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | .DS_Store 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # TypeScript cache 43 | *.tsbuildinfo 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | .env.test 63 | 64 | # parcel-bundler cache (https://parceljs.org/) 65 | .cache 66 | 67 | # next.js build output 68 | .next 69 | 70 | # nuxt.js build output 71 | .nuxt 72 | 73 | # vuepress build output 74 | .vuepress/dist 75 | 76 | # Serverless directories 77 | .serverless/ 78 | 79 | # FuseBox cache 80 | .fusebox/ 81 | 82 | # DynamoDB Local files 83 | .dynamodb/ 84 | 85 | # Webpack 86 | .webpack/ 87 | 88 | # Electron-Forge 89 | out/ 90 | 91 | .env -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ⭐ Building in AI? My new project is an [open-source toolkit for AI devs](https://github.com/llmonitor/llmonitor), a star would mean the world ⭐ 2 | 3 | --- 4 | 5 | # ChatGPT for desktop 6 | 7 | This is a simple app that makes ChatGPT live in your menubar. 8 | 9 | You can use Cmd+Shift+G (Mac) or Ctrl+Shift+G (Win) to quickly open it from anywhere. 10 | 11 | Download: 12 | 13 | - [Mac Arm64 .dmg](https://github.com/vincelwt/chatgpt-mac/releases/download/v0.0.5/ChatGPT-0.0.5-arm64.dmg) 14 | - [Mac Intel .dmg](https://github.com/vincelwt/chatgpt-mac/releases/download/v0.0.5/ChatGPT-0.0.5-x64.dmg) 15 | 16 | No Windows binaries currently offered. Clone the repo, npm install electron-forge and run. 17 | 18 |

19 | 20 |

21 | 22 | ## Credit 23 | 24 | All credit and copyrights goes to OpenAI. 25 | 26 | ## Author 27 | 28 | You can find me on Twitter [@vincelwt](https://twitter.com/vincelwt). 29 | -------------------------------------------------------------------------------- /forge.config.js: -------------------------------------------------------------------------------- 1 | const { parsed } = require("dotenv").config(); 2 | module.exports = { 3 | packagerConfig: { 4 | name: "ChatGPT", 5 | executableName: "ChatGPT", 6 | icon: "images/icon", 7 | appBundleId: "com.vincelwt.chatgptmac", 8 | extendInfo: { 9 | LSUIElement: "true", 10 | }, 11 | osxSign: { 12 | hardenedRuntime: false, 13 | gatekeeperAssess: false, 14 | identity: "Developer ID Application: Lyser.io Ltd (R4PF6TTR6Z)", 15 | }, 16 | osxNotarize: { 17 | appBundleId: "com.vincelwt.chatgptmac", 18 | 19 | tool: "notarytool", 20 | appleId: parsed.APPLE_ID, 21 | appleIdPassword: parsed.APPLE_PASSWORD, 22 | teamId: parsed.APPLE_TEAM_ID, 23 | }, 24 | }, 25 | publishers: [ 26 | { 27 | name: "@electron-forge/publisher-github", 28 | config: { 29 | repository: { 30 | owner: "vincelwt", 31 | name: "chatgpt-mac", 32 | }, 33 | prerelease: true, 34 | }, 35 | }, 36 | ], 37 | 38 | rebuildConfig: {}, 39 | makers: [ 40 | { 41 | name: "@electron-forge/maker-squirrel", 42 | config: {}, 43 | }, 44 | { 45 | name: "@electron-forge/maker-dmg", 46 | platforms: ["darwin"], 47 | config: {}, 48 | }, 49 | { 50 | name: "@electron-forge/maker-deb", 51 | config: {}, 52 | }, 53 | { 54 | name: "@electron-forge/maker-rpm", 55 | config: {}, 56 | }, 57 | ], 58 | }; 59 | -------------------------------------------------------------------------------- /images/OpenAI_Logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /images/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincelwt/chatgpt-mac/5b9df7cbee5fc2a73d10800c4f6293a52176fe8a/images/icon.icns -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincelwt/chatgpt-mac/5b9df7cbee5fc2a73d10800c4f6293a52176fe8a/images/icon.png -------------------------------------------------------------------------------- /images/icon@2x.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincelwt/chatgpt-mac/5b9df7cbee5fc2a73d10800c4f6293a52176fe8a/images/icon@2x.icns -------------------------------------------------------------------------------- /images/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincelwt/chatgpt-mac/5b9df7cbee5fc2a73d10800c4f6293a52176fe8a/images/icon@2x.png -------------------------------------------------------------------------------- /images/newiconTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincelwt/chatgpt-mac/5b9df7cbee5fc2a73d10800c4f6293a52176fe8a/images/newiconTemplate.png -------------------------------------------------------------------------------- /images/newiconTemplate@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincelwt/chatgpt-mac/5b9df7cbee5fc2a73d10800c4f6293a52176fe8a/images/newiconTemplate@2x.png -------------------------------------------------------------------------------- /images/screenshot.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincelwt/chatgpt-mac/5b9df7cbee5fc2a73d10800c4f6293a52176fe8a/images/screenshot.jpeg -------------------------------------------------------------------------------- /index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, 3 | Arial, sans-serif; 4 | padding: 0; 5 | margin: 0; 6 | overflow: hidden; 7 | } 8 | 9 | :root { 10 | /* account for the arrow */ 11 | --actual-height: calc(100vh - 12px); 12 | } 13 | 14 | .myarrow { 15 | position: relative; 16 | /* padding-top: 12px; */ 17 | padding: 12px 0 0 0 ; 18 | } 19 | 20 | .myarrow:before { 21 | content: ''; 22 | height: 0; 23 | width: 0; 24 | border-width: 0 8px 12px 8px; 25 | border-style: solid; 26 | border-color: transparent transparent white transparent; 27 | position: absolute; 28 | 29 | top: 0; 30 | left: 50%; 31 | transform: translateX(-50%); 32 | } 33 | 34 | @media (prefers-color-scheme: dark) { 35 | .myarrow:before { 36 | border-color: transparent transparent #343541 transparent !important; 37 | } 38 | } 39 | 40 | .page { 41 | background: #eeeeee; 42 | width: 100%; 43 | height: 100vh; 44 | margin-top: 12px; 45 | margin: 0 auto; 46 | position: relative; 47 | } 48 | 49 | .darwin.page { 50 | border-radius: 8px; 51 | overflow: hidden; 52 | } 53 | 54 | webview { 55 | /* overflow: hidden; */ 56 | position: absolute; 57 | top: 0; 58 | left: 0; 59 | width: 100%; 60 | height: calc(100vh - 12px); 61 | display: inline-flex !important; 62 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ChatGPT 6 | 7 | 8 | 9 |
10 | 11 | 12 |
13 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require("update-electron-app")(); 2 | 3 | const { menubar } = require("menubar"); 4 | const Nucleus = require("nucleus-analytics"); 5 | 6 | const path = require("path"); 7 | const { 8 | app, 9 | nativeImage, 10 | Tray, 11 | Menu, 12 | globalShortcut, 13 | shell, 14 | } = require("electron"); 15 | const contextMenu = require("electron-context-menu"); 16 | 17 | const image = nativeImage.createFromPath( 18 | path.join(__dirname, `images/newiconTemplate.png`) 19 | ); 20 | 21 | app.on("ready", () => { 22 | Nucleus.init("638d9ccf4a5ed2dae43ce122"); 23 | 24 | const tray = new Tray(image); 25 | 26 | const mb = menubar({ 27 | browserWindow: { 28 | icon: image, 29 | transparent: path.join(__dirname, `images/iconApp.png`), 30 | webPreferences: { 31 | webviewTag: true, 32 | // nativeWindowOpen: true, 33 | }, 34 | width: 450, 35 | height: 550, 36 | }, 37 | tray, 38 | showOnAllWorkspaces: true, 39 | preloadWindow: true, 40 | showDockIcon: false, 41 | icon: image, 42 | }); 43 | 44 | mb.on("ready", () => { 45 | const { window } = mb; 46 | 47 | 48 | if (process.platform !== "darwin") { 49 | window.setSkipTaskbar(true); 50 | } else { 51 | app.dock.hide(); 52 | } 53 | 54 | const contextMenuTemplate = [ 55 | // add links to github repo and vince's twitter 56 | { 57 | label: "Quit", 58 | accelerator: "Command+Q", 59 | click: () => { 60 | app.quit(); 61 | }, 62 | }, 63 | { 64 | label: "Reload", 65 | accelerator: "Command+R", 66 | click: () => { 67 | window.reload(); 68 | }, 69 | }, 70 | { 71 | label: "Open in browser", 72 | click: () => { 73 | shell.openExternal("https://chat.openai.com/chat"); 74 | }, 75 | }, 76 | { 77 | type: "separator", 78 | }, 79 | { 80 | label: "View on GitHub", 81 | click: () => { 82 | shell.openExternal("https://github.com/vincelwt/chatgpt-mac"); 83 | }, 84 | }, 85 | { 86 | label: "Author on Twitter", 87 | click: () => { 88 | shell.openExternal("https://twitter.com/vincelwt"); 89 | }, 90 | }, 91 | ]; 92 | 93 | tray.on("right-click", () => { 94 | mb.tray.popUpContextMenu(Menu.buildFromTemplate(contextMenuTemplate)); 95 | }); 96 | 97 | tray.on("click", (e) => { 98 | //check if ctrl or meta key is pressed while clicking 99 | e.ctrlKey || e.metaKey 100 | ? mb.tray.popUpContextMenu(Menu.buildFromTemplate(contextMenuTemplate)) 101 | : null; 102 | }); 103 | const menu = new Menu(); 104 | 105 | globalShortcut.register("CommandOrControl+Shift+g", () => { 106 | if (window.isVisible()) { 107 | mb.hideWindow(); 108 | } else { 109 | mb.showWindow(); 110 | if (process.platform == "darwin") { 111 | mb.app.show(); 112 | } 113 | mb.app.focus(); 114 | } 115 | }); 116 | 117 | Menu.setApplicationMenu(menu); 118 | 119 | // open devtools 120 | // window.webContents.openDevTools(); 121 | 122 | console.log("Menubar app is ready."); 123 | }); 124 | 125 | app.on("web-contents-created", (e, contents) => { 126 | if (contents.getType() == "webview") { 127 | // open link with external browser in webview 128 | contents.on("new-window", (e, url) => { 129 | e.preventDefault(); 130 | shell.openExternal(url); 131 | }); 132 | // set context menu in webview 133 | contextMenu({ 134 | window: contents, 135 | }); 136 | 137 | // we can't set the native app menu with "menubar" so need to manually register these events 138 | // register cmd+c/cmd+v events 139 | contents.on("before-input-event", (event, input) => { 140 | const { control, meta, key } = input; 141 | if (!control && !meta) return; 142 | if (key === "c") contents.copy(); 143 | if (key === "v") contents.paste(); 144 | if (key === "a") contents.selectAll(); 145 | if (key === "z") contents.undo(); 146 | if (key === "y") contents.redo(); 147 | if (key === "q") app.quit(); 148 | if (key === "r") contents.reload(); 149 | }); 150 | } 151 | }); 152 | 153 | if (process.platform == "darwin") { 154 | // restore focus to previous app on hiding 155 | mb.on("after-hide", () => { 156 | mb.app.hide(); 157 | }); 158 | } 159 | 160 | // open links in new window 161 | // app.on("web-contents-created", (event, contents) => { 162 | // contents.on("will-navigate", (event, navigationUrl) => { 163 | // event.preventDefault(); 164 | // shell.openExternal(navigationUrl); 165 | // }); 166 | // }); 167 | 168 | // prevent background flickering 169 | app.commandLine.appendSwitch( 170 | "disable-backgrounding-occluded-windows", 171 | "true" 172 | ); 173 | }); 174 | 175 | // Quit when all windows are closed, except on macOS. There, it's common 176 | // for applications and their menu bar to stay active until the user quits 177 | // explicitly with Cmd + Q. 178 | app.on("window-all-closed", () => { 179 | if (process.platform !== "darwin") { 180 | app.quit(); 181 | } 182 | }); 183 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincelwt/chatgpt-mac/5b9df7cbee5fc2a73d10800c4f6293a52176fe8a/package-lock.json -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chatgpt-mac", 3 | "productName": "chatgpt-mac", 4 | "version": "0.0.5", 5 | "description": "Menubar app for ChatGPT", 6 | "main": "index.js", 7 | "scripts": { 8 | "start": "electron-forge start", 9 | "package": "electron-forge package --arch arm64,x64", 10 | "make": " electron-forge make --arch arm64,x64", 11 | "publish": "electron-forge publish --arch arm64,x64", 12 | "lint": "echo \"No linting configured\"" 13 | }, 14 | "keywords": [ 15 | "chatgpt", 16 | "openai", 17 | "mac" 18 | ], 19 | "author": { 20 | "name": "Vince Loewe", 21 | "email": "vince@lyser.io" 22 | }, 23 | "license": "MIT", 24 | "dependencies": { 25 | "electron-context-menu": "^3.6.0", 26 | "menubar": "^9.2.3", 27 | "nucleus-analytics": "^4.0.3", 28 | "update-electron-app": "^2.0.1" 29 | }, 30 | "repository": { 31 | "type": "git", 32 | "url": "https://github.com/vincelwt/chatgpt-mac" 33 | }, 34 | "devDependencies": { 35 | "@electron-forge/cli": "^6.0.4", 36 | "@electron-forge/maker-deb": "^6.0.4", 37 | "@electron-forge/maker-dmg": "^6.0.4", 38 | "@electron-forge/maker-rpm": "^6.0.4", 39 | "@electron-forge/maker-squirrel": "^6.0.4", 40 | "@electron-forge/maker-zip": "^6.0.4", 41 | "@electron-forge/publisher-github": "^6.0.4", 42 | "dotenv": "^16.0.3", 43 | "electron": "^21.0.0", 44 | "electron-squirrel-startup": "^1.0.0" 45 | } 46 | } 47 | --------------------------------------------------------------------------------