Merge branch 'master' into hbfm

This commit is contained in:
Víctor Losada Hernández
2026-08-29 23:45:50 +02:00
committed by GitHub
9 changed files with 3997 additions and 2090 deletions
+5 -3
View File
@@ -43,9 +43,9 @@ const themes = { default: defaultCM5Theme, ...cm5Themes, darkbrewery };
const themeCompartment = new Compartment();
const highlightCompartment = new Compartment();
import { generalKeymap, markdownKeymap } from './extensions/customKeyMaps.js';
import { generalKeymap, markdownKeymap, cssKeymap, formatCSS } from './extensions/customKeyMaps.js';
import foldOnPages from './extensions/customFolding.js';
import { customHighlightPlugin, customHighlightStyle } from './extensions/customHighlight.js';
import { customHighlightStyle , customHighlightPlugin } from './extensions/customHighlight.js';
import { legacyCustomHighlightStyle } from './extensions/legacyCustomHighlight.js';
const PAGEBREAK_REGEX_V3 = /^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/m;
@@ -186,7 +186,7 @@ const CodeEditor = forwardRef(
//keyboard shortcut
keymap.of([...defaultKeymap, foldKeymap, ...searchKeymap]),
generalKeymap,
...(tab !== 'brewStyles' ? [markdownKeymap] : []),
...(tab === 'brewStyles' ? [cssKeymap] : [markdownKeymap]),
//multiple cursors and selections
drawSelection(),
@@ -356,6 +356,8 @@ const CodeEditor = forwardRef(
}, 400);
},
formatCode : ()=>formatCSS(viewRef.current),
undo : ()=>undo(viewRef.current),
redo : ()=>redo(viewRef.current),
@@ -15,6 +15,18 @@
}
}
@keyframes slideacross {
0% {
bottom: -200px;
left: -200px;
}
100% {
bottom:100%;
left:100%;
}
}
:where(.codeEditor) {
width : 100%;
height : calc(100% - 25px);
@@ -23,6 +35,27 @@
.cm-editor {
height : 100%;
outline : none !important;
&.cm-flash {
position:relative;
&::after {
position:absolute;
content:'';
bottom:-200px;
left:-200px;
translate:-50%;
width:200%;
height:100px;
background:linear-gradient(0deg, #89eafc00 0px, #89ebfc8e 50px, #89eafc00 100px, transparent);
display:block;
rotate:30deg;
@media screen and (prefers-reduced-motion: no-preference) {
animation: .5s linear 1 slideacross ;
}
}
}
}
&.brewSnippets .cm-snippetLine,
@@ -350,11 +350,10 @@ class ImageWidget extends WidgetType {
toDOM() {
const img = document.createElement('img');
img.loading = "lazy";
img.loading = 'lazy';
img.className = 'cm-preview';
img.src = this.url;
img.onerror = ()=>{
img.src = 'client/icons/broken-image.jpg';
};
@@ -3,7 +3,60 @@ import { keymap } from '@codemirror/view';
import { undo, redo, indentMore, indentLess, deleteLine } from '@codemirror/commands';
import { EditorSelection } from '@codemirror/state';
import { Prec } from '@codemirror/state';
import * as prettier from 'prettier/standalone';
import * as postcssPlugin from 'prettier/plugins/postcss';
export async function formatCSS(view) {
try {
const { from, to, empty } = view.state.selection.main;
const fullDoc = view.state.doc.toString();
const selection = view.state.doc.sliceString(from, to);
const code = empty ? fullDoc : selection;
let formatted = await prettier.format(code, {
parser: 'css',
plugins: [postcssPlugin],
// formatting options
tabWidth: 2,
useTabs: false,
printWidth: 100,
singleQuote: false,
trailingComma: 'all',
bracketSpacing: true,
endOfLine: 'lf'
});
//format manually single declaration rules to span one line.
//Prettier can't do it by default, this is crude but it works
formatted = formatted.replace(
/([^{]+)\{\s*\n\s*([^;\n]+:[^;\n]+;)\s*\n\s*\}(\s*)/g,
(_, selector, decl, whitespace) =>
`${selector} { ${decl.trim()} }${whitespace}`
);
if(formatted === code) return true;
const dom = view.dom;
dom.classList.add('cm-flash');
setTimeout(()=>{
dom.classList.remove('cm-flash');
view.dispatch({
changes : {
from : empty ? 0 : from,
to : empty ? view.state.doc.length : to,
insert : formatted
}
});
}, 500);
} catch (err) {
console.error('Error formatting css: ', err);
}
return true;
}
const insertTab = (view)=>{
// If any selection spans multiple lines, delegates to CodeMirror's indentMore
// Otherwise inserts two spaces at each cursor/selection
@@ -188,6 +241,11 @@ export const generalKeymap = Prec.high(keymap.of([
{ key: 'Mod-d', run: deleteLine }, //annoyingly overrides "selectNextOccurrence" because users asked
]));
export const cssKeymap = Prec.highest(keymap.of([
{ key: 'Mod-Shift-f', run: formatCSS },
{ key: 'Alt-Shift-f', run: formatCSS },
]));
export const markdownKeymap = Prec.highest(keymap.of([
{ key: 'Mod-b', run: wrapSelection('**', '**') }, // makeBold
+5
View File
@@ -111,6 +111,10 @@ const Editor = forwardRef(
useEffect(()=>{ if(liveScroll) brewJump(currentEditorViewPageNum, false); }, [currentEditorViewPageNum, liveScroll]);
useEffect(()=>{ if(liveScroll) brewJump(currentEditorCursorPageNum, false); }, [currentEditorCursorPageNum, liveScroll]);
const handleFormatCode = () => {
codeEditor.current?.formatCode();
};
const handleControlKeys = (e)=>{
if(!(e.ctrlKey && e.metaKey && e.shiftKey)) return;
const LEFTARROW_KEY = 37;
@@ -319,6 +323,7 @@ const Editor = forwardRef(
redo={redo}
foldCode={foldCode}
unfoldCode={unfoldCode}
formatCode={isStyle() ? handleFormatCode : null}
historySize={historySize()}
currentEditorTheme={currentEditorTheme}
updateEditorTheme={updateEditorTheme}
@@ -65,6 +65,7 @@ const Snippetbar = createReactClass({
historySize : ()=>{},
foldCode : ()=>{},
unfoldCode : ()=>{},
formatCode : ()=>{},
updateEditorTheme : ()=>{},
cursorPos : {},
themeBundle : [],
@@ -269,6 +270,10 @@ const Snippetbar = createReactClass({
onClick={this.props.unfoldCode} >
<i className='fas fa-expand-alt' />
</div>
<div className={`editorTool formatCode ${this.props.formatCode ? 'active' : ''}`}
onClick={this.props.formatCode} >
<i className='fas fa-wand-magic-sparkles' />
</div>
<div className={`editorTheme ${this.state.themeSelector ? 'active' : ''}`}
onClick={this.toggleThemeSelector} >
<i className='fas fa-palette' />
@@ -21,7 +21,7 @@
.editors {
display : flex;
justify-content : flex-end;
min-width : 250px; //must be controlled every time an item is added, must be hardcoded for the wrapping as it is applied
min-width : 275px; //must be controlled every time an item is added, must be hardcoded for the wrapping as it is applied
font-size: .85rem;
&:only-child {min-width : unset; margin-left : auto;}
@@ -75,6 +75,11 @@
color : grey;
&.active { color : inherit; }
}
&.formatCode {
.tooltipLeft('Clean your Code');
color : grey;
&.active { color : inherit; }
}
&.history {
.tooltipLeft('History');
position : relative;
@@ -208,7 +213,7 @@
}
}
}
@container editor (width < 816px) {
@container editor (width < 841px) {
.snippetBar {
.editors {
flex : 1;
+3877 -2078
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -145,6 +145,7 @@
"nanoid": "6.0.1",
"nconf": "^0.13.0",
"node": "^26.7.0",
"prettier": "^3.8.1",
"react": "^19.2.7",
"react-dom": "^19.2.7",
"react-frame-component": "^5.3.2",