Skip to content

Instantly share code, notes, and snippets.

@Wigny
Created March 8, 2026 03:28
Show Gist options
  • Select an option

  • Save Wigny/4f713c7edd8064c62c9722c76ecf11b3 to your computer and use it in GitHub Desktop.

Select an option

Save Wigny/4f713c7edd8064c62c9722c76ecf11b3 to your computer and use it in GitHub Desktop.
// https://github.com/microsoft/monaco-editor/blob/v0.38.0/samples/browser-esm-esbuild/index.js
import * as monaco from "monaco-editor";
self.MonacoEnvironment = {
getWorkerUrl: (moduleId, label) => {
switch (label) {
case "json":
return "/admin/assets/monaco-editor/esm/vs/language/json/json.worker.js";
case "css":
case "scss":
case "less":
return "/admin/assets/monaco-editor/esm/vs/language/css/css.worker.js";
case "html":
case "handlebars":
case "razor":
return "/admin/assets/monaco-editor/esm/vs/language/html/html.worker.js";
case "typescript":
case "javascript":
return "/admin/assets/monaco-editor/esm/vs/language/typescript/ts.worker.js";
default:
return "/admin/assets/monaco-editor/esm/vs/editor/editor.worker.js";
}
},
};
export class MonacoEditorElement extends HTMLElement {
private editor?: monaco.editor.IStandaloneCodeEditor;
private input?: HTMLInputElement;
static get observedAttributes() {
return ["language", "readonly"];
}
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: "open" });
const style = document.createElement("link");
style.rel = "stylesheet";
style.type = "text/css";
style.href = "/admin/assets/monaco-editor/min/vs/editor/editor.main.css";
shadowRoot.appendChild(style);
const container = document.createElement("div");
container.id = "container";
container.style.width = "100%";
container.style.height = "100%";
shadowRoot.appendChild(container);
}
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) {
if (oldValue === newValue) return;
if (name === "readonly") {
this.editor?.updateOptions({ readOnly: newValue !== null });
} else if (name === "language") {
const model = this.editor?.getModel();
if (model) monaco.editor.setModelLanguage(model, newValue ?? "plaintext");
}
}
connectedCallback() {
const input = this.querySelector("input");
if (!input) throw new Error("monaco-editor requires an <input> child");
this.input = input;
const container = this.shadowRoot!.getElementById("container")!;
this.editor = monaco.editor.create(container, {
value: this.input.value ?? "",
language: this.getAttribute("language") ?? "plaintext",
readOnly: this.hasAttribute("readonly"),
wordWrap: "on",
minimap: { enabled: false },
automaticLayout: true,
dropIntoEditor: {
enabled: false
},
});
this.editor.onDidChangeModelContent(() => {
const val = this.editor!.getValue();
this.input!.value = val;
this.input!.dispatchEvent(new Event("input", { bubbles: true }));
this.input!.dispatchEvent(new Event("change", { bubbles: true }));
});
this.editor.onDidBlurEditorWidget(() => {
this.input!.dispatchEvent(new Event("blur", { bubbles: true }));
});
this.addEventListener("type", (event: CustomEvent) => {
this.editor!.trigger("keyboard", "type", { text: event.detail });
});
}
disconnectedCallback() {
this.editor?.dispose();
}
get value(): string {
return this.editor?.getValue() ?? this.input?.value ?? "";
}
set value(newValue: string) {
if (this.editor && this.editor.getValue() !== newValue) {
this.editor.setValue(newValue);
}
if (this.input) this.input.value = newValue;
}
}
customElements.define("monaco-editor", MonacoEditorElement);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment