Upgrade codemirror to v6 (#18524)
Co-authored-by: fisker <lionkay@gmail.com>
This commit is contained in:
1 parent
1ce53af15b
commit
b261f60dcf
12 files changed
+1598
-1224
No files matched your search
@@ -74,13 +74,17 @@
|
|||||||
groupName: "GitHub Actions",
|
groupName: "GitHub Actions",
|
||||||
matchManagers: ["github-actions"],
|
matchManagers: ["github-actions"],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
groupName: "Markdown dependencies",
|
||||||
|
matchPackageNames: ["micromark-*", "mdast-", "remark-", "unified"],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
groupName: "Docusaurus",
|
groupName: "Docusaurus",
|
||||||
matchPackageNames: ["@docusaurus/*"],
|
matchPackageNames: ["@docusaurus/*"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
groupName: "Markdown dependencies",
|
groupName: "CodeMirror",
|
||||||
matchPackageNames: ["micromark-*", "mdast-", "remark-", "unified"],
|
matchPackageNames: ["@codemirror/*", "cm6-graphql"],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
ignoreDeps: [
|
ignoreDeps: [
|
||||||
@@ -90,6 +94,8 @@
|
|||||||
"prettier",
|
"prettier",
|
||||||
// Old versions are required to test
|
// Old versions are required to test
|
||||||
"node",
|
"node",
|
||||||
|
// Only pinned version works, https://github.com/prettier/prettier/pull/18524#issuecomment-3694806317
|
||||||
|
"@codemirror/view",
|
||||||
],
|
],
|
||||||
ignorePaths: ["tests/**", "packages/**"],
|
ignorePaths: ["tests/**", "packages/**"],
|
||||||
}
|
}
|
||||||
@@ -40,6 +40,7 @@
|
|||||||
"Burgener",
|
"Burgener",
|
||||||
"callees",
|
"callees",
|
||||||
"camelified",
|
"camelified",
|
||||||
|
"catppuccin",
|
||||||
"Chedeau",
|
"Chedeau",
|
||||||
"cherow",
|
"cherow",
|
||||||
"Cheung",
|
"Cheung",
|
||||||
@@ -142,10 +143,12 @@
|
|||||||
"kddnewton",
|
"kddnewton",
|
||||||
"Kearney",
|
"Kearney",
|
||||||
"Kempson",
|
"Kempson",
|
||||||
|
"Keymap",
|
||||||
"Khatri",
|
"Khatri",
|
||||||
"Konstantin",
|
"Konstantin",
|
||||||
"l’objectif",
|
"l’objectif",
|
||||||
"layed",
|
"layed",
|
||||||
|
"lezer",
|
||||||
"libdef",
|
"libdef",
|
||||||
"lintstaged",
|
"lintstaged",
|
||||||
"lintstagedrc",
|
"lintstagedrc",
|
||||||
|
|||||||
@@ -26,124 +26,6 @@
|
|||||||
<link rel="icon" href="/icon.png" />
|
<link rel="icon" href="/icon.png" />
|
||||||
<link rel="apple-touch-icon" href="/icon.png" />
|
<link rel="apple-touch-icon" href="/icon.png" />
|
||||||
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/codemirror.min.css"
|
|
||||||
integrity="sha512-uf06llspW44/LZpHzHT6qBOIVODjWtv4MxCricRxkzvopAlSWnTf6hpZTFxuuZcuNE9CBQhqE0Seu1CoRk84nQ=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="stylesheet"
|
|
||||||
href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/addon/fold/foldgutter.min.css"
|
|
||||||
integrity="sha512-YwkMTlTHn8dBnwa47IF+cKsS00HPiiVhQ4DpwT1KF2gUftfFR7aefepabSPLAs6zrMyD89M3w0Ow6mQ5XJEUCw=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/codemirror.min.js"
|
|
||||||
integrity="sha512-6cPYokihlrofMNApz7OXVQNObWjLiKGIBBb7+UB+AuMiRCLKmFKgrwms21sHq3bdFFZWpfHYRJBJvMFMPj1S9g=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/mode/javascript/javascript.min.js"
|
|
||||||
integrity="sha512-Cbz+kvn+l5pi5HfXsEB/FYgZVKjGIhOgYNBwj4W2IHP2y8r3AdyDCQRnEUqIQ+6aJjygKPTyaNT2eIihaykJlw=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/mode/xml/xml.min.js"
|
|
||||||
integrity="sha512-LarNmzVokUmcA7aUDtqZ6oTS+YXmUKzpGdm8DxC46A6AHu+PQiYCUlwEGWidjVYMo/QXZMFMIadZtrkfApYp/g=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/mode/jsx/jsx.min.js"
|
|
||||||
integrity="sha512-jlNQWdDsv1W4EG4blPl9HPp+5AXT/uozlC1HWLydcdG9gT8qM2PtWbk5sCZb8eZTX+lT/oHlXKn1SU6LXogFow=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/mode/css/css.js"
|
|
||||||
integrity="sha512-kbf0gMc1+KPTTDWkjkGwf3h2Nx4djnWBVtcCKJcwLQpQ/TIMAjRBKM9f0ZDgj9kT5WTs1Qzsq+5Si2p7j9whkg=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/mode/markdown/markdown.min.js"
|
|
||||||
integrity="sha512-DmMao0nRIbyDjbaHc8fNd3kxGsZj9PCU6Iu/CeidLQT9Py8nYVA5n0PqXYmvqNdU+lCiTHOM/4E7bM/G8BttJg=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/addon/display/placeholder.min.js"
|
|
||||||
integrity="sha512-acBo6sW2h2GZQ9BqU9v5RyYGPUEr1a9jrukJg825Y0ahxAg/7aqTNPtcalloqnf4DfsRVdcdNmcBNWPD8b8W8Q=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/addon/display/rulers.min.js"
|
|
||||||
integrity="sha512-sF+AF3zpVOHMt4f4mN1dVS2/Hon6pr/7h1gc7rDHtga0SeD9z0UiL1J0k3umDFEz5qkY06ARcNzi45NkJ9GsYw=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/addon/search/searchcursor.min.js"
|
|
||||||
integrity="sha512-+ZfZDC9gi1y9Xoxi9UUsSp+5k+AcFE0TRNjI0pfaAHQ7VZTaaoEpBZp9q9OvHdSomOze/7s5w27rcsYpT6xU6g=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/addon/edit/matchbrackets.min.js"
|
|
||||||
integrity="sha512-GSYCbN/le5gNmfAWVEjg1tKnOH7ilK6xCLgA7c48IReoIR2g2vldxTM6kZlN6o3VtWIe6fHu/qhwxIt11J8EBA=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/addon/edit/closebrackets.min.js"
|
|
||||||
integrity="sha512-tsjcYO5hFvViRssxiM7Jhd8601epWOx1He3Hl4yuI5dKKPxr43KxkOhc9GZeeqzlYJm9ABb7UPA9697NiqZZ7Q=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/addon/comment/comment.min.js"
|
|
||||||
integrity="sha512-UaJ8Lcadz5cc5mkWmdU8cJr0wMn7d8AZX5A24IqVGUd1MZzPJTq9dLLW6I102iljTcdB39YvVcCgBhM0raGAZQ=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/addon/wrap/hardwrap.min.js"
|
|
||||||
integrity="sha512-ZPPL1o5pyZOIF/+PL/pRRBdxrYENhEbcNas/6PJcXMM1aBu5wI2yeFzJY6Jv+fJjH8o48HBYomInIYsYvFlXig=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/addon/fold/foldcode.min.js"
|
|
||||||
integrity="sha512-Q2qfEJEU257Qlqc4/5g6iKuJNnn5L0xu2D48p8WHe9YC/kLj2UfkdGD01qfxWk+XIcHsZngcA8WuKcizF8MAHA=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/addon/fold/foldgutter.js"
|
|
||||||
integrity="sha512-tGjjnhNOLvNMtP2S/fpsalcaJofEksE83moo23We+afRBd1kx3JtSSDn9CDmEyKhwk4N7dJqLq3iUWyVOs9ejQ=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/addon/fold/brace-fold.min.js"
|
|
||||||
integrity="sha512-5MuaB1PVXvhsYVG0Ozb0bwauN7/D1VU4P8dwo5E/xiB9SXY+VSEhIyxt1ggYk2xaB/RKqKL7rPXpm1o1IlTQDA=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
<script
|
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.18/keymap/sublime.min.js"
|
|
||||||
integrity="sha512-SV3qeFFtzcmGtUQPLM7HLy/7GKJ/x3c2PdiF5GZQnbHzIlI2q7r77y0IgLLbBDeHiNfCSBYDQt898Xp0tcZOeA=="
|
|
||||||
crossorigin="anonymous"
|
|
||||||
referrerpolicy="no-referrer"
|
|
||||||
></script>
|
|
||||||
|
|
||||||
<script
|
<script
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.11/clipboard.min.js"
|
src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.11/clipboard.min.js"
|
||||||
integrity="sha512-7O5pXpc0oCRrxk8RUfDYFgn0nO1t+jLuIOQdOMRp4APB7uZ4vSjspzp5y6YDtDs4VzUSTbWzBFZ/LKJhnyFOKw=="
|
integrity="sha512-7O5pXpc0oCRrxk8RUfDYFgn0nO1t+jLuIOQdOMRp4APB7uZ4vSjspzp5y6YDtDs4VzUSTbWzBFZ/LKJhnyFOKw=="
|
||||||
|
|||||||
+17
-1
@@ -15,12 +15,24 @@
|
|||||||
"update-stable-docs": "node ./scripts/update-stable-docs.mjs"
|
"update-stable-docs": "node ./scripts/update-stable-docs.mjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@catppuccin/codemirror": "1.0.1",
|
||||||
|
"@codemirror/autocomplete": "6.20.0",
|
||||||
|
"@codemirror/commands": "6.10.1",
|
||||||
|
"@codemirror/lang-css": "6.3.1",
|
||||||
|
"@codemirror/lang-javascript": "6.2.4",
|
||||||
|
"@codemirror/lang-markdown": "6.5.0",
|
||||||
|
"@codemirror/language": "6.12.1",
|
||||||
|
"@codemirror/lint": "6.9.2",
|
||||||
|
"@codemirror/state": "6.5.3",
|
||||||
|
"@codemirror/view": "6.39.4",
|
||||||
"@docusaurus/core": "3.9.2",
|
"@docusaurus/core": "3.9.2",
|
||||||
"@docusaurus/preset-classic": "3.9.2",
|
"@docusaurus/preset-classic": "3.9.2",
|
||||||
|
"@lezer/highlight": "1.2.3",
|
||||||
"@mdx-js/react": "3.1.1",
|
"@mdx-js/react": "3.1.1",
|
||||||
"@sandhose/prettier-animated-logo": "1.0.3",
|
"@sandhose/prettier-animated-logo": "1.0.3",
|
||||||
|
"@types/react": "19.2.7",
|
||||||
"clsx": "2.1.1",
|
"clsx": "2.1.1",
|
||||||
"codemirror-graphql": "2.2.4",
|
"cm6-graphql": "0.2.1",
|
||||||
"graphql": "16.12.0",
|
"graphql": "16.12.0",
|
||||||
"prism-react-renderer": "2.4.1",
|
"prism-react-renderer": "2.4.1",
|
||||||
"react": "19.2.1",
|
"react": "19.2.1",
|
||||||
@@ -36,9 +48,13 @@
|
|||||||
"@vitejs/plugin-vue": "6.0.2",
|
"@vitejs/plugin-vue": "6.0.2",
|
||||||
"@vitejs/plugin-vue-jsx": "5.1.2",
|
"@vitejs/plugin-vue-jsx": "5.1.2",
|
||||||
"concurrently": "9.2.1",
|
"concurrently": "9.2.1",
|
||||||
|
"rollup-plugin-visualizer": "6.0.5",
|
||||||
"vite": "7.2.7",
|
"vite": "7.2.7",
|
||||||
"yaml": "2.8.2"
|
"yaml": "2.8.2"
|
||||||
},
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"@codemirror/view": "6.39.4"
|
||||||
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
">0.5%",
|
">0.5%",
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ import SidebarOptions from "./sidebar/SidebarOptions.jsx";
|
|||||||
import * as urlHash from "./urlHash.js";
|
import * as urlHash from "./urlHash.js";
|
||||||
import {
|
import {
|
||||||
buildCliArgs,
|
buildCliArgs,
|
||||||
convertOffsetToSelection,
|
|
||||||
convertSelectionToRange,
|
|
||||||
getAstAutoFold,
|
getAstAutoFold,
|
||||||
getCodemirrorMode,
|
getCodemirrorMode,
|
||||||
getDefaults,
|
getDefaults,
|
||||||
@@ -114,16 +112,21 @@ function setup(props) {
|
|||||||
const setSelection = (selection) => {
|
const setSelection = (selection) => {
|
||||||
Object.assign(state, { selection });
|
Object.assign(state, { selection });
|
||||||
if (state.trackCursorOffset) {
|
if (state.trackCursorOffset) {
|
||||||
handleOptionValueChange(
|
handleOptionValueChange(cursorOffsetOption, selection?.anchor ?? -1);
|
||||||
cursorOffsetOption,
|
|
||||||
convertSelectionToRange(selection, state.content)[0],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const setSelectionAsRange = () => {
|
const setSelectionAsRange = () => {
|
||||||
const { selection, content, options } = state;
|
const { selection, options } = state;
|
||||||
const [rangeStart, rangeEnd] = convertSelectionToRange(selection, content);
|
if (!selection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const anchor = selection.anchor ?? 0;
|
||||||
|
const head = selection.head ?? anchor;
|
||||||
|
|
||||||
|
const rangeStart = Math.min(anchor, head);
|
||||||
|
const rangeEnd = Math.max(anchor, head);
|
||||||
|
|
||||||
const updatedOptions = { ...options, rangeStart, rangeEnd };
|
const updatedOptions = { ...options, rangeStart, rangeEnd };
|
||||||
if (rangeStart === rangeEnd) {
|
if (rangeStart === rangeEnd) {
|
||||||
delete updatedOptions.rangeStart;
|
delete updatedOptions.rangeStart;
|
||||||
@@ -194,7 +197,7 @@ function setup(props) {
|
|||||||
return props.worker
|
return props.worker
|
||||||
.format(content, {
|
.format(content, {
|
||||||
parser: "__js_expression",
|
parser: "__js_expression",
|
||||||
cursorOffset: convertSelectionToRange(selection, content)[0],
|
cursorOffset: selection?.anchor ?? -1,
|
||||||
})
|
})
|
||||||
.then(({ error, formatted, cursorOffset }) => {
|
.then(({ error, formatted, cursorOffset }) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -203,24 +206,34 @@ function setup(props) {
|
|||||||
|
|
||||||
Object.assign(state, {
|
Object.assign(state, {
|
||||||
content: formatted,
|
content: formatted,
|
||||||
selection: convertOffsetToSelection(cursorOffset, formatted),
|
selection:
|
||||||
|
cursorOffset >= 0
|
||||||
|
? { anchor: cursorOffset, head: cursorOffset }
|
||||||
|
: {},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const insertDummyId = () => {
|
const insertDummyId = () => {
|
||||||
const { content, selection } = state;
|
const { content, selection } = state;
|
||||||
|
if (!selection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const anchor = selection.anchor ?? 0;
|
||||||
|
const head = selection.head ?? anchor;
|
||||||
|
const start = Math.min(anchor, head);
|
||||||
|
const end = Math.max(anchor, head);
|
||||||
|
|
||||||
const dummyId = generateDummyId();
|
const dummyId = generateDummyId();
|
||||||
const range = convertSelectionToRange(selection, content);
|
|
||||||
const modifiedContent =
|
const modifiedContent =
|
||||||
content.slice(0, range[0]) + dummyId + content.slice(range[1]);
|
content.slice(0, start) + dummyId + content.slice(end);
|
||||||
|
|
||||||
Object.assign(state, {
|
Object.assign(state, {
|
||||||
content: modifiedContent,
|
content: modifiedContent,
|
||||||
selection: convertOffsetToSelection(
|
selection: {
|
||||||
range[0] + dummyId.length,
|
anchor: start + dummyId.length,
|
||||||
modifiedContent,
|
head: start + dummyId.length,
|
||||||
),
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -417,8 +430,8 @@ function setup(props) {
|
|||||||
onChange={setContent}
|
onChange={setContent}
|
||||||
onSelectionChange={setSelection}
|
onSelectionChange={setSelection}
|
||||||
extraKeys={{
|
extraKeys={{
|
||||||
"Shift-Alt-F": formatInput,
|
"Shift-Alt-f": formatInput,
|
||||||
"Ctrl-Q": insertDummyId,
|
"Ctrl-q": insertDummyId,
|
||||||
}}
|
}}
|
||||||
foldGutter={options.parser === "doc-explorer"}
|
foldGutter={options.parser === "doc-explorer"}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
const getInitialTheme = () => {
|
||||||
|
const saved = localStorage.getItem("theme");
|
||||||
|
if (saved) {
|
||||||
|
return saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||||
|
? "dark"
|
||||||
|
: "light";
|
||||||
|
};
|
||||||
|
|
||||||
|
const applyTheme = (value = getInitialTheme()) => {
|
||||||
|
document.documentElement.dataset.theme = value;
|
||||||
|
theme.value = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleTheme = () => {
|
||||||
|
const value = theme.value === "dark" ? "light" : "dark";
|
||||||
|
localStorage.setItem("theme", value);
|
||||||
|
applyTheme(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const theme = ref(undefined);
|
||||||
|
applyTheme();
|
||||||
|
|
||||||
|
function useTheme() {
|
||||||
|
return { theme, toggleTheme };
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useTheme };
|
||||||
@@ -1,42 +1,14 @@
|
|||||||
import "codemirror-graphql/cm6-legacy/mode.esm.js";
|
|
||||||
import "./install-service-worker.js";
|
import "./install-service-worker.js";
|
||||||
|
|
||||||
import { createApp, onMounted, reactive, ref, watch } from "vue";
|
import { createApp, onMounted, reactive } from "vue";
|
||||||
|
import { useTheme } from "./composables/use-theme.js";
|
||||||
import Playground from "./Playground.jsx";
|
import Playground from "./Playground.jsx";
|
||||||
import { fixPrettierVersion } from "./utilities.js";
|
import { fixPrettierVersion } from "./utilities.js";
|
||||||
import VersionLink from "./VersionLink.jsx";
|
import VersionLink from "./VersionLink.jsx";
|
||||||
import WorkerApi from "./WorkerApi.js";
|
import WorkerApi from "./WorkerApi.js";
|
||||||
|
|
||||||
function getInitialTheme() {
|
|
||||||
const saved = localStorage.getItem("theme");
|
|
||||||
if (saved) {
|
|
||||||
return saved;
|
|
||||||
}
|
|
||||||
|
|
||||||
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
||||||
? "dark"
|
|
||||||
: "light";
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyTheme(theme) {
|
|
||||||
document.documentElement.dataset.theme = theme;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ThemeToggle() {
|
function ThemeToggle() {
|
||||||
const theme = ref(getInitialTheme());
|
const { theme, toggleTheme } = useTheme();
|
||||||
|
|
||||||
const toggleTheme = () => {
|
|
||||||
const newTheme = theme.value === "dark" ? "light" : "dark";
|
|
||||||
theme.value = newTheme;
|
|
||||||
localStorage.setItem("theme", newTheme);
|
|
||||||
applyTheme(newTheme);
|
|
||||||
};
|
|
||||||
|
|
||||||
applyTheme(theme.value);
|
|
||||||
|
|
||||||
watch(theme, (newTheme) => {
|
|
||||||
applyTheme(newTheme);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
|||||||
+343
-153
@@ -1,17 +1,125 @@
|
|||||||
|
import { catppuccinLatte, catppuccinMocha } from "@catppuccin/codemirror";
|
||||||
|
import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
|
||||||
|
import {
|
||||||
|
defaultKeymap,
|
||||||
|
history,
|
||||||
|
historyKeymap,
|
||||||
|
indentWithTab,
|
||||||
|
} from "@codemirror/commands";
|
||||||
|
import {
|
||||||
|
bracketMatching,
|
||||||
|
foldable,
|
||||||
|
foldEffect,
|
||||||
|
foldGutter as foldGutterExt,
|
||||||
|
foldKeymap,
|
||||||
|
} from "@codemirror/language";
|
||||||
|
import {
|
||||||
|
Compartment,
|
||||||
|
EditorState,
|
||||||
|
StateEffect,
|
||||||
|
StateField,
|
||||||
|
} from "@codemirror/state";
|
||||||
|
import {
|
||||||
|
Decoration,
|
||||||
|
EditorView,
|
||||||
|
keymap,
|
||||||
|
lineNumbers as lineNumbersExt,
|
||||||
|
} from "@codemirror/view";
|
||||||
import { onMounted, onUnmounted, useTemplateRef, watch } from "vue";
|
import { onMounted, onUnmounted, useTemplateRef, watch } from "vue";
|
||||||
|
import { useTheme } from "./composables/use-theme.js";
|
||||||
const { CodeMirror } = window;
|
|
||||||
|
|
||||||
function setup(props, { emit }) {
|
function setup(props, { emit }) {
|
||||||
const textareaRef = useTemplateRef("textarea");
|
const editorRef = useTemplateRef("editorRef");
|
||||||
|
|
||||||
let _codeMirror = null;
|
let _codeMirror = null;
|
||||||
let _cached = "";
|
let _cached = "";
|
||||||
let _overlay = null;
|
const _overlay = StateEffect.define();
|
||||||
|
|
||||||
|
const overlayField = StateField.define({
|
||||||
|
create() {
|
||||||
|
return Decoration.none;
|
||||||
|
},
|
||||||
|
update(decorations, tr) {
|
||||||
|
for (const effect of tr.effects) {
|
||||||
|
if (effect.is(_overlay)) {
|
||||||
|
let { start, end } = effect.value;
|
||||||
|
if (start !== undefined && end !== undefined) {
|
||||||
|
if (end < start) {
|
||||||
|
[start, end] = [end, start];
|
||||||
|
}
|
||||||
|
const { length } = _codeMirror.state.doc;
|
||||||
|
start = Math.min(Math.max(start, 0), length);
|
||||||
|
end = Math.min(Math.max(end, 0), length);
|
||||||
|
if (start !== end) {
|
||||||
|
return Decoration.set([overlayMark.range(start, end)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Decoration.none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decorations.map(tr.changes);
|
||||||
|
},
|
||||||
|
provide: (f) => EditorView.decorations.from(f),
|
||||||
|
});
|
||||||
|
|
||||||
|
const overlayMark = Decoration.mark({
|
||||||
|
class: "cm-overlay-highlight",
|
||||||
|
});
|
||||||
|
|
||||||
|
const languageExt = new Compartment();
|
||||||
|
const rulerTheme = new Compartment();
|
||||||
|
const foldGutterCompartment = new Compartment();
|
||||||
|
|
||||||
|
const { theme } = useTheme();
|
||||||
|
const editorTheme = new Compartment();
|
||||||
|
|
||||||
const onChange = (value) => {
|
const onChange = (value) => {
|
||||||
emit("change", value);
|
emit("change", value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const componentDidMount = () => {
|
const createRulerTheme = (printWidth, color) => {
|
||||||
|
if (!printWidth) {
|
||||||
|
return EditorView.theme({});
|
||||||
|
}
|
||||||
|
|
||||||
|
return EditorView.theme({
|
||||||
|
".cm-content::before": {
|
||||||
|
content: '""',
|
||||||
|
position: "absolute",
|
||||||
|
top: "0",
|
||||||
|
bottom: "0",
|
||||||
|
left: `${printWidth}ch`,
|
||||||
|
width: "1px",
|
||||||
|
backgroundColor: color || "var(--color-gray-300)",
|
||||||
|
pointerEvents: "none",
|
||||||
|
zIndex: "1",
|
||||||
|
marginLeft: "6px",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getLanguageExtension(mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case "css": {
|
||||||
|
const { css } = await import("@codemirror/lang-css");
|
||||||
|
return css();
|
||||||
|
}
|
||||||
|
case "graphql": {
|
||||||
|
const { graphql } = await import("cm6-graphql");
|
||||||
|
return graphql();
|
||||||
|
}
|
||||||
|
case "markdown": {
|
||||||
|
const { markdown } = await import("@codemirror/lang-markdown");
|
||||||
|
return markdown();
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
const { javascript } = await import("@codemirror/lang-javascript");
|
||||||
|
return javascript({ jsx: true, typescript: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const componentDidMount = async () => {
|
||||||
const options = { ...props };
|
const options = { ...props };
|
||||||
for (const property of [
|
for (const property of [
|
||||||
"ruler",
|
"ruler",
|
||||||
@@ -29,123 +137,274 @@ function setup(props, { emit }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
options.rulers = [makeRuler(props)];
|
const state = EditorState.create({
|
||||||
options.gutters = makeGutters(props);
|
doc: props.value || "",
|
||||||
|
extensions: [
|
||||||
|
props.lineNumbers ? lineNumbersExt() : undefined,
|
||||||
|
foldGutterCompartment.of(
|
||||||
|
props.foldGutter
|
||||||
|
? foldGutterExt({
|
||||||
|
openText: "▾",
|
||||||
|
closedText: "▸",
|
||||||
|
})
|
||||||
|
: [],
|
||||||
|
),
|
||||||
|
props.autoCloseBrackets ? closeBrackets() : undefined,
|
||||||
|
props.matchBrackets ? bracketMatching() : undefined,
|
||||||
|
|
||||||
options.rulers = [makeRuler(props)];
|
languageExt.of(await getLanguageExtension(props.mode)),
|
||||||
options.gutters = makeGutters(props);
|
editorTheme.of(
|
||||||
|
theme.value === "dark" ? catppuccinMocha : catppuccinLatte,
|
||||||
|
),
|
||||||
|
overlayField,
|
||||||
|
history(),
|
||||||
|
keymap.of([
|
||||||
|
...defaultKeymap,
|
||||||
|
...closeBracketsKeymap,
|
||||||
|
...historyKeymap,
|
||||||
|
...foldKeymap,
|
||||||
|
indentWithTab,
|
||||||
|
{ key: "Ctrl-l", run: () => false },
|
||||||
|
...(props.extraKeys
|
||||||
|
? Object.entries(props.extraKeys).map(([key, cmd]) => ({
|
||||||
|
key,
|
||||||
|
run: cmd,
|
||||||
|
}))
|
||||||
|
: []),
|
||||||
|
]),
|
||||||
|
|
||||||
_codeMirror = CodeMirror.fromTextArea(textareaRef.value, options);
|
EditorState.tabSize.of(props.tabSize ?? 4),
|
||||||
_codeMirror.on("change", handleChange);
|
EditorState.readOnly.of(props.readOnly ?? false),
|
||||||
_codeMirror.on("focus", handleFocus);
|
|
||||||
_codeMirror.on("beforeSelectionChange", handleSelectionChange);
|
|
||||||
|
|
||||||
window.CodeMirror.keyMap.pcSublime["Ctrl-L"] = false;
|
rulerTheme.of(createRulerTheme(props.ruler, props.rulerColor)),
|
||||||
window.CodeMirror.keyMap.sublime["Ctrl-L"] = false;
|
|
||||||
|
EditorView.updateListener.of((update) => {
|
||||||
|
if (update.docChanged) {
|
||||||
|
const value = update.state.doc.toString();
|
||||||
|
if (value !== _cached) {
|
||||||
|
handleChange(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update.selectionSet) {
|
||||||
|
const selection = update.state.selection.main;
|
||||||
|
handleSelectionChange(selection);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
].filter(Boolean),
|
||||||
|
});
|
||||||
|
|
||||||
|
_codeMirror = new EditorView({
|
||||||
|
state,
|
||||||
|
parent: editorRef.value,
|
||||||
|
});
|
||||||
|
|
||||||
updateValue(props.value || "");
|
|
||||||
updateSelection();
|
updateSelection();
|
||||||
updateOverlay();
|
updateOverlay();
|
||||||
};
|
};
|
||||||
|
|
||||||
const componentWillUnmount = () => {
|
const componentWillUnmount = () => {
|
||||||
_codeMirror?.toTextArea();
|
_codeMirror?.destroy();
|
||||||
};
|
|
||||||
|
|
||||||
const componentDidUpdate = (_, prevProps) => {
|
|
||||||
if (!_codeMirror) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.value !== _cached) {
|
|
||||||
updateValue(props.value);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
!isEqualSelection(props.selection, prevProps.selection) &&
|
|
||||||
!isEqualSelection(props.selection, _codeMirror.listSelections()[0])
|
|
||||||
) {
|
|
||||||
updateSelection();
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
props.overlayStart !== prevProps.overlayStart ||
|
|
||||||
props.overlayEnd !== prevProps.overlayEnd
|
|
||||||
) {
|
|
||||||
updateOverlay();
|
|
||||||
}
|
|
||||||
if (props.mode !== prevProps.mode) {
|
|
||||||
_codeMirror.setOption("mode", props.mode);
|
|
||||||
}
|
|
||||||
if (props.ruler !== prevProps.ruler) {
|
|
||||||
_codeMirror.setOption("rulers", [makeRuler(props)]);
|
|
||||||
}
|
|
||||||
if (props.foldGutter !== prevProps.foldGutter) {
|
|
||||||
_codeMirror.setOption("gutters", makeGutters(props));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateValue = (value) => {
|
const updateValue = (value) => {
|
||||||
|
if (_cached === value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_cached = value;
|
_cached = value;
|
||||||
_codeMirror.setValue(value);
|
_codeMirror.dispatch({
|
||||||
|
changes: {
|
||||||
|
from: 0,
|
||||||
|
to: _codeMirror.state.doc.length,
|
||||||
|
insert: value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (props.autoFold instanceof RegExp) {
|
if (!(props.autoFold instanceof RegExp)) {
|
||||||
const lines = value.split("\n");
|
return;
|
||||||
// going backwards to prevent unfolding folds created earlier
|
}
|
||||||
for (let i = lines.length - 1; i >= 0; i--) {
|
|
||||||
if (props.autoFold.test(lines[i])) {
|
const lines = value.split("\n");
|
||||||
_codeMirror.foldCode(i);
|
const effects = [];
|
||||||
|
|
||||||
|
for (let i = lines.length - 1; i >= 0; i--) {
|
||||||
|
if (props.autoFold.test(lines[i])) {
|
||||||
|
const lineNumber = i + 1;
|
||||||
|
const range = foldable(
|
||||||
|
_codeMirror.state,
|
||||||
|
_codeMirror.state.doc.line(lineNumber).from,
|
||||||
|
_codeMirror.state.doc.line(lineNumber).to,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (range) {
|
||||||
|
effects.push(foldEffect.of(range));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (effects.length > 0) {
|
||||||
|
_codeMirror.dispatch({ effects });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateSelection = () => {
|
const updateSelection = () => {
|
||||||
_codeMirror.setSelection(
|
if (!props.selection || !_codeMirror) {
|
||||||
props.selection?.anchor ?? { line: 0, ch: 0 },
|
return;
|
||||||
props.selection?.head,
|
}
|
||||||
);
|
|
||||||
|
const { doc } = _codeMirror.state;
|
||||||
|
const anchor = Math.max(props.selection.anchor ?? 0, 0);
|
||||||
|
const head = Math.min(props.selection.head ?? anchor, doc.length);
|
||||||
|
|
||||||
|
const currentSelection = _codeMirror.state.selection.main;
|
||||||
|
if (currentSelection.anchor !== anchor || currentSelection.head !== head) {
|
||||||
|
_codeMirror.dispatch({
|
||||||
|
selection: { anchor, head },
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateOverlay = () => {
|
const updateOverlay = () => {
|
||||||
if (_overlay) {
|
if (!_codeMirror) {
|
||||||
_codeMirror.removeOverlay(_overlay);
|
return;
|
||||||
}
|
|
||||||
if (props.overlayStart !== undefined) {
|
|
||||||
const [start, end] = getIndexPosition(props.value, [
|
|
||||||
props.overlayStart,
|
|
||||||
props.overlayEnd,
|
|
||||||
]);
|
|
||||||
_overlay = createOverlay(start, end);
|
|
||||||
_codeMirror.addOverlay(_overlay);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_codeMirror.dispatch({
|
||||||
|
effects: _overlay.of({
|
||||||
|
start: props.overlayStart,
|
||||||
|
end: props.overlayEnd,
|
||||||
|
}),
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFocus = (/* codeMirror, event */) => {
|
const handleChange = (value) => {
|
||||||
if (_codeMirror.getValue() === props.codeSample) {
|
_cached = value;
|
||||||
_codeMirror.execCommand("selectAll");
|
onChange(value);
|
||||||
}
|
updateOverlay();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (doc, change) => {
|
const handleSelectionChange = (selection) => {
|
||||||
if (change.origin !== "setValue") {
|
if (!props.onSelectionChange || !selection) {
|
||||||
_cached = doc.getValue();
|
return;
|
||||||
onChange(_cached);
|
|
||||||
updateOverlay();
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const handleSelectionChange = (doc, change) => {
|
props.onSelectionChange({
|
||||||
props.onSelectionChange?.(change.ranges[0]);
|
anchor: selection.anchor,
|
||||||
|
head: selection.head,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const render = () => (
|
const render = () => (
|
||||||
<div class="editor input">
|
<div class="editor input">
|
||||||
<textarea ref="textarea" />
|
<div ref="editorRef" style={{ position: "absolute", inset: 0 }} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
onMounted(componentDidMount);
|
onMounted(componentDidMount);
|
||||||
onUnmounted(componentWillUnmount);
|
onUnmounted(componentWillUnmount);
|
||||||
watch(() => props, componentDidUpdate, { deep: true });
|
|
||||||
|
watch(
|
||||||
|
() => props.value,
|
||||||
|
(value) => {
|
||||||
|
if (!_codeMirror) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value !== _cached) {
|
||||||
|
updateValue(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.mode,
|
||||||
|
async (mode) => {
|
||||||
|
if (!_codeMirror) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_codeMirror.dispatch({
|
||||||
|
effects: languageExt.reconfigure(await getLanguageExtension(mode)),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.ruler,
|
||||||
|
(ruler) => {
|
||||||
|
if (!_codeMirror) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_codeMirror.dispatch({
|
||||||
|
effects: rulerTheme.reconfigure(
|
||||||
|
createRulerTheme(ruler, props.rulerColor),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.rulerColor,
|
||||||
|
(rulerColor) => {
|
||||||
|
if (!_codeMirror) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_codeMirror.dispatch({
|
||||||
|
effects: rulerTheme.reconfigure(
|
||||||
|
createRulerTheme(props.ruler, rulerColor),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.selection,
|
||||||
|
() => {
|
||||||
|
if (!_codeMirror) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSelection();
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [props.overlayStart, props.overlayEnd],
|
||||||
|
() => {
|
||||||
|
if (!_codeMirror) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateOverlay();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.foldGutter,
|
||||||
|
(foldGutter) => {
|
||||||
|
if (!_codeMirror) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_codeMirror.dispatch({
|
||||||
|
effects: foldGutterCompartment.reconfigure(
|
||||||
|
foldGutter ? foldGutterExt() : [],
|
||||||
|
),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(theme, (newTheme) => {
|
||||||
|
_codeMirror.dispatch({
|
||||||
|
effects: editorTheme.reconfigure(
|
||||||
|
newTheme === "dark" ? catppuccinMocha : catppuccinLatte,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return render;
|
return render;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,81 +429,12 @@ const CodeMirrorPanel = {
|
|||||||
showCursorWhenSelecting: { type: Boolean, default: undefined },
|
showCursorWhenSelecting: { type: Boolean, default: undefined },
|
||||||
tabSize: { type: Number, default: undefined },
|
tabSize: { type: Number, default: undefined },
|
||||||
readOnly: { type: Boolean, default: undefined },
|
readOnly: { type: Boolean, default: undefined },
|
||||||
|
extraKeys: { type: Object, default: undefined },
|
||||||
},
|
},
|
||||||
emits: ["change"],
|
emits: ["change"],
|
||||||
setup,
|
setup,
|
||||||
};
|
};
|
||||||
|
|
||||||
function getIndexPosition(text, indexes) {
|
|
||||||
indexes = [...indexes];
|
|
||||||
let line = 0;
|
|
||||||
let count = 0;
|
|
||||||
let lineStart = 0;
|
|
||||||
const result = [];
|
|
||||||
|
|
||||||
while (indexes.length > 0) {
|
|
||||||
const index = indexes.shift();
|
|
||||||
|
|
||||||
while (count < index && count < text.length) {
|
|
||||||
if (text[count] === "\n") {
|
|
||||||
line++;
|
|
||||||
lineStart = count + 1;
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.push({ line, pos: count - lineStart });
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createOverlay(start, end) {
|
|
||||||
return {
|
|
||||||
token(stream) {
|
|
||||||
const { line } = stream.lineOracle;
|
|
||||||
|
|
||||||
if (line < start.line || line > end.line) {
|
|
||||||
stream.skipToEnd();
|
|
||||||
} else if (line === start.line && stream.pos < start.pos) {
|
|
||||||
stream.pos = start.pos;
|
|
||||||
} else if (line === end.line) {
|
|
||||||
if (stream.pos < end.pos) {
|
|
||||||
stream.pos = end.pos;
|
|
||||||
return "searching";
|
|
||||||
}
|
|
||||||
stream.skipToEnd();
|
|
||||||
} else {
|
|
||||||
stream.skipToEnd();
|
|
||||||
return "searching";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeRuler(props) {
|
|
||||||
return { column: props.ruler, color: props.rulerColor };
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeGutters(props) {
|
|
||||||
return props.foldGutter
|
|
||||||
? ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]
|
|
||||||
: [];
|
|
||||||
}
|
|
||||||
|
|
||||||
function isEqualSelection(selection1, selection2) {
|
|
||||||
const anchor1 = selection1?.anchor ?? { line: 0, ch: 0 };
|
|
||||||
const head1 = selection1?.head ?? anchor1;
|
|
||||||
const anchor2 = selection2?.anchor ?? { line: 0, ch: 0 };
|
|
||||||
const head2 = selection2?.head ?? anchor2;
|
|
||||||
return (
|
|
||||||
head1.line === head2.line &&
|
|
||||||
head1.ch === head2.ch &&
|
|
||||||
anchor1.line === anchor2.line &&
|
|
||||||
anchor1.ch === anchor2.ch
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function InputPanel(props) {
|
export function InputPanel(props) {
|
||||||
return (
|
return (
|
||||||
<CodeMirrorPanel
|
<CodeMirrorPanel
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
html {
|
html {
|
||||||
background-color: light-dark(#fafafa, #1b1b1d);
|
background-color: light-dark(#fafafa, #1b1b1d);
|
||||||
|
--background-color: light-dark(#fafafa, #1b1b1d);
|
||||||
color: light-dark(#6a6a6a, #e3e3e3);
|
color: light-dark(#6a6a6a, #e3e3e3);
|
||||||
--color-border: light-dark(#ddd, #606770);
|
--color-border: light-dark(#ddd, #606770);
|
||||||
color-scheme: light dark;
|
color-scheme: light dark;
|
||||||
@@ -169,94 +170,6 @@ header h1 {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.CodeMirror {
|
|
||||||
height: auto;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
line-height: 1.6;
|
|
||||||
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
|
|
||||||
|
|
||||||
&:is([data-theme="dark"] *) {
|
|
||||||
/* Credit: Chris Kempson, Gerard Braad */
|
|
||||||
background: #000000;
|
|
||||||
color: #eaeaea;
|
|
||||||
div.CodeMirror-selected {
|
|
||||||
background: #424242;
|
|
||||||
}
|
|
||||||
.CodeMirror-gutters {
|
|
||||||
background: #000000;
|
|
||||||
border-right: 0px;
|
|
||||||
}
|
|
||||||
.CodeMirror-guttermarker {
|
|
||||||
color: #e78c45;
|
|
||||||
}
|
|
||||||
.CodeMirror-guttermarker-subtle {
|
|
||||||
color: #777;
|
|
||||||
}
|
|
||||||
.CodeMirror-linenumber {
|
|
||||||
color: #424242;
|
|
||||||
}
|
|
||||||
.CodeMirror-cursor {
|
|
||||||
border-left: 1px solid #6a6a6a;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.cm-comment {
|
|
||||||
color: #d27b53;
|
|
||||||
}
|
|
||||||
span.cm-atom {
|
|
||||||
color: #a16a94;
|
|
||||||
}
|
|
||||||
span.cm-number {
|
|
||||||
color: #a16a94;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.cm-property,
|
|
||||||
span.cm-attribute {
|
|
||||||
color: #99cc99;
|
|
||||||
}
|
|
||||||
span.cm-keyword {
|
|
||||||
color: #d54e53;
|
|
||||||
}
|
|
||||||
span.cm-string {
|
|
||||||
color: #e7c547;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.cm-variable {
|
|
||||||
color: #b9ca4a;
|
|
||||||
}
|
|
||||||
span.cm-variable-2 {
|
|
||||||
color: #7aa6da;
|
|
||||||
}
|
|
||||||
span.cm-def {
|
|
||||||
color: #e78c45;
|
|
||||||
}
|
|
||||||
span.cm-bracket {
|
|
||||||
color: #eaeaea;
|
|
||||||
}
|
|
||||||
span.cm-tag {
|
|
||||||
color: #d54e53;
|
|
||||||
}
|
|
||||||
span.cm-link {
|
|
||||||
color: #a16a94;
|
|
||||||
}
|
|
||||||
span.cm-error {
|
|
||||||
background: #d54e53;
|
|
||||||
color: #6a6a6a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror-activeline-background {
|
|
||||||
background: #2a2a2a;
|
|
||||||
}
|
|
||||||
.CodeMirror-matchingbracket {
|
|
||||||
text-decoration: underline;
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-bar {
|
.bottom-bar {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
@@ -418,3 +331,54 @@ input[type="number"] {
|
|||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
border-top-color: #000;
|
border-top-color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cm-editor {
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--background-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-editor .cm-scroller {
|
||||||
|
font-family:
|
||||||
|
Consolas,
|
||||||
|
Monaco,
|
||||||
|
Andale Mono,
|
||||||
|
Ubuntu Mono,
|
||||||
|
monospace;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-editor.cm-focused {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-content {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-editor .cm-gutters {
|
||||||
|
border-right: 1px solid var(--color-border);
|
||||||
|
background: #f7f7f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-gutter {
|
||||||
|
padding-inline-start: 5px;
|
||||||
|
padding-inline-end: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-overlay-highlight {
|
||||||
|
background-color: rgba(255, 220, 120, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-foldGutter .cm-gutterElement {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="dark"] .cm-overlay-highlight {
|
||||||
|
background-color: rgba(255, 220, 120, 0.32);
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="dark"] .cm-gutters {
|
||||||
|
background-color: #121212;
|
||||||
|
border-right-color: transparent;
|
||||||
|
color: #424242;
|
||||||
|
}
|
||||||
@@ -96,27 +96,3 @@ export function getAstAutoFold(parser) {
|
|||||||
return astAutoFold[parser];
|
return astAutoFold[parser];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertSelectionToRange({ head, anchor }, content) {
|
|
||||||
const lines = content.split("\n");
|
|
||||||
return [head, anchor]
|
|
||||||
.map(
|
|
||||||
({ ch, line }) =>
|
|
||||||
lines.slice(0, line).join("\n").length + ch + (line ? 1 : 0),
|
|
||||||
)
|
|
||||||
.sort((a, b) => a - b);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function convertOffsetToSelection(offset, content) {
|
|
||||||
let line = 0;
|
|
||||||
let ch = 0;
|
|
||||||
for (let i = 0; i < offset && i <= content.length; i++) {
|
|
||||||
if (content[i] === "\n") {
|
|
||||||
line++;
|
|
||||||
ch = 0;
|
|
||||||
} else {
|
|
||||||
ch++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { anchor: { line, ch } };
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,21 @@
|
|||||||
import vitePluginVue from "@vitejs/plugin-vue";
|
import vitePluginVue from "@vitejs/plugin-vue";
|
||||||
import vitePluginVueJsx from "@vitejs/plugin-vue-jsx";
|
import vitePluginVueJsx from "@vitejs/plugin-vue-jsx";
|
||||||
|
import { visualizer as rollupPluginVisualizer } from "rollup-plugin-visualizer";
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
|
|
||||||
const IS_PRODUCTION = process.env.NODE_ENV === "production";
|
const IS_PRODUCTION = process.env.NODE_ENV === "production";
|
||||||
const IS_CI = Boolean(process.env.CI);
|
const IS_CI = Boolean(process.env.CI);
|
||||||
|
|
||||||
// https://vite.dev/config/
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
base: IS_PRODUCTION ? "/playground/" : undefined,
|
base: IS_PRODUCTION ? "/playground/" : undefined,
|
||||||
publicDir: IS_PRODUCTION ? undefined : "./static/",
|
publicDir: IS_PRODUCTION ? undefined : "./static/",
|
||||||
plugins: [vitePluginVue(), vitePluginVueJsx()],
|
plugins: [
|
||||||
|
vitePluginVue(),
|
||||||
|
vitePluginVueJsx(),
|
||||||
|
IS_CI || !IS_PRODUCTION
|
||||||
|
? undefined
|
||||||
|
: rollupPluginVisualizer({ filename: "./static/playground/report.html" }),
|
||||||
|
].filter(Boolean),
|
||||||
build: {
|
build: {
|
||||||
outDir: "./static/playground/",
|
outDir: "./static/playground/",
|
||||||
minify: IS_CI,
|
minify: IS_CI,
|
||||||
|
|||||||
+1101
-787
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user