generated from nhcarrigan/template
feat: add built-in file editor with syntax highlighting #79
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import { EditorView, basicSetup } from "codemirror";
|
||||
import { EditorState } from "@codemirror/state";
|
||||
import { EditorState, Compartment } from "@codemirror/state";
|
||||
import { keymap } from "@codemirror/view";
|
||||
import { oneDark } from "@codemirror/theme-one-dark";
|
||||
import { javascript } from "@codemirror/lang-javascript";
|
||||
@@ -32,7 +32,10 @@
|
||||
import { toml } from "@codemirror/legacy-modes/mode/toml";
|
||||
import { dockerFile } from "@codemirror/legacy-modes/mode/dockerfile";
|
||||
import { powerShell } from "@codemirror/legacy-modes/mode/powershell";
|
||||
import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
|
||||
import { tags } from "@lezer/highlight";
|
||||
import { editorStore } from "$lib/stores/editor";
|
||||
import { configStore } from "$lib/stores/config";
|
||||
import type { EditorTab } from "$lib/types/editor";
|
||||
import type { Extension } from "@codemirror/state";
|
||||
|
||||
@@ -40,6 +43,274 @@
|
||||
|
||||
let editorContainer: HTMLDivElement;
|
||||
let view: EditorView | null = null;
|
||||
let themeCompartment = new Compartment();
|
||||
|
||||
// Subscribe to theme changes
|
||||
const config = configStore.config;
|
||||
|
||||
// Light theme
|
||||
const lightTheme = EditorView.theme(
|
||||
{
|
||||
"&": {
|
||||
backgroundColor: "#ffffff",
|
||||
color: "#24292e",
|
||||
},
|
||||
".cm-content": {
|
||||
caretColor: "#24292e",
|
||||
},
|
||||
".cm-cursor, .cm-dropCursor": {
|
||||
borderLeftColor: "#24292e",
|
||||
},
|
||||
"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": {
|
||||
backgroundColor: "#c8d3f5",
|
||||
},
|
||||
".cm-panels": {
|
||||
backgroundColor: "#f6f8fa",
|
||||
color: "#24292e",
|
||||
},
|
||||
".cm-panels.cm-panels-top": {
|
||||
borderBottom: "1px solid #e1e4e8",
|
||||
},
|
||||
".cm-panels.cm-panels-bottom": {
|
||||
borderTop: "1px solid #e1e4e8",
|
||||
},
|
||||
".cm-searchMatch": {
|
||||
backgroundColor: "#ffdf5d",
|
||||
outline: "1px solid #c4a000",
|
||||
},
|
||||
".cm-searchMatch.cm-searchMatch-selected": {
|
||||
backgroundColor: "#c4a000",
|
||||
},
|
||||
".cm-activeLine": {
|
||||
backgroundColor: "#f6f8fa",
|
||||
},
|
||||
".cm-selectionMatch": {
|
||||
backgroundColor: "#c8d3f5",
|
||||
},
|
||||
".cm-matchingBracket, .cm-nonmatchingBracket": {
|
||||
backgroundColor: "#c8d3f5",
|
||||
outline: "1px solid #888",
|
||||
},
|
||||
".cm-gutters": {
|
||||
backgroundColor: "#f6f8fa",
|
||||
color: "#6a737d",
|
||||
border: "none",
|
||||
borderRight: "1px solid #e1e4e8",
|
||||
},
|
||||
".cm-activeLineGutter": {
|
||||
backgroundColor: "#e1e4e8",
|
||||
},
|
||||
".cm-foldPlaceholder": {
|
||||
backgroundColor: "transparent",
|
||||
border: "none",
|
||||
color: "#6a737d",
|
||||
},
|
||||
".cm-tooltip": {
|
||||
border: "1px solid #e1e4e8",
|
||||
backgroundColor: "#ffffff",
|
||||
},
|
||||
".cm-tooltip .cm-tooltip-arrow:before": {
|
||||
borderTopColor: "transparent",
|
||||
borderBottomColor: "transparent",
|
||||
},
|
||||
".cm-tooltip .cm-tooltip-arrow:after": {
|
||||
borderTopColor: "#ffffff",
|
||||
borderBottomColor: "#ffffff",
|
||||
},
|
||||
".cm-tooltip-autocomplete": {
|
||||
"& > ul > li[aria-selected]": {
|
||||
backgroundColor: "#e1e4e8",
|
||||
color: "#24292e",
|
||||
},
|
||||
},
|
||||
},
|
||||
{ dark: false }
|
||||
);
|
||||
|
||||
const lightHighlightStyle = HighlightStyle.define([
|
||||
{ tag: tags.keyword, color: "#d73a49" },
|
||||
{
|
||||
tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName],
|
||||
color: "#6f42c1",
|
||||
},
|
||||
{ tag: [tags.function(tags.variableName), tags.labelName], color: "#6f42c1" },
|
||||
{ tag: [tags.color, tags.constant(tags.name), tags.standard(tags.name)], color: "#005cc5" },
|
||||
{ tag: [tags.definition(tags.name), tags.separator], color: "#24292e" },
|
||||
{
|
||||
tag: [
|
||||
tags.typeName,
|
||||
tags.className,
|
||||
tags.number,
|
||||
tags.changed,
|
||||
tags.annotation,
|
||||
tags.modifier,
|
||||
tags.self,
|
||||
tags.namespace,
|
||||
],
|
||||
color: "#e36209",
|
||||
},
|
||||
{
|
||||
tag: [
|
||||
tags.operator,
|
||||
tags.operatorKeyword,
|
||||
tags.url,
|
||||
tags.escape,
|
||||
tags.regexp,
|
||||
tags.link,
|
||||
tags.special(tags.string),
|
||||
],
|
||||
color: "#032f62",
|
||||
},
|
||||
{ tag: [tags.meta, tags.comment], color: "#6a737d" },
|
||||
{ tag: tags.strong, fontWeight: "bold" },
|
||||
{ tag: tags.emphasis, fontStyle: "italic" },
|
||||
{ tag: tags.strikethrough, textDecoration: "line-through" },
|
||||
{ tag: tags.link, color: "#032f62", textDecoration: "underline" },
|
||||
{ tag: tags.heading, fontWeight: "bold", color: "#005cc5" },
|
||||
{ tag: [tags.atom, tags.bool, tags.special(tags.variableName)], color: "#005cc5" },
|
||||
{ tag: [tags.processingInstruction, tags.string, tags.inserted], color: "#22863a" },
|
||||
{ tag: tags.invalid, color: "#cb2431" },
|
||||
]);
|
||||
|
||||
// High contrast theme
|
||||
const highContrastTheme = EditorView.theme(
|
||||
{
|
||||
"&": {
|
||||
backgroundColor: "#000000",
|
||||
color: "#ffffff",
|
||||
},
|
||||
".cm-content": {
|
||||
caretColor: "#ffffff",
|
||||
},
|
||||
".cm-cursor, .cm-dropCursor": {
|
||||
borderLeftColor: "#ffffff",
|
||||
borderLeftWidth: "2px",
|
||||
},
|
||||
"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": {
|
||||
backgroundColor: "#264f78",
|
||||
},
|
||||
".cm-panels": {
|
||||
backgroundColor: "#000000",
|
||||
color: "#ffffff",
|
||||
},
|
||||
".cm-panels.cm-panels-top": {
|
||||
borderBottom: "2px solid #ffffff",
|
||||
},
|
||||
".cm-panels.cm-panels-bottom": {
|
||||
borderTop: "2px solid #ffffff",
|
||||
},
|
||||
".cm-searchMatch": {
|
||||
backgroundColor: "#515c6a",
|
||||
outline: "2px solid #ffff00",
|
||||
},
|
||||
".cm-searchMatch.cm-searchMatch-selected": {
|
||||
backgroundColor: "#ffff00",
|
||||
color: "#000000",
|
||||
},
|
||||
".cm-activeLine": {
|
||||
backgroundColor: "#1a1a1a",
|
||||
},
|
||||
".cm-selectionMatch": {
|
||||
backgroundColor: "#264f78",
|
||||
},
|
||||
".cm-matchingBracket, .cm-nonmatchingBracket": {
|
||||
backgroundColor: "#515c6a",
|
||||
outline: "2px solid #ffff00",
|
||||
},
|
||||
".cm-gutters": {
|
||||
backgroundColor: "#000000",
|
||||
color: "#858585",
|
||||
border: "none",
|
||||
borderRight: "2px solid #ffffff",
|
||||
},
|
||||
".cm-activeLineGutter": {
|
||||
backgroundColor: "#1a1a1a",
|
||||
color: "#ffffff",
|
||||
},
|
||||
".cm-foldPlaceholder": {
|
||||
backgroundColor: "transparent",
|
||||
border: "none",
|
||||
color: "#ffff00",
|
||||
},
|
||||
".cm-tooltip": {
|
||||
border: "2px solid #ffffff",
|
||||
backgroundColor: "#000000",
|
||||
},
|
||||
".cm-tooltip .cm-tooltip-arrow:before": {
|
||||
borderTopColor: "transparent",
|
||||
borderBottomColor: "transparent",
|
||||
},
|
||||
".cm-tooltip .cm-tooltip-arrow:after": {
|
||||
borderTopColor: "#000000",
|
||||
borderBottomColor: "#000000",
|
||||
},
|
||||
".cm-tooltip-autocomplete": {
|
||||
"& > ul > li[aria-selected]": {
|
||||
backgroundColor: "#264f78",
|
||||
color: "#ffffff",
|
||||
},
|
||||
},
|
||||
},
|
||||
{ dark: true }
|
||||
);
|
||||
|
||||
const highContrastHighlightStyle = HighlightStyle.define([
|
||||
{ tag: tags.keyword, color: "#569cd6", fontWeight: "bold" },
|
||||
{
|
||||
tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName],
|
||||
color: "#9cdcfe",
|
||||
},
|
||||
{ tag: [tags.function(tags.variableName), tags.labelName], color: "#dcdcaa" },
|
||||
{ tag: [tags.color, tags.constant(tags.name), tags.standard(tags.name)], color: "#4fc1ff" },
|
||||
{ tag: [tags.definition(tags.name), tags.separator], color: "#ffffff" },
|
||||
{
|
||||
tag: [
|
||||
tags.typeName,
|
||||
tags.className,
|
||||
tags.number,
|
||||
tags.changed,
|
||||
tags.annotation,
|
||||
tags.modifier,
|
||||
tags.self,
|
||||
tags.namespace,
|
||||
],
|
||||
color: "#4ec9b0",
|
||||
},
|
||||
{
|
||||
tag: [
|
||||
tags.operator,
|
||||
tags.operatorKeyword,
|
||||
tags.url,
|
||||
tags.escape,
|
||||
tags.regexp,
|
||||
tags.link,
|
||||
tags.special(tags.string),
|
||||
],
|
||||
color: "#d4d4d4",
|
||||
},
|
||||
{ tag: [tags.meta, tags.comment], color: "#6a9955" },
|
||||
{ tag: tags.strong, fontWeight: "bold" },
|
||||
{ tag: tags.emphasis, fontStyle: "italic" },
|
||||
{ tag: tags.strikethrough, textDecoration: "line-through" },
|
||||
{ tag: tags.link, color: "#3794ff", textDecoration: "underline" },
|
||||
{ tag: tags.heading, fontWeight: "bold", color: "#569cd6" },
|
||||
{ tag: [tags.atom, tags.bool, tags.special(tags.variableName)], color: "#569cd6" },
|
||||
{ tag: [tags.processingInstruction, tags.string, tags.inserted], color: "#ce9178" },
|
||||
{ tag: tags.invalid, color: "#f44747" },
|
||||
]);
|
||||
|
||||
function getThemeExtension(theme: string): Extension {
|
||||
switch (theme) {
|
||||
case "light":
|
||||
return [lightTheme, syntaxHighlighting(lightHighlightStyle)];
|
||||
case "high-contrast":
|
||||
return [highContrastTheme, syntaxHighlighting(highContrastHighlightStyle)];
|
||||
case "dark":
|
||||
case "custom":
|
||||
default:
|
||||
return oneDark;
|
||||
}
|
||||
}
|
||||
|
||||
function getLanguageExtension(language: string): Extension {
|
||||
const languageMap: Record<string, () => Extension> = {
|
||||
@@ -84,6 +355,8 @@
|
||||
function createEditor() {
|
||||
if (!editorContainer) return;
|
||||
|
||||
const currentTheme = $config.theme;
|
||||
|
||||
const saveKeymap = keymap.of([
|
||||
{
|
||||
key: "Mod-s",
|
||||
@@ -105,7 +378,7 @@
|
||||
doc: tab.content,
|
||||
extensions: [
|
||||
basicSetup,
|
||||
oneDark,
|
||||
themeCompartment.of(getThemeExtension(currentTheme)),
|
||||
getLanguageExtension(tab.language),
|
||||
saveKeymap,
|
||||
updateListener,
|
||||
@@ -120,10 +393,6 @@
|
||||
".cm-content": {
|
||||
fontFamily: "'JetBrains Mono', 'Fira Code', 'Consolas', monospace",
|
||||
},
|
||||
".cm-gutters": {
|
||||
backgroundColor: "var(--bg-secondary)",
|
||||
borderRight: "1px solid var(--border-color)",
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -141,6 +410,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Watch for theme changes and update the editor
|
||||
$: if (view && $config.theme) {
|
||||
view.dispatch({
|
||||
effects: themeCompartment.reconfigure(getThemeExtension($config.theme)),
|
||||
});
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
createEditor();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user