aboutsummaryrefslogtreecommitdiffstats
path: root/src/routes/Monaco.svelte
diff options
context:
space:
mode:
Diffstat (limited to 'src/routes/Monaco.svelte')
-rw-r--r--src/routes/Monaco.svelte144
1 files changed, 144 insertions, 0 deletions
diff --git a/src/routes/Monaco.svelte b/src/routes/Monaco.svelte
new file mode 100644
index 0000000..efdd67a
--- /dev/null
+++ b/src/routes/Monaco.svelte
@@ -0,0 +1,144 @@
+<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 = `processRequest = (rq) => rq;
+processResponse = (rs) => rs;
+`,
+ 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: function (_moduleId: any, label: string) {
+ if (label === 'json') {
+ return new jsonWorker();
+ }
+ 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));
+ 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.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>