0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-06-22 04:58:40 +00:00

Merge branch 'master' into preloadVars

This commit is contained in:
David Bolack
2026-05-18 21:50:32 -05:00
21 changed files with 859 additions and 1341 deletions
+55 -8
View File
@@ -13,9 +13,11 @@ import {
ViewPlugin, ViewPlugin,
drawSelection, drawSelection,
dropCursor, dropCursor,
rectangularSelection,
crosshairCursor,
} from '@codemirror/view'; } from '@codemirror/view';
import { EditorState, Compartment, StateEffect, StateField } from '@codemirror/state'; import { EditorState, Compartment, StateEffect, StateField } from '@codemirror/state';
import { foldAll as foldAllCmd, unfoldAll as unfoldAllCmd, foldGutter, foldKeymap, syntaxHighlighting } from '@codemirror/language'; import { foldAll as foldAllCmd, unfoldAll as unfoldAllCmd, foldGutter, foldKeymap, foldEffect, foldState, syntaxHighlighting } from '@codemirror/language';
import { defaultKeymap, history, undo, redo, undoDepth, redoDepth } from '@codemirror/commands'; import { defaultKeymap, history, undo, redo, undoDepth, redoDepth } from '@codemirror/commands';
import { languages } from '@codemirror/language-data'; import { languages } from '@codemirror/language-data';
import { css } from '@codemirror/lang-css'; import { css } from '@codemirror/lang-css';
@@ -27,11 +29,11 @@ import { closeBrackets } from '@codemirror/autocomplete';
const autoCloseBrackets = closeBrackets({ brackets: ['()', '[]', '{{}}'] }); const autoCloseBrackets = closeBrackets({ brackets: ['()', '[]', '{{}}'] });
import * as themesImport from '@uiw/codemirror-themes-all';
import defaultCM5Theme from '@themes/codeMirror/default.js'; import defaultCM5Theme from '@themes/codeMirror/default.js';
import darkbrewery from '@themes/codeMirror/darkbrewery.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 themeCompartment = new Compartment();
const highlightCompartment = new Compartment(); const highlightCompartment = new Compartment();
@@ -146,11 +148,14 @@ const CodeEditor = forwardRef(
const editorRef = useRef(null); const editorRef = useRef(null);
const viewRef = useRef(null); const viewRef = useRef(null);
const docsRef = useRef({}); const docsRef = useRef({});
const tabRef = useRef(tab);
const prevTabRef = useRef(tab); const prevTabRef = useRef(tab);
const scrollRef = useRef({});
const foldsRef = useRef({});
const pageMap = useRef([]); const pageMap = useRef([]);
const recomputePages = (doc)=>{ const recomputePages = (doc)=>{
if(tab !== 'brewText') return;
const pages = [0]; const pages = [0];
const text = doc.toString(); const text = doc.toString();
let offset = 0; let offset = 0;
@@ -176,6 +181,14 @@ const CodeEditor = forwardRef(
return page; return page;
}; };
const getFoldRanges = (state)=>{
const folds = [];
state.field(foldState, false)?.between(0, state.doc.length, (from, to)=>{
folds.push({ from, to });
});
return folds;
};
const createExtensions = ({ onChange, language, editorTheme })=>{ const createExtensions = ({ onChange, language, editorTheme })=>{
const setEventListeners = EditorView.updateListener.of((update)=>{ const setEventListeners = EditorView.updateListener.of((update)=>{
if(update.docChanged) { if(update.docChanged) {
@@ -229,6 +242,8 @@ const CodeEditor = forwardRef(
//multiple cursors and selections //multiple cursors and selections
drawSelection(), drawSelection(),
rectangularSelection(),
crosshairCursor(),
EditorState.allowMultipleSelections.of(true), EditorState.allowMultipleSelections.of(true),
dropCursor(), dropCursor(),
programmaticCursorLineField, programmaticCursorLineField,
@@ -260,11 +275,10 @@ const CodeEditor = forwardRef(
ticking = true; ticking = true;
requestAnimationFrame(()=>{ requestAnimationFrame(()=>{
const top = view.scrollDOM.scrollTop; const top = view.scrollDOM.scrollTop;
scrollRef.current[tabRef.current] = top;
const block = view.lineBlockAtHeight(top); const block = view.lineBlockAtHeight(top);
const page = findPageFromPos(block.from);
const page = findPageFromPos(block.from); // CHANGED
onViewChange(page); onViewChange(page);
ticking = false; ticking = false;
}); });
}; };
@@ -279,12 +293,23 @@ const CodeEditor = forwardRef(
}; };
}, []); }, []);
const restoreFolds = (view, folds)=>{
if(!folds?.length) return;
view.dispatch({
effects : folds.map((f)=>foldEffect.of(f))
});
};
useEffect(()=>{ useEffect(()=>{
const view = viewRef.current; const view = viewRef.current;
if(!view) return; if(!view) return;
tabRef.current = tab;
const prevTab = prevTabRef.current; const prevTab = prevTabRef.current;
foldsRef.current[prevTab] = getFoldRanges(view.state);
if(prevTab !== tab) { if(prevTab !== tab) {
docsRef.current[prevTab] = view.state; docsRef.current[prevTab] = view.state;
@@ -298,6 +323,16 @@ const CodeEditor = forwardRef(
} }
view.setState(nextState); view.setState(nextState);
restoreFolds(view, foldsRef.current[tab]);
const savedScroll = scrollRef.current[tab];
if(savedScroll != null) {
requestAnimationFrame(()=>{
view.scrollDOM.scrollTop = savedScroll;
});
}
prevTabRef.current = tab; prevTabRef.current = tab;
} }
view.focus(); view.focus();
@@ -392,7 +427,19 @@ const CodeEditor = forwardRef(
foldAll : ()=>{ foldAll : ()=>{
const view = viewRef.current; const view = viewRef.current;
if(!view) return; 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 : ()=>{ unfoldAll : ()=>{
const view = viewRef.current; const view = viewRef.current;
+128 -21
View File
@@ -1,31 +1,138 @@
// Icon fonts for emoji/autocomplete // Icon fonts for emoji/autocomplete
@import (less) "@themes/fonts/iconFonts/diceFont.less"; @import (less) '@themes/fonts/iconFonts/diceFont.less';
@import (less) "@themes/fonts/iconFonts/elderberryInn.less"; @import (less) '@themes/fonts/iconFonts/elderberryInn.less';
@import (less) "@themes/fonts/iconFonts/gameIcons.less"; @import (less) '@themes/fonts/iconFonts/gameIcons.less';
@import (less) "@themes/fonts/iconFonts/fontAwesome.less"; @import (less) '@themes/fonts/iconFonts/fontAwesome.less';
@keyframes sourceMoveAnimation { @keyframes sourceMoveAnimation {
50% { 50% {
color: white; color : white;
background-color: red; background-color : red;
} }
100% { 100% {
color: unset; color : unset;
background-color: unset; background-color : unset;
} }
} }
:where(.codeEditor) { :where(.codeEditor) {
font-family: monospace; width : 100%;
height: 100%; height : calc(100% - 25px);
width:100%; font-family : monospace;
.cm-content { .cm-editor {
tab-size:2 !important; height : 100%;
outline : none !important;
} }
@media screen and (pointer: coarse) { &.brewSnippets .cm-snippetLine,
font-size: 16px; :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-strikethrough {
text-decoration: line-through;
}
.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 { .cm-gutterElement span {
@@ -44,14 +151,14 @@
/* Flash animation for source moves */ /* Flash animation for source moves */
.cm-line.sourceMoveFlash { .cm-line.sourceMoveFlash {
animation-name: sourceMoveAnimation; animation-name : sourceMoveAnimation;
animation-duration: 0.4s; animation-duration : 0.4s;
} }
/* Search input */ /* Search input */
.cm-searchField { .cm-searchField {
width: 25em !important; width : 25em !important;
outline: 1px inset #00000055 !important; outline : 1px inset #00000055 !important;
} }
/* Tab character visualization (optional) */ /* Tab character visualization (optional) */
@@ -67,6 +174,6 @@
/* Emoji preview styling */ /* Emoji preview styling */
.emojiPreview { .emojiPreview {
font-size: 1.5em; font-size : 1.5em;
line-height: 1.2em; line-height : 1.2em;
} }
@@ -16,6 +16,7 @@ const customTags = {
definitionTerm : 'definitionTerm', // .cm-definitionTerm definitionTerm : 'definitionTerm', // .cm-definitionTerm
definitionDesc : 'definitionDesc', // .cm-definitionDesc definitionDesc : 'definitionDesc', // .cm-definitionDesc
definitionColon : 'definitionColon', // .cm-definitionColon definitionColon : 'definitionColon', // .cm-definitionColon
strikethrough : 'strikethrough', // .cm-strikethrough
//CSS //CSS
@@ -81,6 +82,23 @@ export function tokenizeCustomMarkdown(text) {
} }
} }
// --- Strikethrough ---
if(/\~/.test(lineText)) {
const strikethroughRegex = /~(?!\s)(.+?)(?<!\s)~/g;
const match = strikethroughRegex.exec(lineText);
const type = customTags.strikethrough;
if(match) {
tokens.push({
line : lineNumber,
type,
from : match.index,
to : match.index + match[0].length,
});
}
}
// --- single line def list --- // --- single line def list ---
const singleLineRegex = /^(?=.*[^:])(.+?)(\s*)(::)([^\n]*)$/dmy; const singleLineRegex = /^(?=.*[^:])(.+?)(\s*)(::)([^\n]*)$/dmy;
const match = singleLineRegex.exec(lineText); const match = singleLineRegex.exec(lineText);
@@ -125,8 +143,6 @@ export function tokenizeCustomMarkdown(text) {
from : offset, from : offset,
to : offset + desc.length, to : offset + desc.length,
}); });
return;
} }
// --- multiline def list --- // --- multiline def list ---
@@ -139,14 +155,14 @@ export function tokenizeCustomMarkdown(text) {
for (let i = lineNumber + 1; i < lines.length; i++) { for (let i = lineNumber + 1; i < lines.length; i++) {
const nextLine = lines[i]; const nextLine = lines[i];
const onlyColonsMatch = /^:*$/.test(nextLine); const onlyColonsMatch = /^:*$/.test(nextLine);
const defMatch = /^(::)(.*\S.*)?\s*$/.exec(nextLine); const defMatch = /^(::)(.+)$/.exec(nextLine);
if(!onlyColonsMatch && defMatch) { if(!onlyColonsMatch && defMatch) {
defs.push({ colons: defMatch[1], desc: defMatch[2], line: i }); defs.push({ colons: defMatch[1], desc: defMatch[2], line: i });
endLine = i; endLine = i;
} else break; } else break;
} }
if(defs.length > 0) { if(defs.length > 0 && lineText.trim().length > 0) {
tokens.push({ tokens.push({
line : startLine, line : startLine,
type : customTags.definitionList, type : customTags.definitionList,
@@ -177,20 +193,20 @@ export function tokenizeCustomMarkdown(text) {
line : d.line, line : d.line,
type : customTags.definitionDesc, type : customTags.definitionDesc,
from : d.colons.length, from : d.colons.length,
to : d.colons.length + d.desc.length, to : d.colons.length + d.desc?.length,
}); });
}); });
} }
} }
if(lineText.includes('{') && lineText.includes('}')) { if(lineText.includes('{') && lineText.includes('}')) {
const injectionRegex = /(?:^|[^{\n])({(?=((?:[:=](?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':={}\s]*)*))\2})/gm; const injectionRegex = /(?:^|[^{\n])({(?=((?:[:=](?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':={}\s]*)*))\2})/gmd;
let match; let match;
while ((match = injectionRegex.exec(lineText)) !== null) { while ((match = injectionRegex.exec(lineText)) !== null) {
tokens.push({ tokens.push({
line : lineNumber, line : lineNumber,
from : match.index, from : match.indices[1][0],
to : match.index + match[1].length, to : match.indices[1][1],
type : customTags.injection, type : customTags.injection,
}); });
} }
+69 -87
View File
@@ -1,6 +1,18 @@
/* eslint max-lines: ["error", { "max": 300 }] */ /* eslint max-lines: ["error", { "max": 300 }] */
import { keymap } from '@codemirror/view'; 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';
const insertTab = (view)=>{
const { from, to } = view.state.selection.main;
view.dispatch({
changes : { from, to, insert: ' ' },
selection : { anchor: from + 2 }
});
return true;
};
const indentLess = (view)=>{ const indentLess = (view)=>{
const { from, to } = view.state.selection.main; const { from, to } = view.state.selection.main;
@@ -16,74 +28,43 @@ const indentLess = (view)=>{
return true; return true;
}; };
const makeBold = (view)=>{ const wrapSelection = (prefix, suffix)=>(view)=>{
const { from, to } = view.state.selection.main; const changes = [];
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;
};
const makeItalic = (view)=>{ for (const range of view.state.selection.ranges) {
const { from, to } = view.state.selection.main; const { from, to } = range;
const selected = view.state.doc.sliceString(from, to); 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 makeUnderline = (view)=>{ let text;
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 makeSuper = (view)=>{ if(from === to) { text = prefix + suffix; } else if(selected.startsWith(prefix) && selected.endsWith(suffix)) {
const { from, to } = view.state.selection.main; text = selected.slice(prefix.length, -suffix.length);
const selected = view.state.doc.sliceString(from, to); } else {text = `${prefix}${selected}${suffix}`;}
const text = selected.startsWith('^') && selected.endsWith('^')
? selected.slice(1, -1) changes.push({ from, to, insert: text });
: `^${selected}^`; }
view.dispatch({
changes : { from, to, insert: text },
selection : { anchor: from + text.length },
});
return true;
};
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({ view.dispatch({
changes : { from, to, insert: text }, changes
selection : { anchor: from + text.length },
}); });
return true; return true;
}; };
const makeNbsp = (view)=>{ const makeNbsp = (view)=>{
const { from, to } = view.state.selection.main; const { from } = view.state.selection.main;
view.dispatch({ changes: { from, to, insert: '&nbsp;' } });
const prev2 = from >= 2
? view.state.doc.sliceString(from - 2, from)
: '';
const insert = (prev2 === ':>' || prev2 === '>>') ? '>' : ':>';
view.dispatch({
changes : { from, to: from, insert },
selection : { anchor: from + insert.length },
});
return true; return true;
}; };
@@ -187,36 +168,37 @@ const newPage = (view)=>{
return true; return true;
}; };
export const generalKeymap = keymap.of([ export const generalKeymap = Prec.high(keymap.of([
{ key: 'Tab', run: indentMore }, { key: 'Tab', run: insertTab },
{ key: 'Mod-z', run: undo }, //i think it may be unnecessary { key: 'Mod-z', run: undo }, //i think it may be unnecessary
{ key: 'Mod-Shift-z', run: redo }, { 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: indentMore },
{ key: 'Shift-Tab', run: indentLess }, { key: 'Shift-Tab', run: indentLess },
{ key: 'Mod-b', run: makeBold }, { key: 'Mod-b', run: wrapSelection('**', '**') }, // makeBold
{ key: 'Mod-i', run: makeItalic }, { key: 'Mod-i', run: wrapSelection('*', '*') }, // makeItalic
{ key: 'Mod-u', run: makeUnderline }, { key: 'Mod-u', run: wrapSelection('<u>', '</u>') }, // makeUnderline
{ key: 'Shift-Mod-=', run: makeSuper }, { key: 'Shift-Mod-=', run: wrapSelection('^', '^') }, // makeSuper
{ key: 'Mod-=', run: makeSub }, { key: 'Mod-=', run: wrapSelection('^^', '^^') }, // makeSub
{ key: 'Mod-.', run: makeNbsp }, { key: 'Mod-.', run: makeNbsp },
{ key: 'Shift-Mod-.', run: makeSpace }, { key: 'Shift-Mod-.', run: makeSpace },
{ key: 'Shift-Mod-,', run: removeSpace }, { key: 'Shift-Mod-,', run: removeSpace },
{ key: 'Mod-m', run: makeSpan }, { key: 'Mod-m', run: makeSpan },
{ key: 'Shift-Mod-m', run: makeDiv }, { key: 'Shift-Mod-m', run: makeDiv },
{ key: 'Mod-/', run: makeComment }, { key: 'Mod-/', run: makeComment },
{ key: 'Mod-k', run: makeLink }, { key: 'Mod-k', run: makeLink },
{ key: 'Mod-l', run: makeList('UL') }, { key: 'Mod-l', run: makeList('UL') },
{ key: 'Shift-Mod-l', run: makeList('OL') }, { key: 'Shift-Mod-l', run: makeList('OL') },
{ key: 'Shift-Mod-1', run: makeHeader(1) }, { key: 'Shift-Mod-1', run: makeHeader(1) },
{ key: 'Shift-Mod-2', run: makeHeader(2) }, { key: 'Shift-Mod-2', run: makeHeader(2) },
{ key: 'Shift-Mod-3', run: makeHeader(3) }, { key: 'Shift-Mod-3', run: makeHeader(3) },
{ key: 'Shift-Mod-4', run: makeHeader(4) }, { key: 'Shift-Mod-4', run: makeHeader(4) },
{ key: 'Shift-Mod-5', run: makeHeader(5) }, { key: 'Shift-Mod-5', run: makeHeader(5) },
{ key: 'Shift-Mod-6', run: makeHeader(6) }, { key: 'Shift-Mod-6', run: makeHeader(6) },
{ key: 'Mod-Enter', run: newPage },
{ key: 'Shift-Mod-Enter', run: newColumn }, { key: 'Shift-Mod-Enter', run: newColumn },
{ key: 'Mod-Enter', run: newPage }, ]));
]);
+2 -2
View File
@@ -11,11 +11,11 @@ import MetadataEditor from './metadataEditor/metadataEditor.jsx';
const EDITOR_THEME_KEY = 'HB_editor_theme'; const EDITOR_THEME_KEY = 'HB_editor_theme';
import * as themesImport from '@uiw/codemirror-themes-all';
import defaultCM5Theme from '@themes/codeMirror/default.js'; import defaultCM5Theme from '@themes/codeMirror/default.js';
import darkbrewery from '@themes/codeMirror/darkbrewery.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 EditorThemes = Object.entries(themes) const EditorThemes = Object.entries(themes)
.filter(([name, value])=>Array.isArray(value) && .filter(([name, value])=>Array.isArray(value) &&
-110
View File
@@ -6,116 +6,6 @@
height : 100%; height : 100%;
container : editor / inline-size; container : editor / inline-size;
background : white; background : white;
:where(.codeEditor) {
height : calc(100% - 25px);
.cm-editor { height : 100%;
outline:none !important;
}
&.brewSnippets .cm-snippetLine {
background : #33333328;
border-top : #333399 solid 1px;
}
:where(&.brewText) .cm-pageLine {
background : #33333328;
border-top : #333399 solid 1px;
}
&.brewSnippets {
.cm-pageLine {
background : #3e4e3e1b;
border-top : #3399423b solid 1px;
color:#777;
}
}
&:where(.brewText), &.brewSnippets {
.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-pageLine[data-page-number]::after {
content:attr(data-page-number);
float:right;
color : grey;
}
.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:not(.cm-comment) {
font-weight : bold;
color : red ;
span { 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 {
font-weight : bold;
color : #949494;
background : #E5E5E5;
border-radius : 3px;
}
.cm-definitionDesc { color : rgb(97, 57, 178); }
}
}
}
.brewJump { .brewJump {
position : absolute; position : absolute;
@@ -23,19 +23,25 @@ const ThemeSnippets = {
V3_Blank : V3_Blank, V3_Blank : V3_Blank,
}; };
import * as themesImport from '@uiw/codemirror-themes-all';
import defaultCM5Theme from '@themes/codeMirror/default.js'; import defaultCM5Theme from '@themes/codeMirror/default.js';
import darkbrewery from '@themes/codeMirror/darkbrewery.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 EditorThemes = Object.entries(themes) const themeNames = Object.entries(themes)
.filter(([name, value]) => .filter(([name, value])=>Array.isArray(value) &&
Array.isArray(value) &&
!name.endsWith('Init') && !name.endsWith('Init') &&
!name.endsWith('Style') !name.endsWith('Style')
) )
.map(([name]) => name); .map(([name])=>name);
const EditorThemes = [
'default',
...themeNames
.filter((name)=>name !== 'default')
.sort((a, b)=>a.localeCompare(b))
];
const execute = function(val, props){ const execute = function(val, props){
if(_.isFunction(val)) return val(props); if(_.isFunction(val)) return val(props);
@@ -163,7 +169,7 @@ const Snippetbar = createReactClass({
this.props.updateEditorTheme(e.target.value); this.props.updateEditorTheme(e.target.value);
this.setState({ this.setState({
showThemeSelector : false, themeSelector : false,
}); });
}, },
+7 -1
View File
@@ -17,7 +17,7 @@ const getRedditLink = (brew)=>{
return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(brew.title.toWellFormed())}&text=${encodeURIComponent(text)}`; return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(brew.title.toWellFormed())}&text=${encodeURIComponent(text)}`;
}; };
export default ({ brew })=>( export default ({ brew, currentPage })=>(
<Nav.dropdown> <Nav.dropdown>
<Nav.item color='teal' icon='fas fa-share-alt'> <Nav.item color='teal' icon='fas fa-share-alt'>
share share
@@ -28,6 +28,12 @@ export default ({ brew })=>(
<Nav.item color='blue' onClick={()=>{navigator.clipboard.writeText(`${global.config.baseUrl}/share/${getShareId(brew)}`);}}> <Nav.item color='blue' onClick={()=>{navigator.clipboard.writeText(`${global.config.baseUrl}/share/${getShareId(brew)}`);}}>
copy url copy url
</Nav.item> </Nav.item>
{currentPage > 1 &&
<Nav.item
color='blue'
onClick={()=>{navigator.clipboard.writeText(`${global.config.baseUrl}/share/${getShareId(brew)}#p${currentPage}`);}}>
copy url (page {currentPage})
</Nav.item>}
<Nav.item color='blue' href={getRedditLink(brew)} newTab rel='noopener noreferrer'> <Nav.item color='blue' href={getRedditLink(brew)} newTab rel='noopener noreferrer'>
post to reddit post to reddit
</Nav.item> </Nav.item>
+9 -11
View File
@@ -90,7 +90,7 @@ const EditPage = (props)=>{
const handleControlKeys = (e)=>{ const handleControlKeys = (e)=>{
if(!(e.ctrlKey || e.metaKey)) return; if(!(e.ctrlKey || e.metaKey)) return;
if(e.keyCode === 83) trySaveRef.current(true); if(e.keyCode === 83) trySaveRef.current(true, true, saveGoogle);
if(e.keyCode === 80) printCurrentBrew(); if(e.keyCode === 80) printCurrentBrew();
if([83, 80].includes(e.keyCode)) { if([83, 80].includes(e.keyCode)) {
e.stopPropagation(); e.stopPropagation();
@@ -118,13 +118,9 @@ const EditPage = (props)=>{
const hasChange = !_.isEqual(currentBrew, lastSavedBrew.current); const hasChange = !_.isEqual(currentBrew, lastSavedBrew.current);
setUnsavedChanges(hasChange); setUnsavedChanges(hasChange);
if(autoSaveEnabled) trySave(false, hasChange); if(autoSaveEnabled) trySave(false, hasChange, saveGoogle);
}, [currentBrew]); }, [currentBrew]);
useEffect(()=>{
trySave(true);
}, [saveGoogle]);
const handleSplitMove = ()=>{ const handleSplitMove = ()=>{
editorRef.current?.update(); editorRef.current?.update();
}; };
@@ -183,11 +179,13 @@ const EditPage = (props)=>{
}; };
const toggleGoogleStorage = ()=>{ const toggleGoogleStorage = ()=>{
const newSaveGoogle = !saveGoogle;
setSaveGoogle((prev)=>!prev); setSaveGoogle((prev)=>!prev);
setError(null); setError(null);
trySave(true, true, newSaveGoogle);
}; };
const trySave = (immediate = false, hasChanges = true)=>{ const trySave = (immediate = false, hasChanges = true, saveToGoogle = false)=>{
clearTimeout(saveTimeout.current); clearTimeout(saveTimeout.current);
if(isSaving) return; if(isSaving) return;
if(!hasChanges && !immediate) return; if(!hasChanges && !immediate) return;
@@ -196,7 +194,7 @@ const EditPage = (props)=>{
saveTimeout.current = setTimeout(async ()=>{ saveTimeout.current = setTimeout(async ()=>{
setIsSaving(true); setIsSaving(true);
setError(null); setError(null);
await save(currentBrew, saveGoogle) await save(currentBrew, saveToGoogle)
.catch((err)=>{ .catch((err)=>{
setError(err); setError(err);
}); });
@@ -216,7 +214,7 @@ const EditPage = (props)=>{
const brewToSave = { const brewToSave = {
...brew, ...brew,
text : brew.text.normalize('NFC'), text : brew.text.normalize('NFC'),
pageCount : ((brew.renderer === 'legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1, pageCount : ((brew.renderer === 'legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/gm)) || []).length + 1,
patches : stringifyPatches(makePatches(encodeURI(lastSavedBrew.current.text.normalize('NFC')), encodeURI(brew.text.normalize('NFC')))), patches : stringifyPatches(makePatches(encodeURI(lastSavedBrew.current.text.normalize('NFC')), encodeURI(brew.text.normalize('NFC')))),
hash : await md5(lastSavedBrew.current.text.normalize('NFC')), hash : await md5(lastSavedBrew.current.text.normalize('NFC')),
textBin : undefined, textBin : undefined,
@@ -314,7 +312,7 @@ const EditPage = (props)=>{
// #3 - Unsaved changes exist, click to save, show SAVE NOW // #3 - Unsaved changes exist, click to save, show SAVE NOW
if(unsavedChanges) if(unsavedChanges)
return <Nav.item className='save' onClick={()=>trySave(true)} color='blue' icon='fas fa-save'>save now</Nav.item>; return <Nav.item className='save' onClick={()=>trySave(true, true, saveGoogle)} color='blue' icon='fas fa-save'>save now</Nav.item>;
// #4 - No unsaved changes, autosave is ON, show AUTO-SAVED // #4 - No unsaved changes, autosave is ON, show AUTO-SAVED
if(autoSaveEnabled) if(autoSaveEnabled)
@@ -365,7 +363,7 @@ const EditPage = (props)=>{
<PrintNavItem /> <PrintNavItem />
<HelpNavItem /> <HelpNavItem />
<VaultNavItem /> <VaultNavItem />
<ShareNavItem brew={currentBrew} /> <ShareNavItem brew={currentBrew} currentPage={currentBrewRendererPageNum} />
<RecentNavItem brew={currentBrew} storageKey='edit' /> <RecentNavItem brew={currentBrew} storageKey='edit' />
<AccountNavItem/> <AccountNavItem/>
</Nav.section> </Nav.section>
+1 -1
View File
@@ -156,7 +156,7 @@ const NewPage = (props)=>{
const updatedBrew = { ...currentBrew }; const updatedBrew = { ...currentBrew };
splitTextStyleAndMetadata(updatedBrew); splitTextStyleAndMetadata(updatedBrew);
const pageRegex = updatedBrew.renderer === 'legacy' ? /\\page/g : /^\\page$/gm; const pageRegex = updatedBrew.renderer === 'legacy' ? /\\page/g : /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/gm;
updatedBrew.pageCount = (updatedBrew.text.match(pageRegex) || []).length + 1; updatedBrew.pageCount = (updatedBrew.text.match(pageRegex) || []).length + 1;
const res = await request const res = await request
@@ -92,6 +92,19 @@ const SharePage = (props)=>{
<Nav.item color='blue' icon='fas fa-clone' href={`/new/${processShareId()}`}> <Nav.item color='blue' icon='fas fa-clone' href={`/new/${processShareId()}`}>
clone to new clone to new
</Nav.item> </Nav.item>
<Nav.item
color='blue'
icon='fas fa-link'
onClick={()=>{navigator.clipboard.writeText(`${global.config.baseUrl}/share/${processShareId()}`);}}>
copy url
</Nav.item>
{currentBrewRendererPageNum > 1 &&
<Nav.item
color='blue'
icon='fas fa-hashtag'
onClick={()=>{navigator.clipboard.writeText(`${global.config.baseUrl}/share/${processShareId()}#p${currentBrewRendererPageNum}`);}}>
copy url (page {currentBrewRendererPageNum})
</Nav.item>}
</Nav.dropdown> </Nav.dropdown>
</> </>
)} )}
+457 -914
View File
File diff suppressed because it is too large Load Diff
+15 -15
View File
@@ -58,7 +58,7 @@
"server" "server"
], ],
"transformIgnorePatterns": [ "transformIgnorePatterns": [
"node_modules/(?!(nanoid|@exodus/bytes|parse5|@asamuzakjp|@csstools)/)" "node_modules/(?!(nanoid|@exodus/bytes|parse5|@asamuzakjp|@csstools|entities)/)"
], ],
"transform": { "transform": {
"^.+\\.[jt]s$": "babel-jest", "^.+\\.[jt]s$": "babel-jest",
@@ -88,10 +88,10 @@
"dependencies": { "dependencies": {
"@babel/core": "^7.29.0", "@babel/core": "^7.29.0",
"@babel/plugin-transform-runtime": "^7.29.0", "@babel/plugin-transform-runtime": "^7.29.0",
"@babel/preset-env": "^7.29.2", "@babel/preset-env": "^7.29.5",
"@babel/preset-react": "^7.28.5", "@babel/preset-react": "^7.28.5",
"@babel/runtime": "^7.29.2", "@babel/runtime": "^7.29.2",
"@codemirror/autocomplete": "^6.20.1", "@codemirror/autocomplete": "^6.20.2",
"@codemirror/commands": "^6.10.3", "@codemirror/commands": "^6.10.3",
"@codemirror/highlight": "^0.19.8", "@codemirror/highlight": "^0.19.8",
"@codemirror/lang-css": "^6.3.1", "@codemirror/lang-css": "^6.3.1",
@@ -101,15 +101,15 @@
"@codemirror/language-data": "^6.5.2", "@codemirror/language-data": "^6.5.2",
"@codemirror/search": "^6.6.0", "@codemirror/search": "^6.6.0",
"@codemirror/state": "^6.6.0", "@codemirror/state": "^6.6.0",
"@codemirror/view": "^6.40.0", "@codemirror/view": "^6.43.0",
"@dmsnell/diff-match-patch": "^1.1.0", "@dmsnell/diff-match-patch": "^1.1.0",
"@googleapis/drive": "^20.1.0", "@googleapis/drive": "^20.1.0",
"@lezer/highlight": "^1.2.3", "@lezer/highlight": "^1.2.3",
"@sanity/diff-match-patch": "^3.2.0", "@sanity/diff-match-patch": "^3.2.0",
"@uiw/codemirror-themes-all": "^4.25.8",
"@vitejs/plugin-react": "^5.1.2", "@vitejs/plugin-react": "^5.1.2",
"body-parser": "^2.2.0", "body-parser": "^2.2.0",
"classnames": "^2.5.1", "classnames": "^2.5.1",
"codemirror-5-themes": "^1.5.1",
"cookie-parser": "^1.4.7", "cookie-parser": "^1.4.7",
"core-js": "^3.49.0", "core-js": "^3.49.0",
"cors": "^2.8.5", "cors": "^2.8.5",
@@ -117,9 +117,9 @@
"dedent": "^1.7.1", "dedent": "^1.7.1",
"express": "^5.1.0", "express": "^5.1.0",
"express-async-handler": "^1.2.0", "express-async-handler": "^1.2.0",
"express-static-gzip": "3.0.0", "express-static-gzip": "3.0.1",
"fflate": "^0.8.2", "fflate": "^0.8.2",
"fs-extra": "^11.3.3", "fs-extra": "^11.3.5",
"hash-wasm": "^4.12.0", "hash-wasm": "^4.12.0",
"idb-keyval": "^6.2.2", "idb-keyval": "^6.2.2",
"js-yaml": "^4.1.1", "js-yaml": "^4.1.1",
@@ -138,31 +138,31 @@
"marked-variables": "^1.0.5", "marked-variables": "^1.0.5",
"markedLegacy": "npm:marked@^0.3.19", "markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.30.1", "moment": "^2.30.1",
"mongoose": "^9.3.3", "mongoose": "^9.6.2",
"nanoid": "5.1.7", "nanoid": "5.1.11",
"nconf": "^0.13.0", "nconf": "^0.13.0",
"node": "^25.9.0", "node": "^25.9.0",
"react": "^19.2.4", "react": "^19.2.6",
"react-dom": "^19.2.4", "react-dom": "^19.2.6",
"react-frame-component": "^5.3.2", "react-frame-component": "^5.3.2",
"react-router": "^7.14.0", "react-router": "^7.15.1",
"sanitize-filename": "1.6.4", "sanitize-filename": "1.6.4",
"superagent": "^10.2.1" "superagent": "^10.2.1"
}, },
"devDependencies": { "devDependencies": {
"@stylistic/stylelint-plugin": "^5.0.1", "@stylistic/stylelint-plugin": "^5.0.1",
"babel-jest": "^30.3.0", "babel-jest": "^30.4.1",
"babel-plugin-transform-import-meta": "^2.3.3", "babel-plugin-transform-import-meta": "^2.3.3",
"eslint": "9.7", "eslint": "9.7",
"eslint-plugin-jest": "^29.15.1", "eslint-plugin-jest": "^29.15.1",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"globals": "^16.4.0", "globals": "^16.4.0",
"jest": "^30.3.0", "jest": "^30.4.2",
"jest-expect-message": "^1.1.3", "jest-expect-message": "^1.1.3",
"jsdom": "^28.1.0", "jsdom": "^28.1.0",
"jsdom-global": "^3.0.2", "jsdom-global": "^3.0.2",
"postcss-less": "^6.0.0", "postcss-less": "^6.0.0",
"stylelint": "^17.6.0", "stylelint": "^17.11.1",
"stylelint-config-recess-order": "^7.7.0", "stylelint-config-recess-order": "^7.7.0",
"stylelint-config-recommended": "^18.0.0", "stylelint-config-recommended": "^18.0.0",
"supertest": "^7.1.4", "supertest": "^7.1.4",
+1 -1
View File
@@ -593,7 +593,7 @@ export default async function createApp(vite) {
html = html.replace( html = html.replace(
'<head>', '<head>',
`<head>\n<script id="props" >window.__INITIAL_PROPS__ = ${JSON.stringify(props)}</script>\n${ogMetaTags}` ()=>{ return `<head>\n<script id="props" >window.__INITIAL_PROPS__ = ${JSON.stringify(props)}</script>\n${ogMetaTags}`; }
); );
return html; return html;
+18 -11
View File
@@ -32,20 +32,20 @@ const isStaticTheme = (renderer, themeName)=>{
// }; // };
const migrateSystemsToTags = (brew) => { const migrateSystemsToTags = (brew)=>{
if (!('systems' in brew)) return brew; if(!('systems' in brew)) return brew;
if (!Array.isArray(brew.systems) || brew.systems.length === 0) { if(!Array.isArray(brew.systems) || brew.systems.length === 0) {
brew.systems = undefined; brew.systems = undefined;
return brew; return brew;
} }
const systemMap = { const systemMap = {
'5e': 'system:D&D 5e', '5e' : 'system:D&D 5e',
'4e': 'system:D&D 4e', '4e' : 'system:D&D 4e',
'3.5e': 'system:D&D 3.5e', '3.5e' : 'system:D&D 3.5e',
'Pathfinder': 'system:Pathfinder 2e' 'Pathfinder' : 'system:Pathfinder 2e'
}; };
const systemTags = brew.systems.map(s => systemMap[s]); const systemTags = brew.systems.map((s)=>systemMap[s]);
brew.tags = _.uniq([...(brew.tags || []), ...systemTags]); brew.tags = _.uniq([...(brew.tags || []), ...systemTags]);
brew.systems = undefined; brew.systems = undefined;
@@ -397,17 +397,24 @@ const api = {
return res.status(409).send(JSON.stringify({ message: `The server copy is out of sync with the saved brew. Please save your changes elsewhere, refresh, and try again.` })); return res.status(409).send(JSON.stringify({ message: `The server copy is out of sync with the saved brew. Please save your changes elsewhere, refresh, and try again.` }));
} }
let result = [];
try { try {
const patches = parsePatch(brewFromClient.patches); const patches = parsePatch(brewFromClient.patches);
// Patch to a throwaway variable while parallelizing - we're more concerned with error/no error. // Patch to a throwaway variable while parallelizing - we're more concerned with error/no error.
const patchedResult = decodeURI(applyPatches(patches, encodeURI(brewFromServer.text))[0]); result = applyPatches(patches, encodeURI(brewFromServer.text));
if(patchedResult != brewFromClient.text) const failedPatches = patches.map((patch, index)=>{if(!result[1][index]){ return patch; }});
if(failedPatches > 0){
throw (`Patch failure: ${failedPatches}/${result[1].length} did not apply`);
}
if(decodeURI(result[0]) != brewFromClient.text){
throw ('Patches did not apply cleanly, text mismatch detected'); throw ('Patches did not apply cleanly, text mismatch detected');
}
// brew.text = applyPatches(patches, brewFromServer.text)[0]; // brew.text = applyPatches(patches, brewFromServer.text)[0];
} catch (err) { } catch (err) {
debugTextMismatch(brewFromClient.text, brewFromServer.text, `edit/${brewFromClient.editId}`); debugTextMismatch(brewFromClient.text, brewFromServer.text, `edit/${brewFromClient.editId}`);
console.error('Failed to apply patches:', { console.error('Failed to apply patches:', {
//patches : brewFromClient.patches, // patches : brewFromClient.patches,
// result : result,
brewId : brewFromClient.editId || 'unknown', brewId : brewFromClient.editId || 'unknown',
error : err error : err
}); });
+1 -1
View File
@@ -15,7 +15,7 @@ const HomebrewSchema = mongoose.Schema({
description : { type: String, default: '' }, description : { type: String, default: '' },
tags : { type: [String], index: true }, tags : { type: [String], index: true },
systems : { type: [String], default: undefined }, systems : { type: [String], default: undefined },
lang : { type: String, default: 'en', index: true }, lang : { type: String, default: 'en', index: true },
renderer : { type: String, default: '', index: true }, renderer : { type: String, default: '', index: true },
authors : { type: [String], index: true }, authors : { type: [String], index: true },
+28 -2
View File
@@ -160,9 +160,35 @@ const debugTextMismatch = (clientTextRaw, serverTextRaw, label)=>{
// Char-level diff // Char-level diff
for (let i = 0; i < Math.min(clientText.length, serverText.length); i++) { for (let i = 0; i < Math.min(clientText.length, serverText.length); i++) {
if(clientText[i] !== serverText[i]) { if(clientText[i] !== serverText[i]) {
const getMismatchContext = (text, index, name, size = 10)=>{
const lower = Math.max(index - size, 0);
const upper = Math.min(index + size, text.length);
const slice = `${JSON.stringify(text.slice(lower, index)).slice(1, -1)}\u001B[31m${JSON.stringify(text[i]).slice(1, -1)}\u001B[0m${JSON.stringify(text.slice(index+1, upper)).slice(1, -1)}`;
const lineNo = text.slice(0, index).split('\n').length;
const code = `U+${text.charCodeAt(i).toString(16).toUpperCase()}`;
return {
name,
lineNo,
code,
lower,
upper,
slice
};
};
const boundSize = 10;
const clientContext = getMismatchContext(clientText, i, 'Client', boundSize);
const serverContext = getMismatchContext(serverText, i, 'Server', boundSize);
const logContext = (context)=>{
console.log(` ${context.name} - line ${context.lineNo} : (${context.code})\t${context.slice}`);
};
console.log(`Char mismatch at index ${i}:`); console.log(`Char mismatch at index ${i}:`);
console.log(` Client: '${clientText[i]}' (U+${clientText.charCodeAt(i).toString(16).toUpperCase()})`); logContext(clientContext);
console.log(` Server: '${serverText[i]}' (U+${serverText.charCodeAt(i).toString(16).toUpperCase()})`); logContext(serverContext);
break; break;
} }
} }
+10 -10
View File
@@ -16,17 +16,17 @@ export default {
edge : (side = 'bottom')=>{ edge : (side = 'bottom')=>{
const styles = ()=>{ const styles = ()=>{
switch (side) { switch (side) {
case 'bottom': case 'bottom':
return `{width:100%,bottom:0%}` return `{width:100%,bottom:0%}`;
break; break;
case 'top': case 'top':
return `{width:100%,top:0%}` return `{width:100%,top:0%}`;
break; break;
default: default:
return `{height:100%}` return `{height:100%}`;
break; break;
} }
} };
const rotation = { const rotation = {
'bottom' : 0, 'bottom' : 0,
+5 -2
View File
@@ -54,9 +54,12 @@ export default EditorView.theme({
'.cm-activeLine' : { '.cm-activeLine' : {
backgroundColor : '#868c9323', backgroundColor : '#868c9323',
}, },
'.cm-selected' : { '.cm-selectionBackground' : {
backgroundColor : '#d7d4f0', backgroundColor : '#d7d4f0',
}, },
'&.cm-focused .cm-selectionBackground' : {
backgroundColor : '#d7d4f0 !important',
},
'.cm-pageLine' : { '.cm-pageLine' : {
backgroundColor : '#7ca97c', backgroundColor : '#7ca97c',
color : '#000', color : '#000',
@@ -90,7 +93,7 @@ export default EditorView.theme({
'.cm-strong' : { color: '#309dd2', fontWeight: 'bold' }, '.cm-strong' : { color: '#309dd2', fontWeight: 'bold' },
'.cm-em' : { fontStyle: 'italic' }, '.cm-em' : { fontStyle: 'italic' },
'.cm-keyword' : { color: '#fff' }, '.cm-keyword' : { color: '#fff' },
'.cm-atom, cm-value, cm-color' : { color: '#c1939a' }, '.cm-atom, .cm-value, .cm-color' : { color: '#c1939a' },
'.cm-number' : { color: '#2986cc' }, '.cm-number' : { color: '#2986cc' },
'.cm-def' : { color: '#2986cc' }, '.cm-def' : { color: '#2986cc' },
'.cm-list' : { color: '#3cbf30' }, '.cm-list' : { color: '#3cbf30' },
-126
View File
@@ -1,126 +0,0 @@
/*This document is old, from back when Codemirror was version 5,
if someone wants to update it, feel free, it needs to be like default.js or darkbrewery.js
Then imported in snippetbar.jsx and codeEditor.jsx.
*/
.CodeMirror {
background: #0C0C0C;
color: #B9BDB6;
}
/* Brew BG */
.brewRenderer {
background-color: #0C0C0C;
}
.cm-s-darkvision {
/* Blinking cursor and selection */
.CodeMirror-cursor {
border-left: 1px solid #B9BDB6;
}
.CodeMirror-selected {
background: #E0E8FF40;
}
/* Line number stuff */
.cm-gutter-elt {
color: #81969A;
}
.CodeMirror-linenumber {
background-color: #0C0C0C;
}
.cm-gutter {
background-color: #0C0C0C;
}
/* column splits */
.editor .codeEditor .columnSplit {
font-style: italic;
color: inherit;
background-color:#1F5763;
border-bottom: #299 solid 1px;
}
/* # headings */
.cm-header {
color: #C51B1B;
-webkit-text-stroke-width: 0.1px;
}
/* bold points */
.cm-strong {
font-weight: bold;
color: #309DD2;
}
/* Link headings */
.cm-link {
color: #DD6300;
}
/* links */
.cm-string {
color: #5CE638;
}
/*@import*/
.cm-def {
color: #2986CC;
}
/* Bullets and such */
.cm-variable-2 {
color: #3CBF30;
}
/* Tags (divs) */
.cm-tag {
color: #E3FF00;
}
.cm-attribute {
color: #E3FF00;
}
.cm-atom {
color: #CF7EA9;
}
.cm-qualifier {
color: #EE1919;
}
.cm-comment {
color: #BBC700;
}
.cm-keyword {
color: #CC66FF;
}
.cm-property {
color: aqua;
}
.cm-error {
color: #C50202;
}
.CodeMirror-foldmarker {
color: #F0FF00;
}
/* New page */
.cm-builtin {
color: #FFF;
}
}
.editor .codeEditor {
/* blocks */
.block:not(.cm-comment) {
color: magenta;
}
/* definition lists */
.define.definition {
color: #FFAA3E;
}
.define.term {
color: #7290d9;
}
.define:not(.term):not(.definition) {
background: #333;
}
/* New page */
.pageLine {
background: #000;
color: #000;
border-bottom: 1px solid #FFF;
}
}
+1 -1
View File
@@ -42,7 +42,7 @@ export default EditorView.theme({
'.cm-gutterElement.cm-activeLineGutter' : { '.cm-gutterElement.cm-activeLineGutter' : {
backgroundColor : '#becee374', backgroundColor : '#becee374',
}, },
'.cm-selected' : { '.cm-selectionBackground ' : {
backgroundColor : '#d7d4f0', backgroundColor : '#d7d4f0',
}, },
'.cm-foldmarker' : { '.cm-foldmarker' : {