mirror of
https://github.com/heyman/heynote.git
synced 2025-06-20 09:37:50 +02:00
Initial import
This commit is contained in:
commit
254ca836ef
3478
heynote-codemirror/package-lock.json
generated
Normal file
3478
heynote-codemirror/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
heynote-codemirror/package.json
Normal file
38
heynote-codemirror/package.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "heynote-codemirror",
|
||||||
|
"type": "module",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"build": "rollup -c",
|
||||||
|
"watch": "rollup -c -w",
|
||||||
|
"build_grammar": "lezer-generator src/lang-heynote/heynote.grammar -o src/lang-heynote/heynote.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/commands": "^6.1.2",
|
||||||
|
"@codemirror/lang-html": "^6.4.0",
|
||||||
|
"@codemirror/lang-java": "^6.0.1",
|
||||||
|
"@codemirror/lang-javascript": "^6.1.2",
|
||||||
|
"@codemirror/lang-json": "^6.0.1",
|
||||||
|
"@codemirror/lang-lezer": "^6.0.1",
|
||||||
|
"@codemirror/lang-markdown": "^6.0.5",
|
||||||
|
"@codemirror/lang-php": "^6.0.1",
|
||||||
|
"@codemirror/lang-python": "^6.1.1",
|
||||||
|
"@codemirror/lang-sql": "^6.3.3",
|
||||||
|
"@codemirror/rangeset": "^0.19.9",
|
||||||
|
"@codemirror/search": "^6.2.3",
|
||||||
|
"@lezer/generator": "^1.1.3",
|
||||||
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||||
|
"codemirror": "^6.0.1",
|
||||||
|
"i": "^0.3.7",
|
||||||
|
"npm": "^9.2.0",
|
||||||
|
"rollup": "^3.8.1",
|
||||||
|
"rollup-plugin-typescript2": "^0.34.1",
|
||||||
|
"typescript": "^4.9.4"
|
||||||
|
}
|
||||||
|
}
|
30
heynote-codemirror/rollup.config.mjs
Normal file
30
heynote-codemirror/rollup.config.mjs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//import typescript from 'rollup-plugin-typescript2'
|
||||||
|
import { nodeResolve } from "@rollup/plugin-node-resolve"
|
||||||
|
//import { lezer } from "@lezer/generator/rollup"
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
input: "./src/editor.js",
|
||||||
|
output: {
|
||||||
|
file: "./src/editor.bundle.js",
|
||||||
|
format: "iife",
|
||||||
|
//globals: {
|
||||||
|
// //
|
||||||
|
//},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
// typescript({
|
||||||
|
// check: false,
|
||||||
|
// tsconfigOverride: {
|
||||||
|
// compilerOptions: {
|
||||||
|
// lib: ['es5', 'es6'],
|
||||||
|
// sourceMap: true,
|
||||||
|
// target: 'es6',
|
||||||
|
// strict: false
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }),
|
||||||
|
nodeResolve(),
|
||||||
|
//lezer(),
|
||||||
|
]
|
||||||
|
}
|
5
heynote-codemirror/src/annotation.js
Normal file
5
heynote-codemirror/src/annotation.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { Annotation } from "@codemirror/state"
|
||||||
|
|
||||||
|
export const heynoteEvent = Annotation.define()
|
||||||
|
export const INITIAL_DATA = "initial-data"
|
||||||
|
|
85
heynote-codemirror/src/editor.js
Normal file
85
heynote-codemirror/src/editor.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import {Annotation, EditorState, Compartment} from "@codemirror/state"
|
||||||
|
import {EditorView, keymap, drawSelection} from "@codemirror/view"
|
||||||
|
|
||||||
|
import {indentWithTab, insertTab, indentLess, indentMore} from "@codemirror/commands"
|
||||||
|
import {nord} from "./theme/nord.mjs"
|
||||||
|
import initialData from "./fixture.js"
|
||||||
|
import { customSetup } from "./setup.js"
|
||||||
|
import { heynoteLang } from "./lang-heynote/parser.js"
|
||||||
|
import { noteBlockExtension } from "./note-block.js"
|
||||||
|
import { heynoteEvent, INITIAL_DATA } from "./annotation.js"
|
||||||
|
|
||||||
|
|
||||||
|
let state = EditorState.create({
|
||||||
|
extensions: [
|
||||||
|
/*keymap.of([
|
||||||
|
{
|
||||||
|
key: "Shift-Tab",
|
||||||
|
preventDefault: true,
|
||||||
|
run: () => {
|
||||||
|
console.log("debug:", syntaxTree(editor.state).toString())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]),*/
|
||||||
|
customSetup,
|
||||||
|
//minimalSetup,
|
||||||
|
|
||||||
|
keymap.of([
|
||||||
|
{
|
||||||
|
key: 'Tab',
|
||||||
|
preventDefault: true,
|
||||||
|
//run: insertTab,
|
||||||
|
run: indentMore,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Shift-Tab',
|
||||||
|
preventDefault: true,
|
||||||
|
run: indentLess,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
nord,
|
||||||
|
|
||||||
|
heynoteLang(),
|
||||||
|
noteBlockExtension(),
|
||||||
|
|
||||||
|
// set cursor blink rate to 1 second
|
||||||
|
drawSelection({cursorBlinkRate:1000}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
let editor = new EditorView({
|
||||||
|
state,
|
||||||
|
parent: document.getElementById("editor"),
|
||||||
|
})
|
||||||
|
|
||||||
|
// set initial data
|
||||||
|
editor.update([
|
||||||
|
editor.state.update({
|
||||||
|
changes:{
|
||||||
|
from: 0,
|
||||||
|
to: editor.state.length,
|
||||||
|
insert: initialData,
|
||||||
|
},
|
||||||
|
annotations: heynoteEvent.of(INITIAL_DATA),
|
||||||
|
})
|
||||||
|
])
|
||||||
|
|
||||||
|
/*
|
||||||
|
// render syntax tree
|
||||||
|
setTimeout(() => {
|
||||||
|
function render(tree) {
|
||||||
|
let lists = ''
|
||||||
|
tree.iterate({
|
||||||
|
enter(type) {
|
||||||
|
lists += `<ul><li>${type.name} (${type.from},${type.to})`
|
||||||
|
},
|
||||||
|
leave() {
|
||||||
|
lists += '</ul>'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return lists
|
||||||
|
}
|
||||||
|
let html = render(syntaxTree(editor.state))
|
||||||
|
document.getElementById("syntaxTree").innerHTML = html;
|
||||||
|
}, 1000)
|
||||||
|
*/
|
84
heynote-codemirror/src/fixture.js
Normal file
84
heynote-codemirror/src/fixture.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*export default `∞∞∞text
|
||||||
|
kuk∞∞∞javascript
|
||||||
|
oj∞∞∞prutt
|
||||||
|
hej∞∞∞python
|
||||||
|
f = lambda: 2 +1`;*/
|
||||||
|
|
||||||
|
export default `∞∞∞python
|
||||||
|
# hmm
|
||||||
|
def my_func():
|
||||||
|
print("hejsan")
|
||||||
|
|
||||||
|
|
||||||
|
import {basicSetup} from "codemirror"
|
||||||
|
import {EditorView, keymap} from "@codemirror/view"
|
||||||
|
import {javascript} from "@codemirror/lang-javascript"
|
||||||
|
import {indentWithTab, insertTab, indentLess, indentMore} from "@codemirror/commands"
|
||||||
|
import {nord} from "./nord.mjs"∞∞∞javascript
|
||||||
|
let editor = new EditorView({
|
||||||
|
//extensions: [basicSetup, javascript()],
|
||||||
|
extensions: [
|
||||||
|
basicSetup,
|
||||||
|
javascript(),
|
||||||
|
//keymap.of([indentWithTab]),
|
||||||
|
keymap.of([
|
||||||
|
{
|
||||||
|
key: 'Tab',
|
||||||
|
preventDefault: true,
|
||||||
|
//run: insertTab,
|
||||||
|
run: indentMore,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Shift-Tab',
|
||||||
|
preventDefault: true,
|
||||||
|
run: indentLess,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
nord,
|
||||||
|
],
|
||||||
|
parent: document.getElementById("editor"),
|
||||||
|
})
|
||||||
|
∞∞∞json
|
||||||
|
{
|
||||||
|
"name": "heynote-codemirror",
|
||||||
|
"type": "module",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"build": "rollup -c"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/commands": "^6.1.2",
|
||||||
|
"@codemirror/lang-javascript": "^6.1.2",
|
||||||
|
"@codemirror/lang-json": "^6.0.1",
|
||||||
|
"@codemirror/lang-python": "^6.1.1",
|
||||||
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||||
|
"codemirror": "^6.0.1",
|
||||||
|
"i": "^0.3.7",
|
||||||
|
"npm": "^9.2.0",
|
||||||
|
"rollup": "^3.8.1",
|
||||||
|
"rollup-plugin-typescript2": "^0.34.1",
|
||||||
|
"typescript": "^4.9.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
∞∞∞html
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Test</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Test</h1>
|
||||||
|
<script>
|
||||||
|
console.log("hej")
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
∞∞∞sql
|
||||||
|
SELECT * FROM table WHERE id = 1;
|
||||||
|
|
||||||
|
`;
|
15
heynote-codemirror/src/index.html
Normal file
15
heynote-codemirror/src/index.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Heynote</title>
|
||||||
|
<link rel=stylesheet href="styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="editor"></div>
|
||||||
|
<!--<div id="syntaxTree"></div>-->
|
||||||
|
<script src="editor.bundle.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
31
heynote-codemirror/src/lang-heynote/external-tokens.js
Normal file
31
heynote-codemirror/src/lang-heynote/external-tokens.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { ExternalTokenizer } from '@lezer/lr'
|
||||||
|
import { NoteContent } from "./heynote.terms.js"
|
||||||
|
|
||||||
|
const EOF = -1;
|
||||||
|
|
||||||
|
export const noteContent = new ExternalTokenizer((input) => {
|
||||||
|
let current = input.peek(0);
|
||||||
|
let next = input.peek(1);
|
||||||
|
|
||||||
|
if (current === EOF) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
let potentialLang = "";
|
||||||
|
for (let i=0; i<15; i++) {
|
||||||
|
potentialLang += String.fromCharCode(input.peek(i));
|
||||||
|
}
|
||||||
|
if (potentialLang.match(/^∞∞∞(text|javascript|json|python|html|sql|markdown|java|lezer|php)\n/g)) {
|
||||||
|
input.acceptToken(NoteContent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (next === EOF) {
|
||||||
|
input.acceptToken(NoteContent, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = input.advance();
|
||||||
|
next = input.peek(1);
|
||||||
|
}
|
||||||
|
});
|
21
heynote-codemirror/src/lang-heynote/heynote.grammar
Normal file
21
heynote-codemirror/src/lang-heynote/heynote.grammar
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
@top Document { Note* }
|
||||||
|
|
||||||
|
Note {
|
||||||
|
NoteDelimiter NoteContent
|
||||||
|
}
|
||||||
|
|
||||||
|
NoteDelimiter {
|
||||||
|
noteDelimiterMark NoteLanguage noteDelimiterEnter
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@tokens {
|
||||||
|
noteDelimiterMark { "∞∞∞" }
|
||||||
|
NoteLanguage { "text" | "javascript" | "json" | "python" | "html" | "sql" | "markdown" | "java" | "lezer" | "php" }
|
||||||
|
noteDelimiterEnter { "\n" }
|
||||||
|
//NoteContent { String }
|
||||||
|
//String { (![∞])+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
@external tokens noteContent from "./external-tokens.js" { NoteContent }
|
||||||
|
|
17
heynote-codemirror/src/lang-heynote/heynote.js
Normal file
17
heynote-codemirror/src/lang-heynote/heynote.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||||
|
import {LRParser} from "@lezer/lr"
|
||||||
|
import {noteContent} from "./external-tokens.js"
|
||||||
|
export const parser = LRParser.deserialize({
|
||||||
|
version: 14,
|
||||||
|
states: "!WQQOPOOOVOPO'#C`O[OQO'#C_OOOO'#Cb'#CbQQOPOOOaOPO,58zOOOO,58y,58yOOOO-E6`-E6`OOOQ1G.f1G.f",
|
||||||
|
stateData: "f~OWPO~OTTO~OPUO~OXWO~O",
|
||||||
|
goto: "fVPPPW[P`TROSTQOSQSORVS",
|
||||||
|
nodeNames: "⚠ NoteContent Document Note NoteDelimiter NoteLanguage",
|
||||||
|
maxTerm: 9,
|
||||||
|
skippedNodes: [0],
|
||||||
|
repeatNodeCount: 1,
|
||||||
|
tokenData: "&e~RXYZn#[#]s#^#_![#`#a#u#a#b$_#d#e%T#g#h%p#h#i%v%&x%&y&S~sOX~~vP#h#iy~|P#a#b!P~!SP#`#a!V~![OT~~!_Q#T#U!e#g#h#i~!hP#j#k!k~!nP#T#U!q~!vPT~#g#h!y~!|P#V#W#P~#SP#f#g#V~#YP#]#^#]~#`P#d#e#c~#fP#h#i!V~#lP#c#d#o~#rP#b#c!V~#xP#X#Y#{~$OP#n#o$R~$UP#X#Y$X~$[P#f#g!V~$bP#T#U$e~$hP#f#g$k~$nP#_#`$q~$tP#W#X$w~$zP#c#d$}~%QP#k#l#o~%WQ#[#]%^#m#n%d~%aP#d#e!V~%gP#h#i%j~%mP#[#]#i~%sP#e#f!P~%yP#X#Y%|~&PP#l#m#c~&VP%&x%&y&Y~&]P%&x%&y&`~&eOW~",
|
||||||
|
tokenizers: [0, noteContent],
|
||||||
|
topRules: {"Document":[0,2]},
|
||||||
|
tokenPrec: 0
|
||||||
|
})
|
7
heynote-codemirror/src/lang-heynote/heynote.terms.js
Normal file
7
heynote-codemirror/src/lang-heynote/heynote.terms.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||||
|
export const
|
||||||
|
NoteContent = 1,
|
||||||
|
Document = 2,
|
||||||
|
Note = 3,
|
||||||
|
NoteDelimiter = 4,
|
||||||
|
NoteLanguage = 5
|
45
heynote-codemirror/src/lang-heynote/nested-parser.js
Normal file
45
heynote-codemirror/src/lang-heynote/nested-parser.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { parseMixed } from "@lezer/common"
|
||||||
|
|
||||||
|
import { jsonLanguage } from "@codemirror/lang-json"
|
||||||
|
import { pythonLanguage } from "@codemirror/lang-python"
|
||||||
|
import { javascriptLanguage } from "@codemirror/lang-javascript"
|
||||||
|
import { htmlLanguage } from "@codemirror/lang-html"
|
||||||
|
import { StandardSQL } from "@codemirror/lang-sql"
|
||||||
|
import { markdownLanguage } from "@codemirror/lang-markdown"
|
||||||
|
import { javaLanguage } from "@codemirror/lang-java"
|
||||||
|
import { lezerLanguage } from "@codemirror/lang-lezer"
|
||||||
|
import { phpLanguage } from "@codemirror/lang-php"
|
||||||
|
|
||||||
|
import { NoteContent, NoteLanguage } from "./heynote.terms.js"
|
||||||
|
|
||||||
|
|
||||||
|
const languageMapping = {
|
||||||
|
"json": jsonLanguage.parser,
|
||||||
|
"javascript": javascriptLanguage.parser,
|
||||||
|
"python": pythonLanguage.parser,
|
||||||
|
"html": htmlLanguage.parser,
|
||||||
|
"sql": StandardSQL.language.parser,
|
||||||
|
"markdown": markdownLanguage.parser,
|
||||||
|
"java": javaLanguage.parser,
|
||||||
|
"lezer": lezerLanguage.parser,
|
||||||
|
"php": phpLanguage.parser,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function configureNesting() {
|
||||||
|
return parseMixed((node, input) => {
|
||||||
|
let id = node.type.id
|
||||||
|
if (id == NoteContent) {
|
||||||
|
let noteLang = node.node.parent.firstChild.getChildren(NoteLanguage)[0]
|
||||||
|
let langName = input.read(noteLang?.from, noteLang?.to)
|
||||||
|
//console.log("langName:", langName)
|
||||||
|
if (langName in languageMapping) {
|
||||||
|
//console.log("found parser for language:", langName)
|
||||||
|
return {
|
||||||
|
parser:languageMapping[langName],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
}
|
44
heynote-codemirror/src/lang-heynote/parser.js
Normal file
44
heynote-codemirror/src/lang-heynote/parser.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { parser } from "./heynote.js"
|
||||||
|
import { configureNesting } from "./nested-parser.js";
|
||||||
|
|
||||||
|
import {LRLanguage, LanguageSupport, indentNodeProp, foldNodeProp, foldInside, delimitedIndent} from "@codemirror/language"
|
||||||
|
import {styleTags, tags as t} from "@lezer/highlight"
|
||||||
|
|
||||||
|
import { json } from "@codemirror/lang-json"
|
||||||
|
import { javascript } from "@codemirror/lang-javascript"
|
||||||
|
|
||||||
|
|
||||||
|
function foldNode(node) {
|
||||||
|
//console.log("foldNode", node);
|
||||||
|
return {from:node.from, to:node.to-1};
|
||||||
|
//let first = node.firstChild, last = node.lastChild;
|
||||||
|
//return first && first.to < last.from ? { from: first.to, to: last.type.isError ? node.to : last.from } : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HeynoteLanguage = LRLanguage.define({
|
||||||
|
parser: parser.configure({
|
||||||
|
props: [
|
||||||
|
styleTags({
|
||||||
|
NoteDelimiter: t.tagName,
|
||||||
|
}),
|
||||||
|
|
||||||
|
foldNodeProp.add({
|
||||||
|
NoteContent: foldNode,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
wrap: configureNesting(),
|
||||||
|
}),
|
||||||
|
languageData: {
|
||||||
|
commentTokens: {line: ";"}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export function heynoteLang() {
|
||||||
|
let wrap = configureNesting();
|
||||||
|
let lang = HeynoteLanguage.configure({dialect:"", wrap:wrap});
|
||||||
|
return new LanguageSupport(lang, [json().support])
|
||||||
|
}
|
||||||
|
|
||||||
|
/*export function heynote() {
|
||||||
|
return new LanguageSupport(HeynoteLanguage)
|
||||||
|
}*/
|
178
heynote-codemirror/src/note-block.js
Normal file
178
heynote-codemirror/src/note-block.js
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
import { ViewPlugin, EditorView, Decoration, WidgetType } from "@codemirror/view"
|
||||||
|
import { layer, RectangleMarker } from "@codemirror/view"
|
||||||
|
import { EditorState, RangeSetBuilder, StateField } from "@codemirror/state";
|
||||||
|
import { RangeSet } from "@codemirror/rangeset";
|
||||||
|
import { syntaxTree } from "@codemirror/language"
|
||||||
|
import { Note, Document, NoteDelimiter } from "./lang-heynote/heynote.terms.js"
|
||||||
|
import { IterMode } from "@lezer/common";
|
||||||
|
import { INITIAL_DATA } from "./annotation.js";
|
||||||
|
|
||||||
|
class NoteBlockStart extends WidgetType {
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
eq(other) {
|
||||||
|
//return other.checked == this.checked
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
toDOM() {
|
||||||
|
let wrap = document.createElement("div")
|
||||||
|
wrap.className = "block-start"
|
||||||
|
//wrap.innerHTML = "<br>"
|
||||||
|
return wrap
|
||||||
|
}
|
||||||
|
ignoreEvent() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FirstNoteBlockStart extends WidgetType {
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
eq(other) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
toDOM() {
|
||||||
|
let wrap = document.createElement("span")
|
||||||
|
wrap.className = "block-start-first"
|
||||||
|
return wrap
|
||||||
|
}
|
||||||
|
ignoreEvent() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const noteBlockWidget = () => {
|
||||||
|
const decorate = (state) => {
|
||||||
|
const widgets = [];
|
||||||
|
|
||||||
|
syntaxTree(state).iterate({
|
||||||
|
enter: (type) => {
|
||||||
|
if (type.name === "NoteDelimiter") {
|
||||||
|
//console.log("found!", type.name, type.from, type.to)
|
||||||
|
let deco = Decoration.replace({
|
||||||
|
widget: type.from === 0 ? new FirstNoteBlockStart() : new NoteBlockStart(),
|
||||||
|
inclusive: false,
|
||||||
|
block: type.from === 0 ? false : true,
|
||||||
|
side: 0,
|
||||||
|
});
|
||||||
|
widgets.push(deco.range(type.from, type.from === 0 ? type.to :type.to-1));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mode: IterMode.IgnoreMounts,
|
||||||
|
});
|
||||||
|
|
||||||
|
return widgets.length > 0 ? RangeSet.of(widgets) : Decoration.none;
|
||||||
|
};
|
||||||
|
|
||||||
|
const noteBlockStartField = StateField.define({
|
||||||
|
create(state) {
|
||||||
|
return decorate(state);
|
||||||
|
},
|
||||||
|
update(widgets, transaction) {
|
||||||
|
if (transaction.docChanged) {
|
||||||
|
return decorate(transaction.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
//return widgets.map(transaction.changes);
|
||||||
|
return widgets
|
||||||
|
},
|
||||||
|
provide(field) {
|
||||||
|
return EditorView.decorations.from(field);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return [noteBlockStartField];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function atomicRanges(view) {
|
||||||
|
let builder = new RangeSetBuilder()
|
||||||
|
syntaxTree(view.state).iterate({
|
||||||
|
enter: (type) => {
|
||||||
|
if (type.type.id === NoteDelimiter) {
|
||||||
|
builder.add(type.from, type.to, {})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mode: IterMode.IgnoreMounts,
|
||||||
|
});
|
||||||
|
return builder.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
const atomicNoteBlock = ViewPlugin.fromClass(
|
||||||
|
class {
|
||||||
|
constructor(view) {
|
||||||
|
this.atomicRanges = atomicRanges(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
update(update) {
|
||||||
|
if (update.docChanged) {
|
||||||
|
this.atomicRanges = atomicRanges(update.view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: plugin => EditorView.atomicRanges.of(view => {
|
||||||
|
return view.plugin(plugin)?.atomicRanges || Decoration.none
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const blockLayer = () => {
|
||||||
|
return layer({
|
||||||
|
above: false,
|
||||||
|
|
||||||
|
markers(view) {
|
||||||
|
const markers = []
|
||||||
|
let idx = 0
|
||||||
|
syntaxTree(view.state).iterate({
|
||||||
|
enter: (type) => {
|
||||||
|
//console.log("type", type.name, type.type.id, Document)
|
||||||
|
if (type.type.id == Document || type.type.id == Note) {
|
||||||
|
return true
|
||||||
|
} else if (type.type.id === NoteDelimiter) {
|
||||||
|
const contentNode = type.node.nextSibling
|
||||||
|
//console.log("adding marker", type.node.nextSibling.name)
|
||||||
|
//let line = view.state.doc.lineAt(type.from)
|
||||||
|
const fromCoords = view.coordsAtPos(contentNode.from)
|
||||||
|
const toCoords = view.coordsAtPos(contentNode.to)
|
||||||
|
//console.log("line", fromCoords.top, toCoords.bottom)
|
||||||
|
//console.log("documentTop", view.documentTop)
|
||||||
|
markers.push(new RectangleMarker(
|
||||||
|
idx++ % 2 == 0 ? "block-even" : "block-odd",
|
||||||
|
0,
|
||||||
|
fromCoords.top - (view.documentTop - view.documentPadding.top),
|
||||||
|
2000,
|
||||||
|
(toCoords.bottom - fromCoords.top),
|
||||||
|
))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
mode: IterMode.IgnoreMounts,
|
||||||
|
});
|
||||||
|
return markers
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
update(update, dom) {
|
||||||
|
return update.docChanged || update.viewportChanged
|
||||||
|
},
|
||||||
|
|
||||||
|
class: "blocks-layer"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const preventFirstBlockFromBeingDeleted = EditorState.changeFilter.of((tr) => {
|
||||||
|
if (!tr.annotations.some(a => a.value === INITIAL_DATA)) {
|
||||||
|
return [-1,10]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export const noteBlockExtension = () => {
|
||||||
|
return [noteBlockWidget(), atomicNoteBlock, blockLayer(), preventFirstBlockFromBeingDeleted]
|
||||||
|
}
|
96
heynote-codemirror/src/setup.js
Normal file
96
heynote-codemirror/src/setup.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import { lineNumbers, highlightActiveLineGutter, highlightSpecialChars, drawSelection, dropCursor, rectangularSelection, crosshairCursor, highlightActiveLine, keymap } from '@codemirror/view';
|
||||||
|
export { EditorView } from '@codemirror/view';
|
||||||
|
import { EditorState } from '@codemirror/state';
|
||||||
|
import { foldGutter, indentOnInput, syntaxHighlighting, defaultHighlightStyle, bracketMatching, foldKeymap } from '@codemirror/language';
|
||||||
|
import { history, defaultKeymap, historyKeymap } from '@codemirror/commands';
|
||||||
|
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search';
|
||||||
|
import { closeBrackets, autocompletion, closeBracketsKeymap, completionKeymap } from '@codemirror/autocomplete';
|
||||||
|
import { lintKeymap } from '@codemirror/lint';
|
||||||
|
|
||||||
|
// (The superfluous function calls around the list of extensions work
|
||||||
|
// around current limitations in tree-shaking software.)
|
||||||
|
/**
|
||||||
|
This is an extension value that just pulls together a number of
|
||||||
|
extensions that you might want in a basic editor. It is meant as a
|
||||||
|
convenient helper to quickly set up CodeMirror without installing
|
||||||
|
and importing a lot of separate packages.
|
||||||
|
|
||||||
|
Specifically, it includes...
|
||||||
|
|
||||||
|
- [the default command bindings](https://codemirror.net/6/docs/ref/#commands.defaultKeymap)
|
||||||
|
- [line numbers](https://codemirror.net/6/docs/ref/#view.lineNumbers)
|
||||||
|
- [special character highlighting](https://codemirror.net/6/docs/ref/#view.highlightSpecialChars)
|
||||||
|
- [the undo history](https://codemirror.net/6/docs/ref/#commands.history)
|
||||||
|
- [a fold gutter](https://codemirror.net/6/docs/ref/#language.foldGutter)
|
||||||
|
- [custom selection drawing](https://codemirror.net/6/docs/ref/#view.drawSelection)
|
||||||
|
- [drop cursor](https://codemirror.net/6/docs/ref/#view.dropCursor)
|
||||||
|
- [multiple selections](https://codemirror.net/6/docs/ref/#state.EditorState^allowMultipleSelections)
|
||||||
|
- [reindentation on input](https://codemirror.net/6/docs/ref/#language.indentOnInput)
|
||||||
|
- [the default highlight style](https://codemirror.net/6/docs/ref/#language.defaultHighlightStyle) (as fallback)
|
||||||
|
- [bracket matching](https://codemirror.net/6/docs/ref/#language.bracketMatching)
|
||||||
|
- [bracket closing](https://codemirror.net/6/docs/ref/#autocomplete.closeBrackets)
|
||||||
|
- [autocompletion](https://codemirror.net/6/docs/ref/#autocomplete.autocompletion)
|
||||||
|
- [rectangular selection](https://codemirror.net/6/docs/ref/#view.rectangularSelection) and [crosshair cursor](https://codemirror.net/6/docs/ref/#view.crosshairCursor)
|
||||||
|
- [active line highlighting](https://codemirror.net/6/docs/ref/#view.highlightActiveLine)
|
||||||
|
- [active line gutter highlighting](https://codemirror.net/6/docs/ref/#view.highlightActiveLineGutter)
|
||||||
|
- [selection match highlighting](https://codemirror.net/6/docs/ref/#search.highlightSelectionMatches)
|
||||||
|
- [search](https://codemirror.net/6/docs/ref/#search.searchKeymap)
|
||||||
|
- [linting](https://codemirror.net/6/docs/ref/#lint.lintKeymap)
|
||||||
|
|
||||||
|
(You'll probably want to add some language package to your setup
|
||||||
|
too.)
|
||||||
|
|
||||||
|
This extension does not allow customization. The idea is that,
|
||||||
|
once you decide you want to configure your editor more precisely,
|
||||||
|
you take this package's source (which is just a bunch of imports
|
||||||
|
and an array literal), copy it into your own code, and adjust it
|
||||||
|
as desired.
|
||||||
|
*/
|
||||||
|
const customSetup = /*@__PURE__*/(() => [
|
||||||
|
lineNumbers(),
|
||||||
|
highlightActiveLineGutter(),
|
||||||
|
highlightSpecialChars(),
|
||||||
|
history(),
|
||||||
|
foldGutter(),
|
||||||
|
drawSelection(),
|
||||||
|
dropCursor(),
|
||||||
|
EditorState.allowMultipleSelections.of(true),
|
||||||
|
indentOnInput(),
|
||||||
|
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
||||||
|
bracketMatching(),
|
||||||
|
//closeBrackets(),
|
||||||
|
autocompletion(),
|
||||||
|
rectangularSelection(),
|
||||||
|
crosshairCursor(),
|
||||||
|
highlightActiveLine(),
|
||||||
|
highlightSelectionMatches(),
|
||||||
|
keymap.of([
|
||||||
|
...closeBracketsKeymap,
|
||||||
|
...defaultKeymap,
|
||||||
|
...searchKeymap,
|
||||||
|
...historyKeymap,
|
||||||
|
...foldKeymap,
|
||||||
|
...completionKeymap,
|
||||||
|
...lintKeymap
|
||||||
|
])
|
||||||
|
])();
|
||||||
|
/**
|
||||||
|
A minimal set of extensions to create a functional editor. Only
|
||||||
|
includes [the default keymap](https://codemirror.net/6/docs/ref/#commands.defaultKeymap), [undo
|
||||||
|
history](https://codemirror.net/6/docs/ref/#commands.history), [special character
|
||||||
|
highlighting](https://codemirror.net/6/docs/ref/#view.highlightSpecialChars), [custom selection
|
||||||
|
drawing](https://codemirror.net/6/docs/ref/#view.drawSelection), and [default highlight
|
||||||
|
style](https://codemirror.net/6/docs/ref/#language.defaultHighlightStyle).
|
||||||
|
*/
|
||||||
|
const minimalSetup = /*@__PURE__*/(() => [
|
||||||
|
highlightSpecialChars(),
|
||||||
|
history(),
|
||||||
|
drawSelection(),
|
||||||
|
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
||||||
|
keymap.of([
|
||||||
|
...defaultKeymap,
|
||||||
|
...historyKeymap,
|
||||||
|
])
|
||||||
|
])();
|
||||||
|
|
||||||
|
export { customSetup, minimalSetup };
|
41
heynote-codemirror/src/styles.css
Normal file
41
heynote-codemirror/src/styles.css
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: #fff;
|
||||||
|
color: #444;
|
||||||
|
font-family: 'Gill Sans','Gill Sans MT',Calibri,'Trebuchet MS',sans-serif;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#editor {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
#editor .cm-editor {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#syntaxTree {
|
||||||
|
height: 20%;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kuken {
|
||||||
|
background: #f00;
|
||||||
|
}
|
||||||
|
.balle .test-marker {
|
||||||
|
background: rgb(94, 84, 106);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.blocks-layer .block-even {
|
||||||
|
background: #3f3b4b;
|
||||||
|
}
|
||||||
|
.blocks-layer .block-odd {
|
||||||
|
background: rgb(57, 71, 77);
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-start {
|
||||||
|
/*background: rgb(0, 0, 0);
|
||||||
|
height: 2px*/
|
||||||
|
}
|
214
heynote-codemirror/src/theme/nord.mjs
Normal file
214
heynote-codemirror/src/theme/nord.mjs
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
import { EditorView } from '@codemirror/view';
|
||||||
|
import { HighlightStyle, syntaxHighlighting } from '@codemirror/language';
|
||||||
|
import { tags } from '@lezer/highlight';
|
||||||
|
|
||||||
|
|
||||||
|
// Colors from https://www.nordtheme.com/docs/colors-and-palettes
|
||||||
|
// Polar Night
|
||||||
|
const base00 = '#2e3440', // black
|
||||||
|
base01 = '#3b4252', // dark grey
|
||||||
|
base02 = '#434c5e', base03 = '#4c566a'; // grey
|
||||||
|
// Snow Storm
|
||||||
|
const base04 = '#d8dee9', // grey
|
||||||
|
base05 = '#e5e9f0', // off white
|
||||||
|
base06 = '#eceff4'; // white
|
||||||
|
// Frost
|
||||||
|
const base07 = '#8fbcbb', // moss green
|
||||||
|
base08 = '#88c0d0', // ice blue
|
||||||
|
base09 = '#81a1c1', // water blue
|
||||||
|
base0A = '#5e81ac'; // deep blue
|
||||||
|
// Aurora
|
||||||
|
const base0b = '#bf616a', // red
|
||||||
|
base0C = '#d08770', // orange
|
||||||
|
base0D = '#ebcb8b', // yellow
|
||||||
|
base0E = '#a3be8c', // green
|
||||||
|
base0F = '#b48ead'; // purple
|
||||||
|
const invalid = '#d30102', darkBackground = '#252a33', background = base00, tooltipBackground = base01, selection = base03, cursor = '#fff';
|
||||||
|
const highlightBackground = 'rgba(255,255,255,0.04)';
|
||||||
|
|
||||||
|
const lineNumberColor = '#535963';
|
||||||
|
const commentColor = '#888d97';
|
||||||
|
const matchingBracket = 'rgba(255,255,255,0.1)';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
The editor theme styles for Nord.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const nordTheme = /*@__PURE__*/EditorView.theme({
|
||||||
|
'&': {
|
||||||
|
color: base04,
|
||||||
|
backgroundColor: background
|
||||||
|
},
|
||||||
|
'.cm-content': {
|
||||||
|
caretColor: cursor
|
||||||
|
},
|
||||||
|
'.cm-cursor, .cm-dropCursor': { borderLeftColor: cursor, borderLeftWidth:'2px', height:'19px !important', marginTop:'-2px !important' },
|
||||||
|
'&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': { backgroundColor: selection },
|
||||||
|
'.cm-panels': { backgroundColor: darkBackground, color: base03 },
|
||||||
|
'.cm-panels.cm-panels-top': { borderBottom: '2px solid black' },
|
||||||
|
'.cm-panels.cm-panels-bottom': { borderTop: '2px solid black' },
|
||||||
|
'.cm-searchMatch': {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
outline: `1px solid ${base07}`
|
||||||
|
},
|
||||||
|
'.cm-searchMatch.cm-searchMatch-selected': {
|
||||||
|
backgroundColor: base04,
|
||||||
|
color: base00
|
||||||
|
},
|
||||||
|
'.cm-activeLine': { backgroundColor: highlightBackground },
|
||||||
|
'.cm-selectionMatch': {
|
||||||
|
backgroundColor: base05,
|
||||||
|
color: base01
|
||||||
|
},
|
||||||
|
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
|
||||||
|
outline: `0.5px solid ${base07}`
|
||||||
|
},
|
||||||
|
'&.cm-focused .cm-matchingBracket': {
|
||||||
|
backgroundColor: matchingBracket,
|
||||||
|
color: base02
|
||||||
|
},
|
||||||
|
'&.cm-focused .cm-nonmatchingBracket': {
|
||||||
|
outline: `0.5px solid #bc8f8f`
|
||||||
|
},
|
||||||
|
'.cm-gutters': {
|
||||||
|
backgroundColor: base00,
|
||||||
|
color: lineNumberColor,
|
||||||
|
border: 'none'
|
||||||
|
},
|
||||||
|
'.cm-activeLineGutter': {
|
||||||
|
backgroundColor: highlightBackground,
|
||||||
|
color: base04
|
||||||
|
},
|
||||||
|
'.cm-foldPlaceholder': {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
border: 'none',
|
||||||
|
color: '#ddd'
|
||||||
|
},
|
||||||
|
'.cm-tooltip': {
|
||||||
|
border: 'none',
|
||||||
|
backgroundColor: tooltipBackground
|
||||||
|
},
|
||||||
|
'.cm-tooltip .cm-tooltip-arrow:before': {
|
||||||
|
borderTopColor: 'transparent',
|
||||||
|
borderBottomColor: 'transparent'
|
||||||
|
},
|
||||||
|
'.cm-tooltip .cm-tooltip-arrow:after': {
|
||||||
|
borderTopColor: tooltipBackground,
|
||||||
|
borderBottomColor: tooltipBackground
|
||||||
|
},
|
||||||
|
'.cm-tooltip-autocomplete': {
|
||||||
|
'& > ul > li[aria-selected]': {
|
||||||
|
backgroundColor: highlightBackground,
|
||||||
|
color: base03
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, { dark: true });
|
||||||
|
/**
|
||||||
|
The highlighting style for code in the Nord theme.
|
||||||
|
*/
|
||||||
|
const nordHighlightStyle = /*@__PURE__*/HighlightStyle.define([
|
||||||
|
{ tag: tags.keyword, color: base0A },
|
||||||
|
{
|
||||||
|
tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName],
|
||||||
|
color: base08
|
||||||
|
},
|
||||||
|
{ tag: [tags.variableName], color: base07 },
|
||||||
|
{ tag: [/*@__PURE__*/tags.function(tags.variableName)], color: base07 },
|
||||||
|
{ tag: [tags.labelName], color: base09 },
|
||||||
|
{
|
||||||
|
tag: [tags.color, /*@__PURE__*/tags.constant(tags.name), /*@__PURE__*/tags.standard(tags.name)],
|
||||||
|
color: base0A
|
||||||
|
},
|
||||||
|
{ tag: [/*@__PURE__*/tags.definition(tags.name), tags.separator], color: base0E },
|
||||||
|
{ tag: [tags.brace], color: base07 },
|
||||||
|
{
|
||||||
|
tag: [tags.annotation],
|
||||||
|
color: invalid
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [tags.number, tags.changed, tags.annotation, tags.modifier, tags.self, tags.namespace],
|
||||||
|
color: base0F
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [tags.typeName, tags.className],
|
||||||
|
color: base0D
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [tags.operator, tags.operatorKeyword],
|
||||||
|
color: base0E
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [tags.tagName],
|
||||||
|
color: base0F
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [tags.squareBracket],
|
||||||
|
color: base0b
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [tags.angleBracket],
|
||||||
|
color: base0C
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [tags.attributeName],
|
||||||
|
color: base0D
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [tags.regexp],
|
||||||
|
color: base0A
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [tags.quote],
|
||||||
|
color: base0F
|
||||||
|
},
|
||||||
|
{ tag: [tags.string], color: base0E },
|
||||||
|
{
|
||||||
|
tag: tags.link,
|
||||||
|
color: base0E,
|
||||||
|
textDecoration: 'underline',
|
||||||
|
textUnderlinePosition: 'under'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [tags.url, tags.escape, /*@__PURE__*/tags.special(tags.string)],
|
||||||
|
color: base07
|
||||||
|
},
|
||||||
|
{ tag: [tags.meta], color: base08 },
|
||||||
|
{ tag: [tags.monospace], color: base04, fontStyle: 'italic' },
|
||||||
|
{ tag: [tags.comment], color: commentColor, fontStyle: 'italic' },
|
||||||
|
{ tag: tags.strong, fontWeight: 'bold', color: base0A },
|
||||||
|
{ tag: tags.emphasis, fontStyle: 'italic', color: base0A },
|
||||||
|
{ tag: tags.strikethrough, textDecoration: 'line-through' },
|
||||||
|
{ tag: tags.heading, fontWeight: 'bold', color: base0A },
|
||||||
|
{ tag: /*@__PURE__*/tags.special(tags.heading1), fontWeight: 'bold', color: base0A },
|
||||||
|
{ tag: tags.heading1, fontWeight: 'bold', color: base0A },
|
||||||
|
{
|
||||||
|
tag: [tags.heading2, tags.heading3, tags.heading4],
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: base0A
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [tags.heading5, tags.heading6],
|
||||||
|
color: base0A
|
||||||
|
},
|
||||||
|
{ tag: [tags.atom, tags.bool, /*@__PURE__*/tags.special(tags.variableName)], color: base0C },
|
||||||
|
{
|
||||||
|
tag: [tags.processingInstruction, tags.inserted],
|
||||||
|
color: base07
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: [tags.contentSeparator],
|
||||||
|
color: base0D
|
||||||
|
},
|
||||||
|
{ tag: tags.invalid, color: base02, borderBottom: `1px dotted ${invalid}` }
|
||||||
|
]);
|
||||||
|
/**
|
||||||
|
Extension to enable the Nord theme (both the editor theme and
|
||||||
|
the highlight style).
|
||||||
|
*/
|
||||||
|
const nord = [
|
||||||
|
nordTheme,
|
||||||
|
/*@__PURE__*/syntaxHighlighting(nordHighlightStyle)
|
||||||
|
];
|
||||||
|
|
||||||
|
export { nord, nordHighlightStyle, nordTheme };
|
Loading…
x
Reference in New Issue
Block a user