diff options
Diffstat (limited to 'src/routes')
-rw-r--r-- | src/routes/+layout.svelte | 20 | ||||
-rw-r--r-- | src/routes/+layout.ts | 2 | ||||
-rw-r--r-- | src/routes/+page.svelte | 42 | ||||
-rw-r--r-- | src/routes/Monaco.svelte | 144 | ||||
-rw-r--r-- | src/routes/browser.d.ts | 4 | ||||
-rw-r--r-- | src/routes/userland.d.ts | 4 |
6 files changed, 216 insertions, 0 deletions
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte new file mode 100644 index 0000000..b0e82a6 --- /dev/null +++ b/src/routes/+layout.svelte @@ -0,0 +1,20 @@ +<script lang="ts"> + import { onDestroy, onMount } from 'svelte'; + import '../app.css'; + let { children } = $props(); + + let mounted = $state(false); + onMount(async () => { + (globalThis as any).browser = ( + await import('webextension-polyfill') + ).default; + mounted = true; + }); + onDestroy(() => (mounted = false)); +</script> + +{#if mounted} + {@render children()} +{:else} + <p>Waiting on Mount</p> +{/if} diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts new file mode 100644 index 0000000..748cfd9 --- /dev/null +++ b/src/routes/+layout.ts @@ -0,0 +1,2 @@ +export const prerender = true; +export const csr = true; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte new file mode 100644 index 0000000..7320c2a --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,42 @@ +<script lang="ts"> + import { onMount } from 'svelte'; + import Monaco from './Monaco.svelte'; + let content_script = $state(''); + let default_value = $state(`// Do not put untrusted content here! +// See https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Intercept_HTTP_requests and https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest for a tutorial on how to handle things. +// Content saves on every keystroke due to laziness. +// Reload (disable & re-enable) extension or restart browser to apply changes. +`); + let mounted = $state(false); + onMount(async () => { + const s = (await browser.storage.local.get('sc')).sc; + default_value = s && `${s}`.length ? `${s}` : default_value; + content_script = default_value ?? ''; + setTimeout(() => { + mounted = true; + setTimeout(() => { + content_script = default_value; + }, 0); + }, 0); + }); + $effect(() => { + (async (cs: string) => { + if (mounted) { + await browser.storage.local.set({ sc: cs }); + if (browser.runtime.lastError) { + console.warn('Last Error is ', browser.runtime.lastError.message); + browser.runtime.lastError = undefined; + } + } + })(content_script); + }); +</script> + +{#if mounted} + <Monaco + defaultValue={default_value} + bind:value={content_script} + /> +{:else} + Getting Storage +{/if} 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> diff --git a/src/routes/browser.d.ts b/src/routes/browser.d.ts new file mode 100644 index 0000000..ed54792 --- /dev/null +++ b/src/routes/browser.d.ts @@ -0,0 +1,4 @@ +import Browser from 'webextension-polyfill'; +declare global { + declare const browser: typeof Browser; +} diff --git a/src/routes/userland.d.ts b/src/routes/userland.d.ts new file mode 100644 index 0000000..7b659b8 --- /dev/null +++ b/src/routes/userland.d.ts @@ -0,0 +1,4 @@ +import type { WebRequest } from 'webextension-polyfill'; +export type UserlandBrowser = { + get webRequest(): WebRequest.Static; +}; |