0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-03-22 08:58:11 +00:00

basic css changes

This commit is contained in:
Víctor Losada Hernández
2026-03-21 22:53:38 +01:00
parent 3785d5808f
commit 42e254c9c7
8 changed files with 216 additions and 166 deletions

View File

@@ -6,138 +6,153 @@ import { foldGutter, foldKeymap } from "@codemirror/language";
import { EditorView, keymap, lineNumbers, highlightActiveLineGutter, highlightActiveLine } from "@codemirror/view"; import { EditorView, keymap, lineNumbers, highlightActiveLineGutter, highlightActiveLine } from "@codemirror/view";
import { markdown } from "@codemirror/lang-markdown"; import { markdown } from "@codemirror/lang-markdown";
import { css } from "@codemirror/lang-css"; import { css } from "@codemirror/lang-css";
import { oneDark } from "@codemirror/theme-one-dark";
const CodeEditor = forwardRef(({ value = "", onChange = () => {} }, ref) => { const CodeEditor = forwardRef(
const editorRef = useRef(null); ({ value = "", onChange = () => {}, language, editorTheme, tab, view, style, ...props }, ref) => {
const viewRef = useRef(null); const editorRef = useRef(null);
const viewRef = useRef(null);
// --- init editor --- console.log(props);
useEffect(() => {
if (!editorRef.current) return;
const updateListener = EditorView.updateListener.of((update) => { // --- init editor ---
if (update.docChanged) { useEffect(() => {
onChange(update.state.doc.toString()); if (!editorRef.current) return;
const updateListener = EditorView.updateListener.of((update) => {
if (update.docChanged) {
onChange(update.state.doc.toString());
}
});
const boldCommand = (view) => {
const { from, to } = view.state.selection.main;
const selected = view.state.doc.sliceString(from, to);
const text = `**${selected}**`;
view.dispatch({
changes: { from, to, insert: text },
selection: { anchor: from + text.length },
});
return true;
};
const italicCommand = (view) => {
const { from, to } = view.state.selection.main;
const selected = view.state.doc.sliceString(from, to);
const text = `*${selected}*`;
view.dispatch({
changes: { from, to, insert: text },
selection: { anchor: from + text.length },
});
return true;
};
const customKeymap = keymap.of([
{ key: "Mod-b", run: boldCommand },
{ key: "Mod-i", run: italicCommand },
]);
const languageExtension = () => {
switch (language) {
case "gfm":
return markdown({ codeLanguages: [] }); // GitHub-flavored Markdown
case "css":
return css();
default:
return markdown();
}
};
const state = EditorState.create({
doc: value,
extensions: [
history(),
keymap.of(defaultKeymap),
customKeymap,
updateListener,
languageExtension(),
highlightActiveLine(),
highlightActiveLineGutter(),
keymap.of(foldKeymap),
foldGutter(),
lineNumbers(),
oneDark,
],
});
viewRef.current = new EditorView({
state,
parent: editorRef.current,
});
return () => viewRef.current?.destroy();
}, []);
// --- sync external value ---
useEffect(() => {
const view = viewRef.current;
if (!view) return;
const current = view.state.doc.toString();
if (value !== current) {
view.dispatch({
changes: { from: 0, to: current.length, insert: value },
});
} }
}); }, [value]);
const boldCommand = (view) => { // --- exposed API ---
const { from, to } = view.state.selection.main; useImperativeHandle(ref, () => ({
const selected = view.state.doc.sliceString(from, to); getValue: () => viewRef.current.state.doc.toString(),
const text = `**${selected}**`;
view.dispatch({ setValue: (text) => {
changes: { from, to, insert: text }, const view = viewRef.current;
selection: { anchor: from + text.length }, view.dispatch({
}); changes: { from: 0, to: view.state.doc.length, insert: text },
});
},
return true; injectText: (text) => {
}; const view = viewRef.current;
const { from, to } = view.state.selection.main;
const italicCommand = (view) => { view.dispatch({
const { from, to } = view.state.selection.main; changes: { from, to, insert: text },
const selected = view.state.doc.sliceString(from, to); selection: { anchor: from + text.length },
const text = `*${selected}*`; });
view.dispatch({ view.focus();
changes: { from, to, insert: text }, },
selection: { anchor: from + text.length },
});
return true; getCursorPosition: () => viewRef.current.state.selection.main.head,
};
const customKeymap = keymap.of([ setCursorPosition: (pos) => {
{ key: "Mod-b", run: boldCommand }, viewRef.current.dispatch({ selection: { anchor: pos } });
{ key: "Mod-i", run: italicCommand }, viewRef.current.focus();
]); },
const state = EditorState.create({ undo: () => undo(viewRef.current),
doc: value, redo: () => redo(viewRef.current),
extensions: [
history(),
keymap.of(defaultKeymap),
customKeymap,
updateListener,
markdown(),
css(),
highlightActiveLine(),
highlightActiveLineGutter(),
keymap.of(foldKeymap),
foldGutter(),
lineNumbers(),
],
});
viewRef.current = new EditorView({ historySize: () => {
state, const view = viewRef.current;
parent: editorRef.current, if (!view) return { done: 0, undone: 0 };
});
return () => viewRef.current?.destroy(); const h = view.state.field(historyField, false);
}, []); if (!h) return { done: 0, undone: 0 };
// --- sync external value --- return { done: h.done.length, undone: h.undone.length };
useEffect(() => { },
const view = viewRef.current;
if (!view) return;
const current = view.state.doc.toString(); focus: () => viewRef.current.focus(),
if (value !== current) { }));
view.dispatch({
changes: { from: 0, to: current.length, insert: value },
});
}
}, [value]);
// --- exposed API --- return <div className="codeEditor" ref={editorRef} style={style} />;
useImperativeHandle(ref, () => ({ },
getValue: () => viewRef.current.state.doc.toString(), );
setValue: (text) => { export default CodeEditor;
const view = viewRef.current;
view.dispatch({
changes: { from: 0, to: view.state.doc.length, insert: text },
});
},
injectText: (text) => {
const view = viewRef.current;
const { from, to } = view.state.selection.main;
view.dispatch({
changes: { from, to, insert: text },
selection: { anchor: from + text.length },
});
view.focus();
},
getCursorPosition: () => viewRef.current.state.selection.main.head,
setCursorPosition: (pos) => {
viewRef.current.dispatch({ selection: { anchor: pos } });
viewRef.current.focus();
},
undo: () => undo(viewRef.current),
redo: () => redo(viewRef.current),
historySize: () => {
const view = viewRef.current;
if (!view) return { done: 0, undone: 0 };
const h = view.state.field(historyField, false);
if (!h) return { done: 0, undone: 0 };
return { done: h.done.length, undone: h.undone.length };
},
focus: () => viewRef.current.focus(),
}));
return <div ref={editorRef} style={{ height: "100%" }} />;
});
export default CodeEditor;

