├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc.yaml ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── README.md ├── electron-builder.yml ├── litellm_uuid.txt ├── package-lock.json ├── package.json ├── resources ├── icon.png └── old_icon.png ├── screenshots ├── Screenshot_1.png ├── Screenshot_2.png └── cover_art.png └── src ├── index.html ├── main.js ├── preload.js ├── renderer.js └── style.css /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | out 4 | .gitignore 5 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['eslint:recommended', '@electron-toolkit', '@electron-toolkit/eslint-config-prettier'] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | out 4 | *.log* 5 | litellm_uuid.txt 6 | 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | pnpm-lock.yaml 4 | LICENSE.md 5 | tsconfig.json 6 | tsconfig.*.json 7 | -------------------------------------------------------------------------------- /.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | singleQuote: true 2 | semi: false 3 | printWidth: 100 4 | trailingComma: none 5 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["dbaeumer.vscode-eslint"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug Main Process", 6 | "type": "node", 7 | "request": "launch", 8 | "cwd": "${workspaceRoot}", 9 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite", 10 | "windows": { 11 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd" 12 | }, 13 | "runtimeArgs": ["--sourcemap"], 14 | "env": { 15 | "REMOTE_DEBUGGING_PORT": "9222" 16 | } 17 | }, 18 | { 19 | "name": "Debug Renderer Process", 20 | "port": 9222, 21 | "request": "attach", 22 | "type": "chrome", 23 | "webRoot": "${workspaceFolder}/src/renderer", 24 | "timeout": 60000, 25 | "presentation": { 26 | "hidden": true 27 | } 28 | } 29 | ], 30 | "compounds": [ 31 | { 32 | "name": "Debug All", 33 | "configurations": ["Debug Main Process", "Debug Renderer Process"], 34 | "presentation": { 35 | "order": 1 36 | } 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[typescript]": { 3 | "editor.defaultFormatter": "esbenp.prettier-vscode" 4 | }, 5 | "[javascript]": { 6 | "editor.defaultFormatter": "esbenp.prettier-vscode" 7 | }, 8 | "[json]": { 9 | "editor.defaultFormatter": "esbenp.prettier-vscode" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Electerpreter 2 | 3 | A minimal Electron application to run and display the output of open-interpreter 4 | this is a work in progress and was intended to be my entry for the lablab.ai open-interpreter 24hour hackathon 5 | 6 | link to entry here: https://lablab.ai/event/open-interpreter-hackathon/big-full-stack-gamers/electerpreter-open-interpreter-electron-app 7 | 8 | ![CoverArt](/screenshots/cover_art.png) 9 | 10 | ## Features 11 | 12 | - Runs open-interpreter within an electron app 13 | - Displays the output of open-interpreter 14 | - Allows for the user to send an input to open-interpreter 15 | - Uses Open-Interpreter to automatically execute code that the llm comes up with 16 | 17 | ## TODO 18 | 19 | - [ ] have a spefic argument to send to open-interpreter so that it knows that we are using the electron app and not the cli may make it easier to use 20 | - [ ] a way to change the arguments in the spawned process of open-interpreter 21 | 22 | ## Screenshots 23 | 24 | ![Screenshot](/screenshots/Screenshot_1.png) 25 | ![Screenshot](/screenshots/Screenshot_2.png) 26 | 27 | ## Recommended IDE Setup 28 | 29 | - [VSCode](https://code.visualstudio.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) 30 | 31 | ## Project Setup 32 | 33 | ### Install 34 | 35 | ```bash 36 | $ npm install 37 | ``` 38 | 39 | ### Development 40 | 41 | ```bash 42 | $ npm run dev 43 | ``` 44 | 45 | ### Build 46 | 47 | ```bash 48 | # For windows 49 | $ npm run build:win 50 | 51 | # For macOS 52 | $ npm run build:mac 53 | 54 | # For Linux 55 | $ npm run build:linux 56 | ``` 57 | 58 | ## Note 59 | 60 | if you want to use this application it is currently setup to run the open-interpreter cli with the specific aruements i use for testing which are 61 | 62 | ```bash 63 | $ open-interpreter --context_window 2042 -y 64 | ``` 65 | 66 | this can be changed on line 13 of the preload.js file, you will need to set your environment variable OPENAI_API_KEY -------------------------------------------------------------------------------- /electron-builder.yml: -------------------------------------------------------------------------------- 1 | appId: com.electron.app 2 | productName: open-execute 3 | directories: 4 | buildResources: build 5 | files: 6 | - '!**/.vscode/*' 7 | - '!{.eslintignore,.eslintrc.js,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}' 8 | asarUnpack: 9 | - resources/** 10 | win: 11 | executableName: open-execute 12 | nsis: 13 | artifactName: ${name}-${version}-setup.${ext} 14 | shortcutName: ${productName} 15 | uninstallDisplayName: ${productName} 16 | createDesktopShortcut: always 17 | mac: 18 | entitlementsInherit: build/entitlements.mac.plist 19 | extendInfo: 20 | - NSCameraUsageDescription: Application requests access to the device's camera. 21 | - NSMicrophoneUsageDescription: Application requests access to the device's microphone. 22 | - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder. 23 | - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. 24 | notarize: false 25 | dmg: 26 | artifactName: ${name}-${version}.${ext} 27 | linux: 28 | target: 29 | - AppImage 30 | - snap 31 | - deb 32 | maintainer: electronjs.org 33 | category: Utility 34 | appImage: 35 | artifactName: ${name}-${version}.${ext} 36 | npmRebuild: false 37 | publish: 38 | provider: generic 39 | url: https://example.com/auto-updates 40 | -------------------------------------------------------------------------------- /litellm_uuid.txt: -------------------------------------------------------------------------------- 1 | 7e0fcc89-793a-442b-b05b-253baab4150f -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "open-execute", 3 | "version": "1.0.0", 4 | "description": "A electron app to open and execute open-interpreter queries", 5 | "main": "./src/main.js", 6 | "author": "Rowan wood", 7 | "homepage": "https://www.electronjs.org", 8 | "scripts": { 9 | "format": "prettier --write .", 10 | "lint": "eslint --ext .js .", 11 | "dev": "electron .", 12 | "postinstall": "electron-builder install-app-deps", 13 | "build:win": "electron-builder --win --config", 14 | "build:mac": "electron-builder --mac --config", 15 | "build:linux": "electron-builder --linux --config" 16 | }, 17 | "dependencies": { 18 | "@electron-toolkit/preload": "^2.0.0", 19 | "@electron-toolkit/utils": "^2.0.0" 20 | }, 21 | "devDependencies": { 22 | "@electron-toolkit/eslint-config": "^1.0.1", 23 | "@electron-toolkit/eslint-config-prettier": "^1.0.1", 24 | "electron": "^25.6.0", 25 | "electron-builder": "^24.6.3", 26 | "electron-vite": "^1.0.28", 27 | "eslint": "^8.47.0", 28 | "prettier": "^3.0.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrdiamonddirt/Electerpreter/8df633a2b17ab3fb03753fb2995eb1f0fcf4e3a9/resources/icon.png -------------------------------------------------------------------------------- /resources/old_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrdiamonddirt/Electerpreter/8df633a2b17ab3fb03753fb2995eb1f0fcf4e3a9/resources/old_icon.png -------------------------------------------------------------------------------- /screenshots/Screenshot_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrdiamonddirt/Electerpreter/8df633a2b17ab3fb03753fb2995eb1f0fcf4e3a9/screenshots/Screenshot_1.png -------------------------------------------------------------------------------- /screenshots/Screenshot_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrdiamonddirt/Electerpreter/8df633a2b17ab3fb03753fb2995eb1f0fcf4e3a9/screenshots/Screenshot_2.png -------------------------------------------------------------------------------- /screenshots/cover_art.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrdiamonddirt/Electerpreter/8df633a2b17ab3fb03753fb2995eb1f0fcf4e3a9/screenshots/cover_art.png -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Electerpreter 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 | 16 |
17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // Modules to control application life and create native browser window 2 | const { app, shell, BrowserWindow } = require('electron') 3 | const path = require('path') 4 | const { electronApp, optimizer } = require('@electron-toolkit/utils') 5 | 6 | function createWindow() { 7 | // Create the browser window. 8 | const mainWindow = new BrowserWindow({ 9 | width: 900, 10 | height: 670, 11 | show: false, 12 | autoHideMenuBar: true, 13 | ...(process.platform === 'linux' 14 | ? { 15 | icon: path.join(__dirname, '../resources/icon.png') 16 | } 17 | : {}), 18 | webPreferences: { 19 | preload: path.join(__dirname, 'preload.js'), 20 | // sandbox: true, 21 | nodeIntegration: true, 22 | contentSecurityPolicy: ` 23 | default-src 'none' 'self' 'unsafe-inline'; 24 | `, 25 | } 26 | }) 27 | 28 | mainWindow.webContents.openDevTools() 29 | 30 | mainWindow.on('ready-to-show', () => { 31 | mainWindow.show() 32 | }) 33 | 34 | mainWindow.webContents.setWindowOpenHandler((details) => { 35 | shell.openExternal(details.url) 36 | return { action: 'deny' } 37 | }) 38 | 39 | // and load the index.html of the app. 40 | mainWindow.loadFile(path.join(__dirname, 'index.html')) 41 | } 42 | 43 | // This method will be called when Electron has finished 44 | // initialization and is ready to create browser windows. 45 | // Some APIs can only be used after this event occurs. 46 | app.whenReady().then(() => { 47 | // Set app user model id for windows 48 | electronApp.setAppUserModelId('com.electron') 49 | 50 | // Default open or close DevTools by F12 in development 51 | // and ignore CommandOrControl + R in production. 52 | // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils 53 | app.on('browser-window-created', (_, window) => { 54 | optimizer.watchWindowShortcuts(window) 55 | }) 56 | 57 | createWindow() 58 | 59 | app.on('activate', function () { 60 | // On macOS it's common to re-create a window in the app when the 61 | // dock icon is clicked and there are no other windows open. 62 | if (BrowserWindow.getAllWindows().length === 0) createWindow() 63 | }) 64 | }) 65 | 66 | // Quit when all windows are closed, except on macOS. There, it's common 67 | // for applications and their menu bar to stay active until the user quits 68 | // explicitly with Cmd + Q. 69 | app.on('window-all-closed', function () { 70 | if (process.platform !== 'darwin') { 71 | app.quit() 72 | } 73 | }) 74 | 75 | // In this file you can include the rest of your app's specific main process 76 | // code. You can also put them in separate files and require them here. 77 | 78 | // mainWindow.addEventListner('message', (event) => { 79 | // if (event.source === window){ 80 | // console.log("from preload", event.data) 81 | 82 | // if (event.data === 'ready') { 83 | // console.log('interpreter is ready') 84 | // } 85 | // } 86 | // }) -------------------------------------------------------------------------------- /src/preload.js: -------------------------------------------------------------------------------- 1 | // All of the Node.js APIs are available in the preload process. 2 | // It has the same sandbox as a Chrome extension. 3 | const { contextBridge } = require('electron') 4 | const { electronAPI } = require('@electron-toolkit/preload') 5 | const {spawn} = require('node:child_process') 6 | const { create } = require('domain'); 7 | const { decode } = require('node:punycode'); 8 | 9 | // Set the PYTHONIOENCODING environment variable to UTF-8 10 | process.env.PYTHONIOENCODING = 'UTF-8'; 11 | 12 | function createOpenInterpreterProcess() { 13 | const createProcess = spawn('interpreter', ['--context_window', '2042', '-y'],{ 14 | shell: true, 15 | stdio: ['pipe', 'pipe', 'pipe'], 16 | env: process.env, 17 | // cwd: process.cwd(), 18 | // windowsHide: true, 19 | detached: false, 20 | 21 | }) 22 | 23 | createProcess.stdout.on('data', (data) => { 24 | // log the data 25 | console.log(`stdout: ${data}`) 26 | // send message to renderer 27 | // add the word 'bot-stdount' to the beginning of the message so we can tell the difference between bot output and other output 28 | window.postMessage('bot-stdout' + data, '*') 29 | }) 30 | createProcess.stderr.on('data', (data) => { 31 | data = data.toString() 32 | console.error(`stderr: ${data}`) 33 | if (data.includes('Downloading')) { 34 | // get numbers before % 35 | const percent = data.match(/\d+(?=%)/)[0] 36 | console.log(percent) 37 | window.postMessage('Downloading', '*') 38 | } 39 | }) 40 | 41 | // get anything being piped out of the process 42 | createProcess.stdio[1].on('data', (data) => { 43 | console.log(`stdio[1]: ${data}`) 44 | }) 45 | 46 | // get anything being piped into the process 47 | createProcess.stdio[0].on('data', (data) => { 48 | console.log(`stdio[0]: ${data}`) 49 | }) 50 | 51 | createProcess.on('close', (code) => { 52 | console.log(`child process exited with code ${code}`) 53 | }) 54 | createProcess.on('error', (err) => { 55 | console.error(err) 56 | }) 57 | 58 | // send messages to the process 59 | window.addEventListener('message', (event) => { 60 | if (event.source === window) { 61 | if (event.data !== 'ready' && event.data !== 'command' && event.data !== 'Downloading') { 62 | // and event.data doesnt degin with 'bot-stdout' 63 | if (event.data.startsWith('bot-stdout')) { 64 | if (event.data.includes('Would you like to run this code? (y/n)')) { 65 | console.log('we have some code to run') 66 | // just run it *test* 67 | // createProcess.stdio[1].write('y') 68 | } 69 | // send message to renderer 70 | // window.postMessage('bot-stdout' + event.data, '*') 71 | } 72 | if (event.data.startsWith('user-input')) { 73 | // remove 'user-input' from the beginning of the message 74 | const message = event.data.replace('user-input', '') 75 | createProcess.stdio[0].write(event.data + '\n') 76 | // add output 77 | // addOutput(message) 78 | } 79 | // decodedData = new TextDecoder('utf-8').decode(event.data) 80 | // console.log("sent decoded data: ", decodedData) 81 | } 82 | } 83 | }) 84 | } 85 | 86 | createOpenInterpreterProcess() 87 | 88 | 89 | // Custom APIs for renderer 90 | const api = {} 91 | 92 | // Use `contextBridge` APIs to expose Electron APIs to 93 | // renderer only if context isolation is enabled, otherwise 94 | // just add to the DOM global. 95 | if (process.contextIsolated) { 96 | try { 97 | contextBridge.exposeInMainWorld('electron', electronAPI) 98 | contextBridge.exposeInMainWorld('api', api) 99 | } catch (error) { 100 | console.error(error) 101 | } 102 | } else { 103 | // window.electron = electronAPI 104 | window.api = api 105 | } 106 | -------------------------------------------------------------------------------- /src/renderer.js: -------------------------------------------------------------------------------- 1 | // This file is required by the index.html file and will 2 | // be executed in the renderer process for that window. 3 | // No Node.js APIs are available in this process because 4 | // `nodeIntegration` is turned off. Use `preload.js` to 5 | // selectively enable features needed in the rendering 6 | ;(async () => { 7 | var isReady = false 8 | 9 | window.addEventListener('DOMContentLoaded', () => { 10 | outputcontent = document.getElementById('interpreter-output') 11 | submitbutton = document.getElementById('prompt-submit') 12 | input = document.getElementById('prompt-input') 13 | 14 | // event listener for enter key when input is focused and submit button is not disabled 15 | input.addEventListener('keydown', (event) => { 16 | if (event.key === 'Enter' && !submitbutton.disabled) { 17 | submitbutton.click() 18 | } 19 | }) 20 | 21 | submitbutton.addEventListener('click', () => { 22 | // send input to python 23 | // at the begining of the message add 'user-input' so we can tell the difference between user input and other output 24 | window.postMessage('user-input ' + input.value, '*') 25 | // window.postMessage('command', '*') 26 | // clear input 27 | addInput(input.value) 28 | input.value = '' 29 | }) 30 | }) 31 | 32 | 33 | 34 | var output 35 | 36 | function addOutput(message) { 37 | if (message.trim() !== '') { // Check if the message is not empty or only whitespace 38 | outputcontent.innerHTML += `
Open-Interpreter: ${message}
`; 39 | // scroll to the bottom of the container 40 | const lastElement = outputcontent.lastElementChild; 41 | if (lastElement) { 42 | lastElement.scrollIntoView({ behavior: 'smooth', block: 'end' }); 43 | } 44 | } 45 | } 46 | 47 | function addInput(message) { 48 | if (message.trim() !== '') { // Check if the message is not empty or only whitespace 49 | outputcontent.innerHTML += `
Your Prompt: ${message}
`; 50 | // scroll to the bottom of the container 51 | const lastElement = outputcontent.lastElementChild; 52 | if (lastElement) { 53 | lastElement.scrollIntoView({ behavior: 'smooth', block: 'end' }); 54 | } 55 | } 56 | } 57 | 58 | window.addEventListener('message', (event) => { 59 | if (event.data.startsWith('bot-stdout')) { 60 | // remove 'bot-stdout' from the beginning of the message 61 | const message = event.data.replace('bot-stdout', '') 62 | // add output 63 | console.log("message", message) 64 | addOutput(message) 65 | } 66 | if (event.data !== 'ready') { 67 | // check type of event.data 68 | if (typeof event.data === 'string') { 69 | console.log('string') 70 | console.log("sent event data: ",event.data) 71 | } else if (typeof event.data === 'object') { 72 | console.log('object') 73 | const textDecoder = new TextDecoder('utf-8'); 74 | const decodedData = textDecoder.decode(event.data); 75 | output = decodedData 76 | console.log("sent event data: ",decodedData) 77 | } else { 78 | console.log('other') 79 | console.log("other event data: ",event.data) 80 | } 81 | } 82 | }) 83 | })() 84 | -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | /* Add styles here to customize the appearance of your app */ 2 | body { 3 | display: flex; 4 | flex-direction: column; 5 | font-family: 6 | Roboto, 7 | -apple-system, 8 | BlinkMacSystemFont, 9 | 'Helvetica Neue', 10 | 'Segoe UI', 11 | 'Oxygen', 12 | 'Ubuntu', 13 | 'Cantarell', 14 | 'Open Sans', 15 | sans-serif; 16 | color: #86a5b1; 17 | background-color: #2f3241; 18 | overflow: hidden; 19 | } 20 | 21 | /* less shit scroll bars */ 22 | 23 | ::-webkit-scrollbar { 24 | width: 5px; 25 | height: 5px; 26 | } 27 | 28 | /* Track */ 29 | ::-webkit-scrollbar-track { 30 | margin-top: 20px; 31 | margin-bottom: 50px; 32 | box-shadow: inset 0 0 5px grey; 33 | border-radius: 10px; 34 | } 35 | 36 | /* Handle */ 37 | ::-webkit-scrollbar-thumb { 38 | background: rgb(4, 249, 4); 39 | border-radius: 10px; 40 | } 41 | 42 | #output-container { 43 | display: flex; 44 | flex-direction: column; 45 | background-color: rgb(79, 79, 80); 46 | border: 1px solid black; 47 | width: 100%; 48 | height: calc(100vh - 80px); 49 | border-radius: 5px; 50 | overflow-y: scroll; 51 | overflow-x: hidden; 52 | } 53 | 54 | 55 | .bot { 56 | background-color: black; 57 | color: white; 58 | border: 1px solid white; 59 | border-radius: 5px; 60 | margin: 10px; 61 | padding: 5px; 62 | } 63 | 64 | .user { 65 | background-color: #000f58; 66 | color: white; 67 | border: 1px solid #86a5b1; 68 | border-radius: 5px; 69 | margin: 10px; 70 | padding: 5px; 71 | } 72 | 73 | #container { 74 | position: relative; 75 | } 76 | 77 | #prompt-container { 78 | display: flex; 79 | position: absolute; 80 | bottom: 20px; 81 | flex-direction: row; 82 | justify-content: center; 83 | align-items: center; 84 | width: 100vw; 85 | } 86 | 87 | #prompt-input { 88 | /* modern style input box */ 89 | height: 30px; 90 | width: 80vw; 91 | } 92 | 93 | #prompt-submit { 94 | width: 50px; 95 | height: 30px; 96 | border-radius: 2px; 97 | background-color: green; 98 | color: black; 99 | } 100 | 101 | * { 102 | padding: 0; 103 | margin: 0; 104 | } 105 | 106 | ul { 107 | list-style: none; 108 | } 109 | 110 | code { 111 | font-weight: 600; 112 | padding: 3px 5px; 113 | border-radius: 2px; 114 | background-color: #26282e; 115 | font-family: 116 | ui-monospace, 117 | SFMono-Regular, 118 | SF Mono, 119 | Menlo, 120 | Consolas, 121 | Liberation Mono, 122 | monospace; 123 | font-size: 85%; 124 | } 125 | 126 | a { 127 | color: #9feaf9; 128 | font-weight: 600; 129 | cursor: pointer; 130 | text-decoration: none; 131 | outline: none; 132 | } 133 | 134 | a:hover { 135 | border-bottom: 1px solid; 136 | } 137 | 138 | .container { 139 | flex: 1; 140 | display: flex; 141 | flex-direction: column; 142 | max-width: 900px; 143 | margin: 0 auto; 144 | padding: 15px 30px 0 30px; 145 | box-sizing: border-box; 146 | } 147 | 148 | 149 | @media (min-width: 840px) { 150 | .container { 151 | width: 100%; 152 | } 153 | } --------------------------------------------------------------------------------