<script lang="ts"> import type monaco from 'monaco-editor'; import { onDestroy, onMount } from 'svelte'; import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'; // import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'; // import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'; // import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'; import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'; // @ts-ignore import wrqTypes from '@types/webextension-polyfill/namespaces/webRequest.d.ts?raw'; // @ts-ignore import evTypes from '@types/webextension-polyfill/namespaces/events.d.ts?raw'; import userland from './userland.d.ts?raw'; let divEl: HTMLDivElement | null = $state(null); let editor: monaco.editor.IStandaloneCodeEditor = $state(null as any); let Monaco: typeof monaco; let { defaultValue, typeDefs = `import type { UserlandBrowser } from './userland'; declare global { /** * The subset of the host extension's browser type available to the extension * Note: We don't properly sandbox anything. You can likely easily get access to shit outside of here from the browser global. */ declare const browser: UserlandBrowser; } `, value = $bindable(''), }: { value?: string; defaultValue: string; typeDefs?: string; } = $props(); let writeDebounce = false; onMount(async () => { // @ts-ignore globalThis.MonacoEnvironment = { getWorker: async (_moduleId: any, label: string) => { if (label === 'json') { return new ( await import( 'monaco-editor/esm/vs/language/json/json.worker?worker' ) ).default(); } // if (label === 'css' || label === 'scss' || label === 'less') { // return new cssWorker(); // } // if (label === 'html' || label === 'handlebars' || label === 'razor') { // return new htmlWorker(); // } if (label === 'typescript' || label === 'javascript') { return new tsWorker(); } return new editorWorker(); }, }; Monaco = await import('monaco-editor'); Monaco.languages.typescript.typescriptDefaults.setCompilerOptions({ allowNonTsExtensions: true, moduleResolution: Monaco.languages.typescript.ModuleResolutionKind.NodeJs, module: Monaco.languages.typescript.ModuleKind.ESNext, noEmit: true, typeRoots: ['node_modules/@types'], }); Monaco.editor.defineTheme('redirext', { base: 'vs-dark', inherit: true, rules: [], colors: { 'editor.foreground': '#dedede', 'editor.background': '#23222b', 'editor.selectionBackground': '#4c2889', 'editor.inactiveSelectionBackground': '#444d56', 'editor.lineHighlightBackground': '#444d56', 'editorCursor.foreground': '#ffffff', 'editorWhitespace.foreground': '#6a737d', 'editorIndentGuide.background': '#6a737d', 'editorIndentGuide.activeBackground': '#f6f8fa', 'editor.selectionHighlightBorder': '#444d56', }, }); if (!divEl) while (!divEl) await new Promise((rs) => setTimeout(rs, 100)); for (const [_filename, contents] of Object.entries( (await import('./filemap.js')).default )) { Monaco.languages.typescript.typescriptDefaults.addExtraLib( contents // ,_filename ); } Monaco.languages.typescript.typescriptDefaults.addExtraLib( evTypes, 'node_modules/@types/webextension-polyfill/namespaces/events.d.ts' ); Monaco.languages.typescript.typescriptDefaults.addExtraLib( wrqTypes, 'node_modules/@types/webextension-polyfill/namespaces/webRequest.d.ts' ); Monaco.languages.typescript.typescriptDefaults.addExtraLib( `export * from './namespaces/webRequest';`, 'node_modules/@types/webextension-polyfill/index.d.ts' ); Monaco.languages.typescript.typescriptDefaults.addExtraLib( userland, 'node_modules/@types/redirext/userland.d.ts' ); Monaco.languages.typescript.typescriptDefaults.addExtraLib( typeDefs, 'node_modules/@types/redirext/index.d.ts' ); editor = Monaco.editor.create(divEl, { value: defaultValue, language: 'typescript', theme: 'redirext', autoDetectHighContrast: false, }); editor.addCommand(Monaco.KeyMod.Alt | Monaco.KeyCode.Backslash, () => { const win = window.open(location.href); if (win) { const el = document.createElement('div'); el.setAttribute( 'style', 'display:flex;width:100vw;height:100vh;position:fixed;left:0;top:0;align-items:center;justify-content:center;text-align:center;z-index:9999;background:#23222B;' ); el.textContent = 'Popped out'; document.body.appendChild(el); win.addEventListener('close', () => { el.textContent = 'Popping In...'; location.reload(); }); } }); editor.getModel()?.onDidChangeContent((e) => { writeDebounce = true; const upd = () => { try { value = editor.getValue({ lineEnding: '\n', preserveBOM: false }).trim() + '\n'; // console.debug('Updated value from editor to:', value); } catch (error) { writeDebounce = false; throw error; } setTimeout(() => { writeDebounce = false; }, 0); }; setTimeout(upd, 0); }); }); $effect(() => { if (editor && !writeDebounce) { // console.debug('Updating editor with value', value); editor.setValue(value.trim() + '\n'); } }); onDestroy(() => { if (editor) editor.dispose(); }); </script> <div bind:this={divEl} class="h-screen overflow-hidden bg-card box-border max-h-full" ></div>