View File

@@ -1,60 +1,81 @@
@import (less) 'codemirror/lib/codemirror.css'; // Icon fonts for emoji/autocomplete
@import (less) 'codemirror/addon/fold/foldgutter.css'; @import (less) "@themes/fonts/iconFonts/diceFont.less";
@import (less) 'codemirror/addon/search/matchesonscrollbar.css'; @import (less) "@themes/fonts/iconFonts/elderberryInn.less";
@import (less) 'codemirror/addon/dialog/dialog.css'; @import (less) "@themes/fonts/iconFonts/gameIcons.less";
@import (less) 'codemirror/addon/hint/show-hint.css'; @import (less) "@themes/fonts/iconFonts/fontAwesome.less";
//Icon fonts included so they can appear in emoji autosuggest dropdown
@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 { @keyframes sourceMoveAnimation {
50% { color : white;background-color : red;} 50% {
100% { color : unset;background-color : unset;} color: white;
background-color: red;
}
100% {
color: unset;
background-color: unset;
}
} }
.codeEditor { .codeEditor {
@media screen and (pointer : coarse) { font-family: monospace;
font-size : 16px; height: 100%;
}
.CodeMirror-foldmarker { @media screen and (pointer: coarse) {
font-family : inherit; font-size: 16px;
font-weight : 600;
color : grey;
text-shadow : none;
} }
.CodeMirror-foldgutter { /* Line numbers and gutters */
cursor : pointer; .cm-gutters {
border-left : 1px solid #EEEEEE; background-color: #f0f0f0;
transition : background 0.1s; color: #555;
&:hover { background : #DDDDDD; } border-right: 1px solid #ddd;
} }
.sourceMoveFlash .CodeMirror-line { /* Folding gutter */
animation-name : sourceMoveAnimation; .cm-foldGutter {
animation-duration : 0.4s; cursor: pointer;
color: grey;
font-weight: 600;
transition: background 0.1s;
&:hover {
background: #dddddd;
}
} }
.CodeMirror-search-field { /* Active line */
width:25em !important; .cm-activeLine {
outline:1px inset #00000055 !important; background-color: #f5f5f5;
} }
.cm-activeLineGutter {
background-color: #e0e0e0;
}
/* Flash animation for source moves */
.sourceMoveFlash .cm-line {
animation-name: sourceMoveAnimation;
animation-duration: 0.4s;
}
/* Search input */
.cm-searchField {
width: 25em !important;
outline: 1px inset #00000055 !important;
}
/* Tab character visualization (optional) */
//.cm-tab { //.cm-tab {
// background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAQAAACOs/baAAAARUlEQVR4nGJgIAG8JkXxUAcCtDWemcGR1lY4MvgzCEKY7jSBjgxBDAG09UEQzAe0AMwMHrSOAwEGRtpaMIwAAAAA//8DAG4ID9EKs6YqAAAAAElFTkSuQmCC) no-repeat right; // background: url(...) no-repeat right;
//} //}
//.cm-trailingspace { /* Trailing space visualization (optional) */
// .cm-space { //.cm-trailingSpace .cm-space {
// background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQAgMAAABW5NbuAAAACVBMVEVHcEwAAAAAAAAWawmTAAAAA3RSTlMAPBJ6PMxpAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAFUlEQVQI12NgwACcCQysASAEZGAAACMuAX06aCQUAAAAAElFTkSuQmCC) no-repeat right; // background: url(...) no-repeat right;
// }
//} //}
} }
/* Emoji preview styling */
.emojiPreview { .emojiPreview {
font-size : 1.5em; font-size: 1.5em;
line-height : 1.2em; line-height: 1.2em;
} }

View File

@@ -9,7 +9,7 @@
background:white; background:white;
.codeEditor { .codeEditor {
height : calc(100% - 25px); height : calc(100% - 25px);
.CodeMirror { height : 100%; } .cm-editor { height : 100%; }
.pageLine, .snippetLine { .pageLine, .snippetLine {
background : #33333328; background : #33333328;
border-top : #333399 solid 1px; border-top : #333399 solid 1px;

13
package-lock.json generated
View File

@@ -21,6 +21,7 @@
"@codemirror/lang-markdown": "^6.5.0", "@codemirror/lang-markdown": "^6.5.0",
"@codemirror/language": "^6.12.2", "@codemirror/language": "^6.12.2",
"@codemirror/state": "^6.6.0", "@codemirror/state": "^6.6.0",
"@codemirror/theme-one-dark": "^6.1.3",
"@codemirror/view": "^6.40.0", "@codemirror/view": "^6.40.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",
@@ -2173,6 +2174,18 @@
"@marijn/find-cluster-break": "^1.0.0" "@marijn/find-cluster-break": "^1.0.0"
} }
}, },
"node_modules/@codemirror/theme-one-dark": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz",
"integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==",
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"@lezer/highlight": "^1.0.0"
}
},
"node_modules/@codemirror/view": { "node_modules/@codemirror/view": {
"version": "6.40.0", "version": "6.40.0",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.40.0.tgz", "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.40.0.tgz",

View File

@@ -97,6 +97,7 @@
"@codemirror/lang-markdown": "^6.5.0", "@codemirror/lang-markdown": "^6.5.0",
"@codemirror/language": "^6.12.2", "@codemirror/language": "^6.12.2",
"@codemirror/state": "^6.6.0", "@codemirror/state": "^6.6.0",
"@codemirror/theme-one-dark": "^6.1.3",
"@codemirror/view": "^6.40.0", "@codemirror/view": "^6.40.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",

View File

@@ -1,4 +1,4 @@
.editor .codeEditor .CodeMirror { .editor .codeEditor .cm-editor {
// Themes with dark backgrounds // Themes with dark backgrounds
&.cm-s-3024-night, &.cm-s-3024-night,
&.cm-s-abbott, &.cm-s-abbott,

View File

@@ -15,11 +15,11 @@
--highlight: #bcbcbc; --highlight: #bcbcbc;
color: #91A6AA; color: #91A6AA;
background: var(--bg); background: var(--bg);
.CodeMirror-scroll { .cm-scroller {
.CodeMirror-gutters { .cm-gutters {
border-right: 1px solid #555; border-right: 1px solid #555;
background: var(--bg); background: var(--bg);
.CodeMirror-gutter { .cm-gutter {
background-color: var(--bg); background-color: var(--bg);
&.CodeMirror-foldgutter { &.CodeMirror-foldgutter {
cursor: pointer; cursor: pointer;
@@ -31,9 +31,9 @@
} }
} }
} }
.CodeMirror-lines { .cm-content {
/* Line numbers*/ /* Line numbers*/
.CodeMirror-linenumber.CodeMirror-gutter-elt { .CodeMirror-linenumber.cm-gutter-elt {
background-color: var(--bg); background-color: var(--bg);
color: #81969A; color: #81969A;
} }

View File

@@ -18,13 +18,13 @@
} }
/* Line number stuff */ /* Line number stuff */
.CodeMirror-gutter-elt { .cm-gutter-elt {
color: #81969A; color: #81969A;
} }
.CodeMirror-linenumber { .CodeMirror-linenumber {
background-color: #0C0C0C; background-color: #0C0C0C;
} }
.CodeMirror-gutter { .cm-gutter {
background-color: #0C0C0C; background-color: #0C0C0C;
} }