diff options
| author | netop://ウィビ <paul@webb.page> | 2026-04-24 11:33:25 -0700 |
|---|---|---|
| committer | netop://ウィビ <paul@webb.page> | 2026-04-24 11:33:25 -0700 |
| commit | 8a59f92d031963e23ecc84b75feecf43eb4dd146 (patch) | |
| tree | 75de5768885583897061a3b1795e4c987ce90039 /source/library/components/Editor.svelte | |
| download | graphiql-8a59f92d031963e23ecc84b75feecf43eb4dd146.tar.gz graphiql-8a59f92d031963e23ecc84b75feecf43eb4dd146.zip | |
Initial commit: @eol/graphiql v0.3
Svelte 5 GraphiQL alternative for JSR. Covers:
- HTTP fetcher with injectable fetch; SSE/WS stubs
- Session store with tabs, auto-titling, persistence, rename
- Operation detection via graphql parse(); Toolbar picker
- CodeMirror 6 editor via cm6-graphql with theme prop
- Light theme preset (hand-rolled EditorView.theme)
- Doc explorer with breadcrumb nav and type guards
- History panel with 100-entry cap, favorite pinning
- Deno tests for operations, storage, and history eviction
Diffstat (limited to 'source/library/components/Editor.svelte')
| -rw-r--r-- | source/library/components/Editor.svelte | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/source/library/components/Editor.svelte b/source/library/components/Editor.svelte new file mode 100644 index 0000000..f2bf82d --- /dev/null +++ b/source/library/components/Editor.svelte @@ -0,0 +1,136 @@ +<script lang="ts"> + /*** IMPORT ------------------------------------------- ***/ + + import { browser } from "$app/environment"; + import { onMount } from "svelte"; + import type { Extension } from "@codemirror/state"; + import type { EditorView } from "@codemirror/view"; + import type { GraphQLSchema } from "graphql"; + + /*** UTILITY ------------------------------------------ ***/ + + type Props = { + language?: "graphql" | "json"; + onChange: (value: string) => void; + readOnly?: boolean; + schema?: string; + theme?: Extension; + value: string; + }; + + let { + language = "graphql", + onChange, + readOnly = false, + schema, + theme, + value + }: Props = $props(); + + let buildSchemaFn = $state<((sdl: string) => GraphQLSchema) | null>(null); + let container: HTMLDivElement; + let updateSchemaFn = $state<((v: EditorView, s: GraphQLSchema) => void) | null>(null); + let view = $state<EditorView | null>(null); + + onMount(() => { + if (!browser) + return; + + let disposed = false; + + (async () => { + const [ + { EditorView: EV, keymap, lineNumbers, highlightActiveLine }, + { EditorState }, + { defaultKeymap, history, historyKeymap }, + { syntaxHighlighting, defaultHighlightStyle, bracketMatching, indentOnInput }, + { closeBrackets, closeBracketsKeymap }, + { graphql, updateSchema }, + { json }, + { buildSchema } + ] = await Promise.all([ + import("@codemirror/view"), + import("@codemirror/state"), + import("@codemirror/commands"), + import("@codemirror/language"), + import("@codemirror/autocomplete"), + import("cm6-graphql"), + import("@codemirror/lang-json"), + import("graphql") + ]); + + if (disposed) + return; + + const themeExt: Extension = theme ?? (await import("@codemirror/theme-one-dark")).oneDark; + + const languageExt = language === "graphql" ? + graphql(schema ? buildSchema(schema) : undefined) : + json(); + + const instance = new EV({ + parent: container, + state: EditorState.create({ + doc: value, + extensions: [ + lineNumbers(), + highlightActiveLine(), + history(), + bracketMatching(), + closeBrackets(), + indentOnInput(), + syntaxHighlighting(defaultHighlightStyle, { fallback: true }), + keymap.of([...closeBracketsKeymap, ...defaultKeymap, ...historyKeymap]), + languageExt, + themeExt, + EV.editable.of(!readOnly), + EV.updateListener.of((u) => { + if (u.docChanged) + onChange(u.state.doc.toString()); + }) + ] + }) + }); + + view = instance; + updateSchemaFn = updateSchema; + buildSchemaFn = buildSchema; + })(); + + return () => { + disposed = true; + view?.destroy(); + }; + }); + + $effect(() => { + if (!view || !updateSchemaFn || !buildSchemaFn) + return; + + if (language !== "graphql" || !schema) + return; + + try { + updateSchemaFn(view, buildSchemaFn(schema)); + } catch { + // Invalid SDL — silently skip; editor keeps working without schema awareness + } + }); +</script> + +<style lang="scss"> + .editor { + height: 100%; + width: 100%; + + :global(.cm-editor) { + height: 100%; + } + + :global(.cm-scroller) { + font-family: inherit; + } + } +</style> + +<div bind:this={container} class="editor"></div> |