An embeddable code editor for the browser 🍯
A Pen by Anton Medvedev on CodePen.
An embeddable code editor for the browser 🍯
A Pen by Anton Medvedev on CodePen.
| <main class="view"> | |
| <div class="logo"></div> | |
| <h1 class="title"> | |
| CodeJar Code Editor | |
| </h1> | |
| <div class="window"> | |
| <div class="window-header"> | |
| <div class="action-buttons"></div> | |
| <div class="controls"> | |
| <div>language: <a class="switch-language" href="">js</a></div> | |
| <div>style: <a class="switch-style" href="">dracula</a></div> | |
| </div> | |
| </div> | |
| <div class="window-body"> | |
| <div class="editor language-js" data-gramm="false"></div> | |
| </div> | |
| </div> | |
| <div class="features"> | |
| <h2>Features</h2> | |
| <ul> | |
| <li>Lightweight (2 kB only) | |
| <li>Preserves indentation on a new line | |
| <li>Adds closing brackets, quotes | |
| <li>Indents line with the Tab key | |
| <li>Supports undo/redo | |
| </ul> | |
| </div> | |
| </main> |
| import { CodeJar } from "https://medv.io/codejar/codejar.js"; | |
| const highlight = (editor) => { | |
| // highlight.js does not trims old tags, | |
| // let's do it by this hack. | |
| editor.textContent = editor.textContent; | |
| hljs.highlightBlock(editor); | |
| }; | |
| const editor = document.querySelector(".editor"); | |
| const jar = new CodeJar(editor, highlight); | |
| let currentStyle = 0; | |
| const styles = [ | |
| "dracula", | |
| "github", | |
| "solarized-dark", | |
| "solarized-light", | |
| "railscasts", | |
| "monokai-sublime", | |
| "mono-blue", | |
| "tomorrow", | |
| "color-brewer", | |
| "zenburn", | |
| "agate", | |
| "androidstudio", | |
| "atom-one-light", | |
| "rainbow", | |
| "vs", | |
| "atom-one-dark" | |
| ].map((name) => { | |
| const link = document.createElement("link"); | |
| link.href = `https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.1/styles/${name}.min.css`; | |
| link.rel = "stylesheet"; | |
| link.disabled = "true"; | |
| document.head.appendChild(link); | |
| return link; | |
| }); | |
| styles[currentStyle].removeAttribute("disabled"); | |
| const switchStyleButton = document.querySelector(".switch-style"); | |
| switchStyleButton.addEventListener("click", (event) => { | |
| event.preventDefault(); | |
| styles[currentStyle].setAttribute("disabled", "true"); | |
| currentStyle = (currentStyle + 1) % styles.length; | |
| styles[currentStyle].removeAttribute("disabled"); | |
| let [, name] = styles[currentStyle].href.match( | |
| /highlight.js.+?\/styles\/(.+?)\.min\.css$/ | |
| ); | |
| switchStyleButton.textContent = name; | |
| }); | |
| let currentLanguage = 0; | |
| const languages = [ | |
| function () { | |
| editor.className = "editor language-js"; | |
| jar.updateCode(`import {CodeJar} from '@medv/codejar'; | |
| import Prism from 'prismjs'; | |
| const editor = document.querySelector('#editor'); | |
| const jar = new CodeJar(editor, Prism.highlightElement, {tab: '\\t'}); | |
| // Update code | |
| jar.updateCode('let foo = bar'); | |
| // Get code | |
| let code = jar.toString(); | |
| // Listen to updates | |
| jar.onUpdate(code => { | |
| console.log(code); | |
| }); | |
| `); | |
| jar.updateOptions({ tab: " " }); | |
| }, | |
| function () { | |
| editor.className = "editor language-md"; | |
| jar.updateCode(`# CodeJar | |
| An embeddable code editor for the browser 🍯 | |
| ## Features | |
| * Lightweight (**2 kB** only) | |
| * Preserves indentation on a new line | |
| * Adds closing brackets, quotes | |
| * Indents line with the \`Tab\` key | |
| * Supports *undo*/*redo* | |
| ## Getting Started | |
| \`\`\`bash | |
| npm i @medv/codejar | |
| \`\`\``); | |
| jar.updateOptions({ tab: " " }); | |
| }, | |
| function () { | |
| editor.className = "editor language-go"; | |
| jar.updateCode(`package main | |
| import ( | |
| \t"fmt" | |
| \t"github.com/antonmedv/expr" | |
| ) | |
| func main() { | |
| \tfmt.Println("Hello, CodeJar") | |
| \toutput, err := expr.Eval("1+2") | |
| \tif err != nil { | |
| \t\tpanic(err) | |
| \t} | |
| } | |
| `); | |
| jar.updateOptions({ tab: "\t" }); | |
| }, | |
| function () { | |
| editor.className = "editor language-ts"; | |
| jar.updateCode(`interface Person { | |
| firstName: string; | |
| lastName: string; | |
| } | |
| function greeter(person: Person) { | |
| return "Hello, " + person.firstName + " " + person.lastName; | |
| } | |
| let user = { | |
| firstName: "Jane", | |
| lastName: "User" | |
| }; | |
| document.body.textContent = greeter(user);`); | |
| jar.updateOptions({ tab: " " }); | |
| }, | |
| function () { | |
| editor.className = "editor language-rust"; | |
| jar.updateCode(`#[derive(Debug)] | |
| struct Rectangle { | |
| width: u32, | |
| height: u32, | |
| } | |
| impl Rectangle { | |
| fn area(&self) -> u32 { | |
| self.width * self.height | |
| } | |
| } | |
| fn main() { | |
| let rect1 = Rectangle { width: 30, height: 50 }; | |
| println!( | |
| "The area of the rectangle is {} square pixels.", | |
| rect1.area() | |
| ); | |
| }`); | |
| jar.updateOptions({ tab: " " }); | |
| }, | |
| function () { | |
| editor.className = "editor language-html"; | |
| jar.updateCode(`<!doctype html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>CodeJar</title> | |
| <meta name="author" content="Anton Medvedev"> | |
| <meta name="description" content="Micro Code Editor"> | |
| </head> | |
| <body> | |
| <h1>CodeJar — Micro Code Editor</h1> | |
| </body> | |
| </html>`); | |
| jar.updateOptions({ tab: " " }); | |
| }, | |
| function () { | |
| editor.className = "editor language-kotlin"; | |
| jar.updateCode(`suspend fun main() = coroutineScope { | |
| for (i in 0 until 10) { | |
| launch { | |
| delay(1000L - i * 10) | |
| print("❤️$i ") | |
| } | |
| } | |
| } | |
| val positiveNumbers = list.filter { it > 0 } | |
| fun calculateTotal(obj: Any) { | |
| if (obj is Invoice) | |
| obj.calculateTotal() | |
| }`); | |
| jar.updateOptions({ tab: " " }); | |
| } | |
| ]; | |
| languages[currentLanguage](); | |
| const switchLanguageButton = document.querySelector(".switch-language"); | |
| switchLanguageButton.addEventListener("click", (event) => { | |
| event.preventDefault(); | |
| currentLanguage = (currentLanguage + 1) % languages.length; | |
| languages[currentLanguage](); | |
| const [, name] = editor.className.match(/language-(\w+)/); | |
| switchLanguageButton.textContent = name; | |
| }); |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.1/highlight.min.js"></script> |
| @import url("https://fonts.googleapis.com/css2?family=Lato:wght@300&family=PT+Mono&display=swap"); | |
| @import url("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.1/styles/dracula.min.css"); | |
| body { | |
| background-image: linear-gradient( | |
| 109.6deg, | |
| rgba(253, 199, 141, 1) 11.3%, | |
| rgba(249, 143, 253, 1) 100.2% | |
| ); | |
| font-family: Lato, sans-serif; | |
| font-weight: 300; | |
| font-size: 15px; | |
| margin: 0; | |
| } | |
| *, | |
| *:before, | |
| *:after { | |
| box-sizing: border-box; | |
| } | |
| *:focus { | |
| outline: none; | |
| } | |
| a, | |
| a:visited, | |
| a:active { | |
| color: black; | |
| } | |
| main { | |
| min-height: 100vh; | |
| display: flex; | |
| align-items: center; | |
| flex-direction: column; | |
| } | |
| .logo { | |
| display: block; | |
| margin: 30px auto 10px; | |
| width: calc(145px / 2); | |
| height: calc(185px / 2); | |
| background: url(https://medv.io/codejar/codejar.svg) no-repeat; | |
| background-size: contain; | |
| } | |
| .title { | |
| color: #fff; | |
| text-align: center; | |
| font-weight: 300; | |
| text-shadow: 0 1px 0 rgba(0, 0, 0, 0.2); | |
| font-size: 34px; | |
| margin-top: 20px; | |
| } | |
| .window { | |
| width: 547px; | |
| border-radius: 6px; | |
| box-shadow: 0 8px 12px rgba(0, 0, 0, 0.1); | |
| overflow: hidden; | |
| margin-bottom: 20px; | |
| } | |
| .window .window-header { | |
| height: 25px; | |
| background: Gainsboro; | |
| position: relative; | |
| } | |
| .window .window-header .action-buttons { | |
| position: absolute; | |
| top: 50%; | |
| left: 10px; | |
| margin-top: -5px; | |
| width: 10px; | |
| height: 10px; | |
| background: Crimson; | |
| border-radius: 50%; | |
| box-shadow: 15px 0 0 Orange, 30px 0 0 LimeGreen; | |
| } | |
| .editor { | |
| border-bottom-left-radius: 6px; | |
| border-bottom-right-radius: 6px; | |
| box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), | |
| 0 3px 1px -2px rgba(0, 0, 0, 0.2); | |
| font-family: "PT Mono", monospace; | |
| font-size: 14px; | |
| font-weight: 400; | |
| min-height: 340px; | |
| letter-spacing: normal; | |
| line-height: 20px; | |
| padding: 10px; | |
| resize: none !important; | |
| tab-size: 4; | |
| } | |
| .editor.hljs { | |
| padding: 10px; | |
| } | |
| .controls { | |
| font-size: 14px; | |
| position: absolute; | |
| top: 50%; | |
| right: 10px; | |
| margin-top: -10px; | |
| display: flex; | |
| } | |
| .controls > div:first-child > a { | |
| display: inline-block; | |
| width: 40px; | |
| } | |
| .features { | |
| width: 547px; | |
| font-size: 16px; | |
| margin-bottom: 30px; | |
| } |