import "/static/vendor/ace/ace.js"; import { $el } from "/static/vendor/domutil/domutil.js"; class Editor extends HTMLElement { private editor: AceAjax.Editor; private tabEl: HTMLDivElement; private sessions: Record; private root: ShadowRoot; private addTabButton: HTMLDivElement; constructor() { super(); this.sessions = {}; this.root = this.attachShadow({ mode: "open" }); } connectedCallback() { this.addTabButton = $el("div", { className: "tab" }, "+"); this.addTabButton.addEventListener("click", () => this.#createNewTab()); this.tabEl = $el("div", { className: "tab-container" }, this.addTabButton); this.root.append(this.tabEl); const style = $el("style"); style.textContent = ` .tab-container { display: flex; margin-bottom: -2px; & .tab { border: 2px solid var(--table-border-color); padding: 0.5ch 0.8ch; background-color: var(--button-bg); cursor: pointer; &.active { background-color: var(--table-border-color); } &:not(:first-child) { margin-left: -2px; } } } `; const slot = $el("slot", { name: "body" }); this.root.append(style, slot); const editorEl = $el("div", { slot: "body" }); this.append(editorEl); // Create editor ace.config.set('basePath', '/static/vendor/ace') this.editor = ace.edit(editorEl); // todo make this stuff customizable?? this.editor.setTheme("ace/theme/dracula"); this.editor.setOptions({ fontFamily: "Iosevka Web", fontSize: "12pt", }); this.editor.getSession().setUseWrapMode(true); this.editor.setKeyboardHandler("ace/keyboard/vim"); } /** * Add a file editing session * @param name File name * @param content File contents */ addFile(name: string, content: string) { // Add to session list this.sessions[name] = ace.createEditSession(content); // TODO replace this with auto-detection this.sessions[name].setMode("ace/mode/nix"); // Create tab and set as active const tab = this.#addTab(name); tab.click(); } setCurrent(name: string) { this.editor.setSession(this.sessions[name]); this.tabEl.querySelectorAll(".tab").forEach(el => { if (el.dataset.name === name) el.classList.add("active"); else el.classList.remove("active"); }); } files(): [String, String][] { return Object.entries(this.sessions).map(([name, session]) => [name, session.getValue()]); } /** * Create a new tab * @param name Tab name * @returns Tab element */ #addTab(name: string) { const tab = $el("div", { className: "tab", dataset: { name }, "@click": () => this.setCurrent(name) }, name); this.tabEl.insertBefore(tab, this.addTabButton); return tab; } #createNewTab() { this.addFile(`untitled${Object.keys(this.sessions).filter(s => s.startsWith("untitled")).length + 1}`, ""); } } customElements.define("file-editor", Editor);