mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-05-07 23:08:38 +00:00
Merge branches 'master' and 'add-cm-features' of https://github.com/naturalcrit/homebrewery into add-cm-features
This commit is contained in:
@@ -13,9 +13,12 @@ import {
|
||||
ViewPlugin,
|
||||
drawSelection,
|
||||
dropCursor,
|
||||
rectangularSelection,
|
||||
crosshairCursor,
|
||||
} from '@codemirror/view';
|
||||
import { EditorState, Compartment, StateEffect, StateField } from '@codemirror/state';
|
||||
import { foldAll as foldAllCmd, unfoldAll as unfoldAllCmd, foldGutter, foldKeymap, syntaxHighlighting } from '@codemirror/language';
|
||||
import { foldEffect } from '@codemirror/language';
|
||||
import { defaultKeymap, history, undo, redo, undoDepth, redoDepth } from '@codemirror/commands';
|
||||
import { languages } from '@codemirror/language-data';
|
||||
import { css } from '@codemirror/lang-css';
|
||||
@@ -27,14 +30,16 @@ import { closeBrackets } from '@codemirror/autocomplete';
|
||||
|
||||
const autoCloseBrackets = closeBrackets({ brackets: ['()', '[]', '{{}}'] });
|
||||
|
||||
import * as themesImport from '@uiw/codemirror-themes-all';
|
||||
import defaultCM5Theme from '@themes/codeMirror/default.js';
|
||||
import darkbrewery from '@themes/codeMirror/darkbrewery.js';
|
||||
import cm5Themes from 'codemirror-5-themes';
|
||||
|
||||
const themes = { default: defaultCM5Theme, darkbrewery, ...themesImport };
|
||||
const themes = { default: defaultCM5Theme, ...cm5Themes, darkbrewery };
|
||||
const themeCompartment = new Compartment();
|
||||
const highlightCompartment = new Compartment();
|
||||
|
||||
console.log(themes);
|
||||
|
||||
import { generalKeymap, markdownKeymap } from './customKeyMaps.js';
|
||||
import foldOnPages from './customFolding.js';
|
||||
import { customHighlightStyle, tokenizeCustomMarkdown, tokenizeCustomCSS } from './customHighlight.js';
|
||||
@@ -229,6 +234,8 @@ const CodeEditor = forwardRef(
|
||||
|
||||
//multiple cursors and selections
|
||||
drawSelection(),
|
||||
rectangularSelection(),
|
||||
crosshairCursor(),
|
||||
EditorState.allowMultipleSelections.of(true),
|
||||
dropCursor(),
|
||||
programmaticCursorLineField,
|
||||
@@ -392,7 +399,19 @@ const CodeEditor = forwardRef(
|
||||
foldAll : ()=>{
|
||||
const view = viewRef.current;
|
||||
if(!view) return;
|
||||
view.dispatch(foldAllCmd(view));
|
||||
|
||||
const doc = view.state.doc;
|
||||
const pages = pageMap.current;
|
||||
|
||||
const effects = pages.map((start, i)=>{
|
||||
const next = pages[i + 1] || doc.length;
|
||||
const from = i ? doc.line(doc.lineAt(start).number + 1).from : 0;
|
||||
const to = doc.line(doc.lineAt(next).number).from - 1;
|
||||
|
||||
return to > from ? foldEffect.of({ from, to }) : null;
|
||||
}).filter(Boolean);
|
||||
|
||||
view.dispatch({ effects });
|
||||
},
|
||||
unfoldAll : ()=>{
|
||||
const view = viewRef.current;
|
||||
|
||||
@@ -1,31 +1,134 @@
|
||||
// Icon fonts for emoji/autocomplete
|
||||
@import (less) "@themes/fonts/iconFonts/diceFont.less";
|
||||
@import (less) "@themes/fonts/iconFonts/elderberryInn.less";
|
||||
@import (less) "@themes/fonts/iconFonts/gameIcons.less";
|
||||
@import (less) "@themes/fonts/iconFonts/fontAwesome.less";
|
||||
@import (less) '@themes/fonts/iconFonts/diceFont.less';
|
||||
@import (less) '@themes/fonts/iconFonts/elderberryInn.less';
|
||||
@import (less) '@themes/fonts/iconFonts/gameIcons.less';
|
||||
@import (less) '@themes/fonts/iconFonts/fontAwesome.less';
|
||||
|
||||
@keyframes sourceMoveAnimation {
|
||||
50% {
|
||||
color: white;
|
||||
background-color: red;
|
||||
color : white;
|
||||
background-color : red;
|
||||
}
|
||||
100% {
|
||||
color: unset;
|
||||
background-color: unset;
|
||||
color : unset;
|
||||
background-color : unset;
|
||||
}
|
||||
}
|
||||
|
||||
:where(.codeEditor) {
|
||||
font-family: monospace;
|
||||
height: 100%;
|
||||
width:100%;
|
||||
|
||||
.cm-content {
|
||||
tab-size:2 !important;
|
||||
width : 100%;
|
||||
height : calc(100% - 25px);
|
||||
font-family : monospace;
|
||||
|
||||
.cm-editor {
|
||||
height : 100%;
|
||||
outline : none !important;
|
||||
}
|
||||
|
||||
@media screen and (pointer: coarse) {
|
||||
font-size: 16px;
|
||||
&.brewSnippets .cm-snippetLine,
|
||||
:where(&.brewText) .cm-pageLine {
|
||||
background : #33333328;
|
||||
border-top : #333399 solid 1px;
|
||||
}
|
||||
|
||||
&.brewSnippets {
|
||||
.cm-pageLine {
|
||||
color : #777777;
|
||||
background : #3E4E3E1B;
|
||||
border-top : #3399423B solid 1px;
|
||||
}
|
||||
}
|
||||
|
||||
&:where(.brewText), &.brewSnippets {
|
||||
|
||||
|
||||
.cm-pageLine[data-page-number]::after {
|
||||
float : right;
|
||||
color : grey;
|
||||
content : attr(data-page-number);
|
||||
}
|
||||
.cm-columnSplit {
|
||||
font-style : italic;
|
||||
color : grey;
|
||||
background-color : fade(#229999, 15%);
|
||||
border-bottom : #229999 solid 1px;
|
||||
}
|
||||
.cm-define {
|
||||
&:not(.term):not(.definition) {
|
||||
font-weight : bold;
|
||||
color : #949494;
|
||||
background : #E5E5E5;
|
||||
border-radius : 3px;
|
||||
}
|
||||
&.term { color : rgb(96, 117, 143); }
|
||||
&.definition { color : rgb(97, 57, 178); }
|
||||
}
|
||||
.cm-block:not(.cm-comment) {
|
||||
font-weight : bold;
|
||||
color : purple;
|
||||
}
|
||||
.cm-inline-block,
|
||||
.cm-define .cm-inline-block {
|
||||
font-weight : bold;
|
||||
color : red;
|
||||
span:not(.cm-comment) { color : inherit; }
|
||||
}
|
||||
.cm-injection:not(.cm-comment) {
|
||||
font-weight : bold;
|
||||
color : green;
|
||||
span { color : inherit; }
|
||||
}
|
||||
.cm-emoji:not(.cm-comment) {
|
||||
padding-bottom : 1px;
|
||||
margin-left : 2px;
|
||||
font-weight : bold;
|
||||
color : #360034;
|
||||
outline : solid 2px #FF96FC;
|
||||
outline-offset : -2px;
|
||||
background : #FFC8FF;
|
||||
border-radius : 6px;
|
||||
}
|
||||
.cm-superscript:not(.cm-comment) {
|
||||
font-size : 0.9em;
|
||||
font-weight : bold;
|
||||
vertical-align : super;
|
||||
color : goldenrod;
|
||||
}
|
||||
.cm-subscript:not(.cm-comment) {
|
||||
font-size : 0.9em;
|
||||
font-weight : bold;
|
||||
vertical-align : sub;
|
||||
color : rgb(123, 123, 15);
|
||||
}
|
||||
.cm-definitionList {
|
||||
.cm-definitionTerm { color : rgb(96, 117, 143); }
|
||||
.cm-definitionColon:not(:has(.cm-comment)) {
|
||||
font-weight : bold;
|
||||
color : #949494;
|
||||
background : #E5E5E5;
|
||||
border-radius : 3px;
|
||||
}
|
||||
.cm-definitionDesc { color : rgb(97, 57, 178); }
|
||||
}
|
||||
|
||||
.cm-tooltip-autocomplete {
|
||||
|
||||
li {
|
||||
display : flex;
|
||||
gap : 10px;
|
||||
align-items : center;
|
||||
justify-content : flex-start;
|
||||
|
||||
.cm-completionIcon { display : none; }
|
||||
.cm-tooltip-autocomplete .cm-completionLabel { translate : 0 -2px; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cm-content { tab-size : 2 !important; }
|
||||
|
||||
@media screen and (pointer : coarse) {
|
||||
font-size : 16px;
|
||||
}
|
||||
|
||||
.cm-gutterElement span {
|
||||
@@ -44,14 +147,14 @@
|
||||
|
||||
/* Flash animation for source moves */
|
||||
.cm-line.sourceMoveFlash {
|
||||
animation-name: sourceMoveAnimation;
|
||||
animation-duration: 0.4s;
|
||||
animation-name : sourceMoveAnimation;
|
||||
animation-duration : 0.4s;
|
||||
}
|
||||
|
||||
/* Search input */
|
||||
.cm-searchField {
|
||||
width: 25em !important;
|
||||
outline: 1px inset #00000055 !important;
|
||||
width : 25em !important;
|
||||
outline : 1px inset #00000055 !important;
|
||||
}
|
||||
|
||||
/* Tab character visualization (optional) */
|
||||
@@ -67,6 +170,6 @@
|
||||
|
||||
/* Emoji preview styling */
|
||||
.emojiPreview {
|
||||
font-size: 1.5em;
|
||||
line-height: 1.2em;
|
||||
font-size : 1.5em;
|
||||
line-height : 1.2em;
|
||||
}
|
||||
|
||||
@@ -125,8 +125,6 @@ export function tokenizeCustomMarkdown(text) {
|
||||
from : offset,
|
||||
to : offset + desc.length,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// --- multiline def list ---
|
||||
@@ -139,14 +137,14 @@ export function tokenizeCustomMarkdown(text) {
|
||||
for (let i = lineNumber + 1; i < lines.length; i++) {
|
||||
const nextLine = lines[i];
|
||||
const onlyColonsMatch = /^:*$/.test(nextLine);
|
||||
const defMatch = /^(::)(.*\S.*)?\s*$/.exec(nextLine);
|
||||
const defMatch = /^(::)(.+)$/.exec(nextLine);
|
||||
if(!onlyColonsMatch && defMatch) {
|
||||
defs.push({ colons: defMatch[1], desc: defMatch[2], line: i });
|
||||
endLine = i;
|
||||
} else break;
|
||||
}
|
||||
|
||||
if(defs.length > 0) {
|
||||
if(defs.length > 0 && lineText.trim().length > 0) {
|
||||
tokens.push({
|
||||
line : startLine,
|
||||
type : customTags.definitionList,
|
||||
@@ -177,7 +175,7 @@ export function tokenizeCustomMarkdown(text) {
|
||||
line : d.line,
|
||||
type : customTags.definitionDesc,
|
||||
from : d.colons.length,
|
||||
to : d.colons.length + d.desc.length,
|
||||
to : d.colons.length + d.desc?.length,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* eslint max-lines: ["error", { "max": 300 }] */
|
||||
import { keymap } from '@codemirror/view';
|
||||
import { undo, redo, indentMore } from '@codemirror/commands';
|
||||
import { undo, redo, indentMore, deleteLine } from '@codemirror/commands';
|
||||
import { Prec } from '@codemirror/state';
|
||||
import * as prettier from "prettier/standalone";
|
||||
import * as postcssPlugin from "prettier/plugins/postcss";
|
||||
|
||||
@@ -35,75 +36,48 @@ const indentLess = (view)=>{
|
||||
return true;
|
||||
};
|
||||
|
||||
const makeBold = (view)=>{
|
||||
const wrapSelection = (prefix, suffix) => (view) => {
|
||||
const { from, to } = view.state.selection.main;
|
||||
const selected = view.state.doc.sliceString(from, to);
|
||||
const text = selected.startsWith('**') && selected.endsWith('**')
|
||||
? selected.slice(2, -2)
|
||||
: `**${selected}**`;
|
||||
|
||||
let text, selection;
|
||||
|
||||
if(from === to) {
|
||||
text = prefix + suffix;
|
||||
selection = { anchor: from + prefix.length, head: from + prefix.length };
|
||||
}
|
||||
else if(selected.startsWith(prefix) && selected.endsWith(suffix)) {
|
||||
text = selected.slice(prefix.length, -suffix.length);
|
||||
selection = { anchor: from, head: from + text.length };
|
||||
}
|
||||
else {
|
||||
text = `${prefix}${selected}${suffix}`;
|
||||
selection = { anchor: from, head: from + text.length };
|
||||
}
|
||||
|
||||
view.dispatch({
|
||||
changes : { from, to, insert: text },
|
||||
selection : { anchor: from + text.length },
|
||||
selection
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const makeItalic = (view)=>{
|
||||
const { from, to } = view.state.selection.main;
|
||||
const selected = view.state.doc.sliceString(from, to);
|
||||
const text = selected.startsWith('*') && selected.endsWith('*')
|
||||
? selected.slice(1, -1)
|
||||
: `*${selected}*`;
|
||||
view.dispatch({
|
||||
changes : { from, to, insert: text },
|
||||
selection : { anchor: from + text.length },
|
||||
});
|
||||
return true;
|
||||
};
|
||||
const makeNbsp = (view) => {
|
||||
const { from } = view.state.selection.main;
|
||||
|
||||
const makeUnderline = (view)=>{
|
||||
const { from, to } = view.state.selection.main;
|
||||
const selected = view.state.doc.sliceString(from, to);
|
||||
const text = selected.startsWith('<u>') && selected.endsWith('</u>')
|
||||
? selected.slice(3, -4)
|
||||
: `<u>${selected}</u>`;
|
||||
view.dispatch({
|
||||
changes : { from, to, insert: text },
|
||||
selection : { anchor: from + text.length },
|
||||
});
|
||||
return true;
|
||||
};
|
||||
const prev2 = from >= 2
|
||||
? view.state.doc.sliceString(from - 2, from)
|
||||
: '';
|
||||
|
||||
const makeSuper = (view)=>{
|
||||
const { from, to } = view.state.selection.main;
|
||||
const selected = view.state.doc.sliceString(from, to);
|
||||
const text = selected.startsWith('^') && selected.endsWith('^')
|
||||
? selected.slice(1, -1)
|
||||
: `^${selected}^`;
|
||||
view.dispatch({
|
||||
changes : { from, to, insert: text },
|
||||
selection : { anchor: from + text.length },
|
||||
});
|
||||
return true;
|
||||
};
|
||||
const insert = (prev2 === ':>' || prev2 === '>>') ? '>' : ':>';
|
||||
|
||||
const makeSub = (view)=>{
|
||||
const { from, to } = view.state.selection.main;
|
||||
const selected = view.state.doc.sliceString(from, to);
|
||||
const text = selected.startsWith('^^') && selected.endsWith('^^')
|
||||
? selected.slice(2, -2)
|
||||
: `^^${selected}^^`;
|
||||
view.dispatch({
|
||||
changes : { from, to, insert: text },
|
||||
selection : { anchor: from + text.length },
|
||||
});
|
||||
return true;
|
||||
};
|
||||
view.dispatch({
|
||||
changes : { from, to: from, insert },
|
||||
selection : { anchor: from + insert.length },
|
||||
});
|
||||
|
||||
const makeNbsp = (view)=>{
|
||||
const { from, to } = view.state.selection.main;
|
||||
view.dispatch({ changes: { from, to, insert: ' ' } });
|
||||
return true;
|
||||
return true;
|
||||
};
|
||||
|
||||
const makeSpace = (view)=>{
|
||||
@@ -206,36 +180,38 @@ const newPage = (view)=>{
|
||||
return true;
|
||||
};
|
||||
|
||||
export const generalKeymap = keymap.of([
|
||||
export const generalKeymap = Prec.high(keymap.of([
|
||||
{ key: 'Tab', run: indentMore },
|
||||
{ key: 'Mod-z', run: undo }, //i think it may be unnecessary
|
||||
{ key: 'Mod-Shift-z', run: redo },
|
||||
]);
|
||||
{ key: 'Mod-y', run: redo },
|
||||
{ key: 'Mod-d', run: deleteLine },
|
||||
]));
|
||||
|
||||
export const markdownKeymap = keymap.of([
|
||||
export const markdownKeymap = Prec.highest(keymap.of([
|
||||
//{ key: 'Shift-Tab', run: indentMore },
|
||||
{ key: 'Shift-Tab', run: indentLess },
|
||||
{ key: 'Mod-b', run: makeBold },
|
||||
{ key: 'Mod-i', run: makeItalic },
|
||||
{ key: 'Mod-u', run: makeUnderline },
|
||||
{ key: 'Shift-Mod-=', run: makeSuper },
|
||||
{ key: 'Mod-=', run: makeSub },
|
||||
{ key: 'Mod-.', run: makeNbsp },
|
||||
{ key: 'Shift-Mod-.', run: makeSpace },
|
||||
{ key: 'Shift-Mod-,', run: removeSpace },
|
||||
{ key: 'Mod-m', run: makeSpan },
|
||||
{ key: 'Shift-Mod-m', run: makeDiv },
|
||||
{ key: 'Mod-/', run: makeComment },
|
||||
{ key: 'Mod-k', run: makeLink },
|
||||
{ key: 'Mod-l', run: makeList('UL') },
|
||||
{ key: 'Shift-Mod-l', run: makeList('OL') },
|
||||
{ key: 'Shift-Mod-1', run: makeHeader(1) },
|
||||
{ key: 'Shift-Mod-2', run: makeHeader(2) },
|
||||
{ key: 'Shift-Mod-3', run: makeHeader(3) },
|
||||
{ key: 'Shift-Mod-4', run: makeHeader(4) },
|
||||
{ key: 'Shift-Mod-5', run: makeHeader(5) },
|
||||
{ key: 'Shift-Mod-6', run: makeHeader(6) },
|
||||
{ key: 'Shift-Tab', run: indentLess },
|
||||
{ key: 'Mod-b', run: wrapSelection('**', '**') }, // makeBold
|
||||
{ key: 'Mod-i', run: wrapSelection('*', '*') }, // makeItalic
|
||||
{ key: 'Mod-u', run: wrapSelection('<u>', '</u>') }, // makeUnderline
|
||||
{ key: 'Shift-Mod-=', run: wrapSelection('^', '^') }, // makeSuper
|
||||
{ key: 'Mod-=', run: wrapSelection('^^', '^^') }, // makeSub
|
||||
{ key: 'Mod-.', run: makeNbsp },
|
||||
{ key: 'Shift-Mod-.', run: makeSpace },
|
||||
{ key: 'Shift-Mod-,', run: removeSpace },
|
||||
{ key: 'Mod-m', run: makeSpan },
|
||||
{ key: 'Shift-Mod-m', run: makeDiv },
|
||||
{ key: 'Mod-/', run: makeComment },
|
||||
{ key: 'Mod-k', run: makeLink },
|
||||
{ key: 'Mod-l', run: makeList('UL') },
|
||||
{ key: 'Shift-Mod-l', run: makeList('OL') },
|
||||
{ key: 'Shift-Mod-1', run: makeHeader(1) },
|
||||
{ key: 'Shift-Mod-2', run: makeHeader(2) },
|
||||
{ key: 'Shift-Mod-3', run: makeHeader(3) },
|
||||
{ key: 'Shift-Mod-4', run: makeHeader(4) },
|
||||
{ key: 'Shift-Mod-5', run: makeHeader(5) },
|
||||
{ key: 'Shift-Mod-6', run: makeHeader(6) },
|
||||
{ key: 'Mod-Enter', run: newPage },
|
||||
{ key: 'Shift-Mod-Enter', run: newColumn },
|
||||
{ key: 'Mod-Enter', run: newPage },
|
||||
{ key: 'Mod-Shift-f', run: formatCSS },
|
||||
]);
|
||||
{ key: 'Mod-Shift-f', run: formatCSS },
|
||||
]));
|
||||
|
||||
Reference in New Issue
Block a user