This commit is contained in:
Víctor Losada Hernández
2026-08-27 20:33:48 +02:00
parent a321402556
commit c747b60bb7
6 changed files with 117 additions and 134 deletions
@@ -251,13 +251,13 @@ function tokenizeCustomMarkdown(text) {
); );
if(match) endCh = match.index + match[0].length; if(match) endCh = match.index + match[0].length;
const closingMatch = lineText.match(/ *(}})/d); const closingMatch = lineText.match(/ *(}})/d);
if(closingMatch) { if(closingMatch) {
tokens.push({ line: lineNumber, from: closingMatch.indices[1][0], to: closingMatch.indices[1][1], type: customTags.block }); tokens.push({ line: lineNumber, from: closingMatch.indices[1][0], to: closingMatch.indices[1][1], type: customTags.block });
} else { } else {
tokens.push({ line: lineNumber, type: customTags.block }); tokens.push({ line: lineNumber, type: customTags.block });
} }
} }
}); });
@@ -350,10 +350,10 @@ class ImageWidget extends WidgetType {
toDOM() { toDOM() {
const img = document.createElement('img'); const img = document.createElement('img');
img.loading = "lazy"; img.loading = 'lazy';
img.className = 'cm-preview'; img.className = 'cm-preview';
img.src = this.url; img.src = this.url;
img.onerror = ()=>{ img.onerror = ()=>{
img.src = 'client/icons/broken-image.jpg'; img.src = 'client/icons/broken-image.jpg';
@@ -403,8 +403,8 @@ export function customHighlightPlugin(renderer, tab, settings) {
const url = getUrl(node, view.state.doc); const url = getUrl(node, view.state.doc);
const widgetPosition = node.node.lastChild.from; const widgetPosition = node.node.lastChild.from;
//this is not exactly standard, but should hold, //this is not exactly standard, but should hold,
//and is the shortest way i could find of positioning //and is the shortest way i could find of positioning
//the image inside the cm-image node //the image inside the cm-image node
if(!url) return; if(!url) return;
+18 -19
View File
@@ -14,7 +14,6 @@ const EDITOR_SETTINGS_KEY = 'HB_edit_settings';
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'; import cm5Themes from 'codemirror-5-themes';
import { autoCloseTags } from '@codemirror/lang-javascript';
const themes = { default: defaultCM5Theme, ...cm5Themes, darkbrewery }; const themes = { default: defaultCM5Theme, ...cm5Themes, darkbrewery };
@@ -88,13 +87,13 @@ const Editor = forwardRef(
const [view, setView] = useState('text'); // 'text', 'style', 'meta', 'snippet' const [view, setView] = useState('text'); // 'text', 'style', 'meta', 'snippet'
const [snippetBarHeight, setSnippetBarHeight] = useState(26); const [snippetBarHeight, setSnippetBarHeight] = useState(26);
const [editorSettings, setEditorSettings] = useState({ const [editorSettings, setEditorSettings] = useState({
autoCloseBrackets: true, autoCloseBrackets : true,
showImagePreviews: true, showImagePreviews : true,
activeLineShading: true, activeLineShading : true,
lineNumbers: true, lineNumbers : true,
fontSize: 13, fontSize : 13,
editorTheme: 'default', editorTheme : 'default',
}) });
const editor = useRef(null); const editor = useRef(null);
const codeEditor = useRef(null); const codeEditor = useRef(null);
@@ -115,10 +114,10 @@ const Editor = forwardRef(
const localEditorTheme = window.localStorage.getItem(EDITOR_THEME_KEY); const localEditorTheme = window.localStorage.getItem(EDITOR_THEME_KEY);
if(localEditorTheme && EditorThemes.includes(localEditorTheme)) { if(localEditorTheme && EditorThemes.includes(localEditorTheme)) {
setEditorSettings({...editorSettings , editorTheme: localEditorTheme}) setEditorSettings({ ...editorSettings, editorTheme: localEditorTheme });
} else setEditorSettings({...editorSettings , editorTheme: 'default'}); } else setEditorSettings({ ...editorSettings, editorTheme: 'default' });
const localEditorSettings = window.localStorage.getItem(EDITOR_SETTINGS_KEY); const localEditorSettings = window.localStorage.getItem(EDITOR_SETTINGS_KEY);
if (localEditorSettings) setEditorSettings(JSON.parse(localEditorSettings)); if(localEditorSettings) setEditorSettings(JSON.parse(localEditorSettings));
const snippetBar = document.querySelector('.editor > .snippetBar'); const snippetBar = document.querySelector('.editor > .snippetBar');
if(!snippetBar) return; if(!snippetBar) return;
@@ -235,13 +234,13 @@ const Editor = forwardRef(
const updateEditorTheme = (newTheme)=>{ const updateEditorTheme = (newTheme)=>{
window.localStorage.setItem(EDITOR_THEME_KEY, newTheme); window.localStorage.setItem(EDITOR_THEME_KEY, newTheme);
setEditorSettings({...editorSettings , editorTheme: newTheme}) setEditorSettings({ ...editorSettings, editorTheme: newTheme });
}; };
const updateEditorSettings = (newEditorSettings) => { const updateEditorSettings = (newEditorSettings)=>{
window.localStorage.setItem(EDITOR_SETTINGS_KEY, JSON.stringify(newEditorSettings)); window.localStorage.setItem(EDITOR_SETTINGS_KEY, JSON.stringify(newEditorSettings));
setEditorSettings(newEditorSettings); setEditorSettings(newEditorSettings);
} };
const renderEditor = ()=>{ const renderEditor = ()=>{
if(isText()) { if(isText()) {
@@ -324,14 +323,14 @@ const Editor = forwardRef(
if(isSettings()){ if(isSettings()){
return ( return (
<> <>
<CodeEditor <CodeEditor
key='codeEditor' key='codeEditor'
tab='brewSettings' //necessary or the brew object loses its contents, culprit possibly on the tab dependent useEffect in codeEditor.jsx tab='brewSettings' //necessary or the brew object loses its contents, culprit possibly on the tab dependent useEffect in codeEditor.jsx
view={view} view={view}
style={{ display: 'none' }} style={{ display: 'none' }}
settings={editorSettings} settings={editorSettings}
/> />
<SettingsEditor <SettingsEditor
settings={editorSettings} settings={editorSettings}
EditorThemeNameList={EditorThemeNameList} EditorThemeNameList={EditorThemeNameList}
updateSettings={updateEditorSettings} updateSettings={updateEditorSettings}
@@ -1,5 +1,5 @@
import '../uiEditor.less'; import '../uiEditor.less';
import React, { useState, useEffect} from 'react'; import React from 'react';
const SettingsEditor = ({ settings, updateSettings = ()=>{}, EditorThemeNameList })=>{ const SettingsEditor = ({ settings, updateSettings = ()=>{}, EditorThemeNameList })=>{
@@ -56,7 +56,7 @@ const SettingsEditor = ({ settings, updateSettings = ()=>{}, EditorThemeNameList
Select your Editor Theme Select your Editor Theme
</label> </label>
<div className='value'> <div className='value'>
<select id="changeEditorTheme" value={settings.editorTheme} onChange={(e)=>handleFieldChange('editorTheme', e)} > <select id='changeEditorTheme' value={settings.editorTheme} onChange={(e)=>handleFieldChange('editorTheme', e)} >
{EditorThemeNameList.map((theme, key)=>{ {EditorThemeNameList.map((theme, key)=>{
return <option key={key} value={theme}>{theme}</option>; return <option key={key} value={theme}>{theme}</option>;
})} })}
@@ -127,7 +127,7 @@ const SettingsEditor = ({ settings, updateSettings = ()=>{}, EditorThemeNameList
<label htmlFor='fontSize'> <label htmlFor='fontSize'>
Editor Font Size Editor Font Size
</label> </label>
<div className='value'> <div className='value'>
<small>from 9px to 30px</small> <small>from 9px to 30px</small>
<input <input
@@ -35,21 +35,21 @@ const Snippetbar = createReactClass({
displayName : 'SnippetBar', displayName : 'SnippetBar',
getDefaultProps : function() { getDefaultProps : function() {
return { return {
brew : {}, brew : {},
view : 'text', view : 'text',
onViewChange : ()=>{}, onViewChange : ()=>{},
onInject : ()=>{}, onInject : ()=>{},
onToggle : ()=>{}, onToggle : ()=>{},
showEditButtons : true, showEditButtons : true,
renderer : 'legacy', renderer : 'legacy',
undo : ()=>{}, undo : ()=>{},
redo : ()=>{}, redo : ()=>{},
historySize : ()=>{}, historySize : ()=>{},
foldCode : ()=>{}, foldCode : ()=>{},
unfoldCode : ()=>{}, unfoldCode : ()=>{},
cursorPos : {}, cursorPos : {},
themeBundle : [], themeBundle : [],
updateBrew : ()=>{} updateBrew : ()=>{}
}; };
}, },
@@ -294,7 +294,7 @@ const SnippetGroup = createReactClass({
<Dropdown groupName={snippet.name} icon={snippet.icon} key={snippet.name}> <Dropdown groupName={snippet.name} icon={snippet.icon} key={snippet.name}>
{this.renderSnippets(snippet.subsnippets)} {this.renderSnippets(snippet.subsnippets)}
</Dropdown> </Dropdown>
) );
} }
}); });
@@ -3,26 +3,26 @@
@import (less) '@themes/fonts/5e/fonts.less'; @import (less) '@themes/fonts/5e/fonts.less';
.snippetBar { .snippetBar {
--activeTriggerColor: inherit; --activeTriggerColor : inherit;
--menuColor : #DDDDDD; --menuColor : #DDDDDD;
@menuHeight : 25px; @menuHeight : 25px;
position : relative; position : relative;
display : flex; display : flex;
flex-wrap : wrap-reverse; flex-wrap : wrap-reverse;
justify-content : space-between; justify-content : space-between;
height : auto; height : auto;
color : black; font-family : 'Open Sans', sans-serif;
background-color : #DDDDDD; font-size : 0.65rem;
font-size : .65rem; font-weight : 800;
font-family: 'Open Sans', sans-serif; color : black;
text-transform: uppercase; text-transform : uppercase;
font-weight: 800; background-color : #DDDDDD;
.editors { .editors {
display : flex; display : flex;
justify-content : flex-end; justify-content : flex-end;
min-width : 275px; //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; font-size : 0.85rem;
&:only-child {min-width : unset; margin-left : auto;} &:only-child {min-width : unset; margin-left : auto;}
>div { >div {
@@ -130,36 +130,32 @@
} }
// removed caret for top level items, by request (makes buttons too wide). // removed caret for top level items, by request (makes buttons too wide).
.menu-wrapper .menu-item:is(.snippets > .menu-wrapper > .menu-item):first-child .caret { display: none; } .menu-wrapper .menu-item:is(.snippets > .menu-wrapper > .menu-item):first-child .caret { display : none; }
.menu-item { .menu-item {
position : relative; position : relative;
display : flex; display : flex;
justify-content: space-between; align-items : center;
align-items : center; justify-content : space-between;
min-width : max-content; width : 100%;
padding : 5px; min-width : max-content;
cursor : pointer; padding : 5px;
width: 100%; cursor : pointer;
&:is(.menu-list .menu-item) [class*="name"] { &:is(.menu-list .menu-item) [class*='name'] {
padding-inline: 8px; // additional space between icon and name (helpful in Fonts menu especially). padding-inline : 8px; // additional space between icon and name (helpful in Fonts menu especially).
} }
.menu-name { .menu-name {
flex: 1; flex : 1;
text-align: left; text-align : left;
text-box-trim: trim-end; text-box-trim : trim-end;
} }
i { i {
min-width : 25px; min-width : 25px;
height : .85rem; height : 0.85rem;
font-size : 1.2em; font-size : 1.2em;
text-align : center; text-align : center;
&.caret { &.caret { margin-right : 0; }
margin-right: 0; &.caret:is(.menu-wrapper .menu-wrapper *) { text-align : right; }
}
&.caret:is(.menu-wrapper .menu-wrapper * ) {
text-align: right;
}
/* Fonts */ /* Fonts */
&.font { &.font {
height : auto; height : auto;
@@ -198,13 +194,11 @@
background : grey; background : grey;
border-radius : 12px; border-radius : 12px;
} }
&:hover { &:hover { background-color : #999999; }
background-color : #999999;
}
&:disabled { &:disabled {
color: gray; color : gray;
cursor: not-allowed; cursor : not-allowed;
&:hover { background-color: unset; } &:hover { background-color : unset; }
} }
} }
} }
+39 -49
View File
@@ -64,19 +64,17 @@
&[data-tooltip-right] { max-width : 380px; } &[data-tooltip-right] { max-width : 380px; }
&:invalid { background : #FFB9B9; } &:invalid { background : #FFB9B9; }
small { small {
display : block; display : block;
font-size : 0.9em; width : fit-content;
font-style : italic; margin-inline : 5px;
line-height : 1.4em; font-size : 0.9em;
font-style : italic;
line-height : 1.4em;
} }
small {
width:fit-content;
margin-inline:5px;
}
input[type='checkbox'], input[type='number'] { input[type='checkbox'], input[type='number'] {
float:right; float : right;
text-align:end; text-align : end;
} }
} }
input[type='text'], textarea { input[type='text'], textarea {
@@ -185,47 +183,41 @@
} }
.authors.field { .authors.field {
.tag { .tag {
font-weight:300; font-weight : 300;
transition:background-color 0.2s; transition : background-color 0.2s;
&.owner { &.owner {
position: relative; position : relative;
background-color:@silverLight; display : grid;
min-width:25px; place-items : center;
display:grid; min-width : 25px;
place-items:center; font-weight : 900;
font-weight: 900; background-color : @silverLight;
&::after { &::after {
content: "\f521"; position : absolute;
font-family: "Font Awesome 6 Free"; top : 0;
color:gold; left : 0;
position: absolute; width : 15px;
top: 0; height : 15px;
left: 0; font-family : 'Font Awesome 6 Free';
width:15px; color : gold;
height:15px; content : '\f521';
rotate:-25deg; transform : scaleY(0.7);
translate:-30% -50%; rotate : -25deg;
transform: scaleY(0.7); translate : -30% -50%;
} }
} }
&:has(button) a { &:has(button) a { padding-right : 5px; }
padding-right:5px; &:has(button:hover) { background : #D97D7D; }
}
&:has(button:hover) {
background:#d97d7d;
}
button { button { color : @red; }
color:@red;
}
} }
a { a {
color:black; color : black;
text-decoration:unset; text-decoration : unset;
} }
} }
@@ -337,9 +329,7 @@
.icon { #groupedIcon; } .icon { #groupedIcon; }
button { button { cursor : pointer; }
cursor : pointer;
}
} }
.input-group { .input-group {
@@ -385,17 +375,17 @@
.settingsEditor .field { .settingsEditor .field {
border-bottom:1px solid black; padding-bottom : 10px;
padding-bottom:10px; border-bottom : 1px solid black;
.value { .value {
display:flex; display : flex;
justify-content: end; justify-content : end;
} }
> label { > label {
width:fit-content; width : fit-content;
max-width:calc(100% - 200px); max-width : calc(100% - 200px);
} }
} }