From 49e4f0a597cddc06d2a7ee14ffd6aa9d0780278a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 22 Apr 2026 12:34:50 +0200 Subject: [PATCH 1/3] css reordering --- client/components/codeEditor/codeEditor.less | 147 +++++++++++++++--- .../components/codeEditor/customHighlight.js | 2 - client/homebrew/editor/editor.less | 110 ------------- 3 files changed, 125 insertions(+), 134 deletions(-) diff --git a/client/components/codeEditor/codeEditor.less b/client/components/codeEditor/codeEditor.less index 3f3869756..84ed8058d 100644 --- a/client/components/codeEditor/codeEditor.less +++ b/client/components/codeEditor/codeEditor.less @@ -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; } diff --git a/client/components/codeEditor/customHighlight.js b/client/components/codeEditor/customHighlight.js index 3fa164757..345e253d3 100644 --- a/client/components/codeEditor/customHighlight.js +++ b/client/components/codeEditor/customHighlight.js @@ -125,8 +125,6 @@ export function tokenizeCustomMarkdown(text) { from : offset, to : offset + desc.length, }); - - return; } // --- multiline def list --- diff --git a/client/homebrew/editor/editor.less b/client/homebrew/editor/editor.less index 7503749fc..a55fad852 100644 --- a/client/homebrew/editor/editor.less +++ b/client/homebrew/editor/editor.less @@ -6,116 +6,6 @@ height : 100%; container : editor / inline-size; 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 { position : absolute; From f863871173b4d92ddd4ded6a8690403fa3b0c9e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 22 Apr 2026 17:05:17 +0200 Subject: [PATCH 2/3] move darkvision to library, and sort themes --- client/components/codeEditor/customKeyMaps.js | 4 +- .../homebrew/editor/snippetbar/snippetbar.jsx | 9 +- package-lock.json | 8 +- package.json | 2 +- themes/codeMirror/darkvision.css | 126 ------------------ 5 files changed, 15 insertions(+), 134 deletions(-) delete mode 100644 themes/codeMirror/darkvision.css diff --git a/client/components/codeEditor/customKeyMaps.js b/client/components/codeEditor/customKeyMaps.js index 7a9f64d6d..86b108ac3 100644 --- a/client/components/codeEditor/customKeyMaps.js +++ b/client/components/codeEditor/customKeyMaps.js @@ -188,13 +188,13 @@ 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 = Prec.highest(keymap.of([ //{ key: 'Shift-Tab', run: indentMore }, diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index 6beff1510..07a20fa08 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -29,7 +29,7 @@ import cm5Themes from 'codemirror-5-themes'; const themes = { default: defaultCM5Theme, ...cm5Themes, darkbrewery }; -const EditorThemes = Object.entries(themes) +const themeNames = Object.entries(themes) .filter(([name, value]) => Array.isArray(value) && !name.endsWith('Init') && @@ -37,6 +37,13 @@ const EditorThemes = Object.entries(themes) ) .map(([name]) => name); +const EditorThemes = [ + 'default', + ...themeNames + .filter(name => name !== 'default') + .sort((a, b) => a.localeCompare(b)) +]; + const execute = function(val, props){ if(_.isFunction(val)) return val(props); return val; diff --git a/package-lock.json b/package-lock.json index fc582c2f9..565fe43bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "@vitejs/plugin-react": "^5.1.2", "body-parser": "^2.2.0", "classnames": "^2.5.1", - "codemirror-5-themes": "^1.3.0", + "codemirror-5-themes": "^1.4.0", "cookie-parser": "^1.4.7", "core-js": "^3.49.0", "cors": "^2.8.5", @@ -6094,9 +6094,9 @@ } }, "node_modules/codemirror-5-themes": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/codemirror-5-themes/-/codemirror-5-themes-1.3.0.tgz", - "integrity": "sha512-FO8HG4m4GdcphVtJFcj8wcx9nhktb4UMnSu8ia5yCNd3G89pBGqrIxw2UAEluJP4D2gLZWQOwdPaU8Ik3Bqt2w==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/codemirror-5-themes/-/codemirror-5-themes-1.4.0.tgz", + "integrity": "sha512-3WKAmpTLqcE1MXFLHdNtwQYaxlWZXS69MY79UiNsFpW9KedMkf/vBYBjUAmacDXPKAJjdPSNMGmCPIZi21yoXA==", "dependencies": { "@codemirror/view": "^6.41.1" } diff --git a/package.json b/package.json index e1d15eebe..b25885497 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "@vitejs/plugin-react": "^5.1.2", "body-parser": "^2.2.0", "classnames": "^2.5.1", - "codemirror-5-themes": "^1.3.0", + "codemirror-5-themes": "^1.4.0", "cookie-parser": "^1.4.7", "core-js": "^3.49.0", "cors": "^2.8.5", diff --git a/themes/codeMirror/darkvision.css b/themes/codeMirror/darkvision.css deleted file mode 100644 index c88455c91..000000000 --- a/themes/codeMirror/darkvision.css +++ /dev/null @@ -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; - } -} From 7139993e159bc50172f5d939f9f36fdfc4732add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 22 Apr 2026 17:32:26 +0200 Subject: [PATCH 3/3] repair shortcuts to put the cursor at the center if there is no selection --- client/components/codeEditor/customKeyMaps.js | 122 ++++++++++++++---- 1 file changed, 96 insertions(+), 26 deletions(-) diff --git a/client/components/codeEditor/customKeyMaps.js b/client/components/codeEditor/customKeyMaps.js index 86b108ac3..7da06b119 100644 --- a/client/components/codeEditor/customKeyMaps.js +++ b/client/components/codeEditor/customKeyMaps.js @@ -20,72 +20,142 @@ const indentLess = (view)=>{ const makeBold = (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, cursor; + + if(from === to) { + text = '****'; + cursor = from + 2; + } else if(selected.startsWith('**') && selected.endsWith('**')) { + text = selected.slice(2, -2); + cursor = from + text.length; + } else { + text = `**${selected}**`; + cursor = from + text.length; + } + view.dispatch({ changes : { from, to, insert: text }, - selection : { anchor: from + text.length }, + selection : { anchor: cursor }, }); + 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}*`; + + let text, cursor; + + if(from === to) { + text = '**'; + cursor = from + 1; + } else if(selected.startsWith('*') && selected.endsWith('*')) { + text = selected.slice(2, -2); + cursor = from + text.length; + } else { + text = `*${selected}*`; + cursor = from + text.length; + } + view.dispatch({ changes : { from, to, insert: text }, - selection : { anchor: from + text.length }, + selection : { anchor: cursor }, }); + return true; }; const makeUnderline = (view)=>{ const { from, to } = view.state.selection.main; const selected = view.state.doc.sliceString(from, to); - const text = selected.startsWith('') && selected.endsWith('') - ? selected.slice(3, -4) - : `${selected}`; + + let text, cursor; + + if(from === to) { + text = ''; + cursor = from + 3; + } else if(selected.startsWith('') && selected.endsWith('')) { + text = selected.slice(3, -4); + cursor = from + text.length; + } else { + text = `${selected}`; + cursor = from + text.length; + } + view.dispatch({ changes : { from, to, insert: text }, - selection : { anchor: from + text.length }, + selection : { anchor: cursor }, }); + return true; }; - 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}^`; + + let text, cursor; + + if(from === to) { + text = '^^'; + cursor = from + 1; + } else if(selected.startsWith('^') && selected.endsWith('^')) { + text = selected.slice(1, -1); + cursor = from + text.length; + } else { + text = `^${selected}^`; + cursor = from + text.length; + } + view.dispatch({ changes : { from, to, insert: text }, - selection : { anchor: from + text.length }, + selection : { anchor: cursor }, }); + 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}^^`; + + let text, cursor; + + if(from === to) { + text = '^^^^'; + cursor = from + 2; + } else if(selected.startsWith('^^') && selected.endsWith('^^')) { + text = selected.slice(2, -2); + cursor = from + text.length; + } else { + text = `^^${selected}^^`; + cursor = from + text.length; + } + view.dispatch({ changes : { from, to, insert: text }, - selection : { anchor: from + text.length }, + selection : { anchor: cursor }, }); + return true; }; -const makeNbsp = (view)=>{ - const { from, to } = view.state.selection.main; - view.dispatch({ changes: { from, to, insert: ' ' } }); - return true; +const makeNbsp = (view) => { + const { from } = view.state.selection.main; + + 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; }; const makeSpace = (view)=>{ @@ -193,7 +263,7 @@ export const generalKeymap = Prec.high(keymap.of([ { 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}, + { key: 'Mod-d', run: deleteLine }, ])); export const markdownKeymap = Prec.highest(keymap.of([