From 689aef910f36906a87b0b4c9ebfe66365c18cea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Sun, 23 Aug 2026 18:36:38 +0200 Subject: [PATCH 01/17] UI Base --- client/homebrew/editor/editor.jsx | 9 +++ .../editor/settingsEditor/settingsEditor.jsx | 55 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 client/homebrew/editor/settingsEditor/settingsEditor.jsx diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index f500bebeb..d6dda1d55 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -8,6 +8,7 @@ import dedent from 'dedent'; import CodeEditor from '@components/codeEditor/codeEditor.jsx'; import SnippetBar from './snippetbar/snippetbar.jsx'; import MetadataEditor from './metadataEditor/metadataEditor.jsx'; +import SettingsEditor from './settingsEditor/settingsEditor.jsx'; const EDITOR_THEME_KEY = 'HB_editor_theme'; @@ -297,6 +298,14 @@ const Editor = createReactClass({ style={{ height: `calc(100% - 25px)` }}/> ; } + if(this.isSettings()){ + return <> + + + + } }, redo : function(){ diff --git a/client/homebrew/editor/settingsEditor/settingsEditor.jsx b/client/homebrew/editor/settingsEditor/settingsEditor.jsx new file mode 100644 index 000000000..1d2d2d070 --- /dev/null +++ b/client/homebrew/editor/settingsEditor/settingsEditor.jsx @@ -0,0 +1,55 @@ +import "./metadataEditor.less"; +import React, { useState } from "react"; + +const SettingsEditor = () => { + const [settings, setSettings] = useState({ + blockShading: false, + activeLineShading: true, + showLineNumbers: true, + }); + + const handleFieldChange = (setting, e) => { + setSettings((prevSettings) => ({ + ...prevSettings, + [setting]: e.target.checked, + })); + }; + + return ( +
+

Editor Settings

+ + + + + + +
+ ); +}; + +export default SettingsEditor; From 705fc1982ea1ae4fac079d492f384b037bb6347d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Mon, 24 Aug 2026 01:08:21 +0200 Subject: [PATCH 02/17] basic UI and functionality --- client/homebrew/editor/editor.jsx | 38 +- .../editor/settingsEditor/settingsEditor.jsx | 126 ++++-- .../editor/settingsEditor/settingsEditor.less | 373 ++++++++++++++++++ .../homebrew/editor/snippetbar/snippetbar.jsx | 6 +- .../editor/snippetbar/snippetbar.less | 4 +- 5 files changed, 507 insertions(+), 40 deletions(-) create mode 100644 client/homebrew/editor/settingsEditor/settingsEditor.less diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 87d8133da..797a452fb 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -9,6 +9,7 @@ import MetadataEditor from './metadataEditor/metadataEditor.jsx'; import SettingsEditor from './settingsEditor/settingsEditor.jsx'; const EDITOR_THEME_KEY = 'HB_editor_theme'; +const EDITOR_SETTINGS_KEY = 'HB_edit_settings'; import defaultCM5Theme from '@themes/codeMirror/default.js'; import darkbrewery from '@themes/codeMirror/darkbrewery.js'; @@ -73,6 +74,12 @@ const Editor = forwardRef( const [currentEditorTheme, setEditorTheme] = useState(editorTheme); const [view, setView] = useState('text'); // 'text', 'style', 'meta', 'snippet' const [snippetBarHeight, setSnippetBarHeight] = useState(26); + const [editorSettings, setEditorSettings] = useState({ + blockShading: false, + activeLineShading: true, + lineNumbers: true, + fontSize: 14 + }) const editor = useRef(null); const codeEditor = useRef(null); @@ -82,6 +89,7 @@ const Editor = forwardRef( const isStyle = ()=>isView('style'); const isMeta = ()=>isView('meta'); const isSnip = ()=>isView('snippet'); + const isSettings = ()=>isView('settings'); const isView = (name)=>view === name; @@ -92,6 +100,8 @@ const Editor = forwardRef( const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY); if(editorTheme && EditorThemes.includes(editorTheme)) setEditorTheme(editorTheme); else setEditorTheme('default'); + const localEditorSettings = window.localStorage.getItem(EDITOR_SETTINGS_KEY); + if (localEditorSettings) setEditorSettings(JSON.parse(localEditorSettings)); const snippetBar = document.querySelector('.editor > .snippetBar'); if(!snippetBar) return; @@ -210,6 +220,11 @@ const Editor = forwardRef( window.localStorage.setItem(EDITOR_THEME_KEY, newTheme); setEditorTheme(newTheme); }; + + const updateEditorSettings = (newEditorSettings) => { + window.localStorage.setItem(EDITOR_SETTINGS_KEY, JSON.stringify(newEditorSettings)); + setEditorSettings(newEditorSettings); + } const renderEditor = ()=>{ if(isText()) { @@ -228,6 +243,7 @@ const Editor = forwardRef( editorTheme={currentEditorTheme} renderer={brew.renderer} style={{ height: `calc(100% - ${snippetBarHeight}px)` }} + settings={editorSettings} /> ); @@ -246,6 +262,7 @@ const Editor = forwardRef( editorTheme={currentEditorTheme} renderer={brew.renderer} style={{ height: `calc(100% - ${snippetBarHeight}px)` }} + settings={editorSettings} /> ); @@ -268,6 +285,7 @@ const Editor = forwardRef( editorTheme={currentEditorTheme} renderer={brew.renderer} style={{ height: `calc(100% - 25px)` }} + settings={editorSettings} /> ); @@ -275,7 +293,7 @@ const Editor = forwardRef( if(isMeta()) { return ( <> - + ); } + if(isSettings()){ + return ( + <> + + + + ); + } }; const redo = ()=>codeEditor.current?.redo(); @@ -305,7 +340,6 @@ const Editor = forwardRef( unfoldCode, historySize, })); - return (
{ - const [settings, setSettings] = useState({ - blockShading: false, - activeLineShading: true, - showLineNumbers: true, - }); +const SettingsEditor = ({settings, updateSettings = () => {} }) => { + const [currentSettings, setCurrentSettings] = useState(settings); + + const validations = { + fontSize: [ + (value) => { + const number = Number(value); + + if (number < 9 || number > 30) { + return 'Font size must be between 9 and 30.'; + } + + return null; + }, + ], + }; const handleFieldChange = (setting, e) => { - setSettings((prevSettings) => ({ - ...prevSettings, - [setting]: e.target.checked, - })); + const value = + e.target.type === 'checkbox' + ? e.target.checked + : e.target.value; + + const inputRules = validations[setting] ?? []; + + const validationErrors = inputRules + .map((rule) => rule(value)) + .filter(Boolean); + + if (validationErrors.length > 0) { + e.target.setCustomValidity(validationErrors.join('\n')); + e.target.reportValidity(); + return; + } + + e.target.setCustomValidity(''); + + const updatedSettings = { + ...currentSettings, + [setting]: e.target.type === 'number' + ? Number(value) + : value, + }; + + setCurrentSettings(updatedSettings); + updateSettings(updatedSettings); }; return ( -
+

Editor Settings

-
); }; -export default SettingsEditor; +export default SettingsEditor; \ No newline at end of file diff --git a/client/homebrew/editor/settingsEditor/settingsEditor.less b/client/homebrew/editor/settingsEditor/settingsEditor.less new file mode 100644 index 000000000..21e8e1af6 --- /dev/null +++ b/client/homebrew/editor/settingsEditor/settingsEditor.less @@ -0,0 +1,373 @@ +@import '@sharedStyles/core.less'; + + +.settingsEditor { + position : absolute; + box-sizing : border-box; + width : 100%; + height : calc(100vh - 54px); // 54px is the height of the navbar + snippet bar. probably a better way to dynamic get this. + padding : 25px; + overflow-y : auto; + font-size : 13px; + background-color : #999999; + + h1 { + margin : 0 0 40px; + font-weight : bold; + text-transform : uppercase; + } + + h2 { + margin : 20px 0; + font-weight : bold; + color : #555555; + border-bottom : 2px solid gray; + } + + & > div { margin-bottom : 10px; } + + .field-group { + display : flex; + flex-wrap : wrap; + gap : 10px; + width : 100%; + } + + .field-column { + display : flex; + flex : 5 0 200px; + flex-direction : column; + gap : 10px; + } + + .field { + position : relative; + display : flex; + justify-content: space-between; + flex-wrap : wrap; + width : 100%; + min-width : 200px; + border-bottom:1px solid; + padding:5px; + & > label { + width : fit-content; + font-size : 0.9em; + font-weight : 800; + line-height : 1.8em; + text-transform : uppercase; + } + & > .value { + flex : 1 1 auto; + width : 50px; + &[data-tooltip-right] { max-width : 380px; } + &:invalid { background : #FFB9B9; } + small { + display : block; + font-size : 0.9em; + font-style : italic; + line-height : 1.4em; + } + } + input[type='text'], textarea { + border : 1px solid gray; + &:focus { outline : 1px solid #444444; } + } + + &.description { + flex : 1; + textarea.value { + height : auto; + font-family : 'Open Sans', sans-serif; + resize : none; + } + } + + &.thumbnail, &.themes { + label { line-height : 2.0em; } + .value { + overflow : hidden; + text-overflow : ellipsis; + } + button { + .colorButton(); + padding : 0px 5px; + color : white; + background-color : black; + border : 1px solid #999999; + &:hover { background-color : #777777; } + } + } + + &.tags .tagInput-dropdown { + z-index : 400; + max-width : 200px; + } + &.language .value { + z-index : 300; + max-width : 150px; + } + + &.themes { + .value { + overflow : visible; + text-overflow : auto; + } + button { + padding-right : 5px; + padding-left : 5px; + } + } + + &.invitedAuthors .value { + z-index : 100; + + .tagInput-dropdown { max-width : 200px; } + } + } + + .thumbnail-preview { + position : relative; + flex : 1 1; + justify-self : center; + width : 80px; + height : min-content; + max-height : 115px; + aspect-ratio : 1 / 1; + object-fit : contain; + background-color : #AAAAAA; + } + + .renderers.field .value { + label { + display : inline-flex; + align-items : center; + margin-right : 15px; + font-size : 0.9em; + font-weight : 800; + vertical-align : middle; + white-space : nowrap; + cursor : pointer; + user-select : none; + } + input { + margin : 3px; + vertical-align : middle; + cursor : pointer; + } + } + .publish.field .value { + position : relative; + margin-bottom : 15px; + button { width : 100%; } + button.publish { + .colorButton(@blueLight); + } + button.unpublish { + .colorButton(@silver); + } + } + + .delete.field .value { + button { + .colorButton(@red); + } + } + .authors.field { + .tag { + font-weight:300; + transition:background-color 0.2s; + + &.owner { + position: relative; + background-color:@silverLight; + min-width:25px; + display:grid; + place-items:center; + font-weight: 900; + + &::after { + content: "\f521"; + font-family: "Font Awesome 6 Free"; + color:gold; + position: absolute; + top: 0; + left: 0; + width:15px; + height:15px; + rotate:-25deg; + translate:-30% -50%; + transform: scaleY(0.7); + } + } + &:has(button) a { + padding-right:5px; + } + &:has(button:hover) { + background:#d97d7d; + } + + button { + color:@red; + } + + + } + a { + color:black; + text-decoration:unset; + } + } + + .themes.field { + & .dropdown-container { + position : relative; + z-index : 200; + background-color : white; + } + & .dropdown-options { overflow-y : visible; } + .disabled { + font-style : italic; + color : dimgray; + background-color : darkgray; + } + .item { + position : relative; + padding : 3px 3px; + overflow : visible; + background-color : white; + border-top : 1px solid rgb(118, 118, 118); + .preview { + position : absolute; + top : 0; + right : 0; + z-index : 1; + display : flex; + flex-direction : column; + width : 200px; + overflow : hidden; + color : black; + background : #CCCCCC; + border-radius : 5px; + box-shadow : 0 0 5px black; + opacity : 0; + transition : opacity 250ms ease; + h6 { + padding-block : 0.5em; + padding-inline : 1em; + font-weight : 900; + border-bottom : 2px solid hsl(0,0%,40%); + } + } + + .texture-container { + position : absolute; + top : 0; + left : 0; + width : 100%; + height : 100%; + min-height : 100%; + overflow : hidden; + > img { + position : absolute; + top : 0; + right : 0; + width : 50%; + min-height : 100%; + -webkit-mask-image : linear-gradient(90deg, transparent, black 20%); + mask-image : linear-gradient(90deg, transparent, black 20%); + } + } + + &:hover { + color : white; + background-color : @blue; + filter : unset; + } + &:hover > .preview { opacity : 1; } + } + } + + .field .list { + display : flex; + flex : 1 0; + flex-wrap : wrap; + + > * { flex : 0 0 auto; } + + #groupedIcon { + #backgroundColors; + position : relative; + top : -0.3em; + right : -0.3em; + display : inline-block; + min-width : 20px; + height : ~'calc(100% + 0.6em)'; + color : white; + text-align : center; + cursor : pointer; + + i { + position : relative; + top : 50%; + transform : translateY(-50%); + } + + &:not(:last-child) { border-right : 1px solid black; } + + &:last-child { border-radius : 0 0.5em 0.5em 0; } + } + + .tag { + padding : 0.35em; + margin : 2px; + font-size : 0.95em; + background-color : #DDDDDD; + border-radius : 0.5em; + + .icon { #groupedIcon; } + + button { + cursor : pointer; + } + } + + .input-group { + height : ~'calc(.9em + 4px + .6em)'; + + input { border-radius : 0.5em 0 0 0.5em; } + + input:last-child { border-radius : 0.5em; } + + .value { + width : 7.5vw; + min-width : 75px; + height : 100%; + } + + .input-group { + height : ~'calc(.9em + 4px + .6em)'; + + input { border-radius : 0.5em 0 0 0.5em; } + + input:last-child { border-radius : 0.5em; } + + .value { + width : 7.5vw; + min-width : 75px; + height : 100%; + } + + .invalid:focus { background-color : pink; } + + .icon { + #groupedIcon; + top : -0.54em; + right : 1px; + height : 97%; + + i { font-size : 1.125em; } + } + } + } + } +} \ No newline at end of file diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index 96edbd49c..ec4a583e5 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -245,7 +245,7 @@ const Snippetbar = createReactClass({ return (
- {this.props.view !== 'meta' && <>
+ {this.props.view !== 'meta' && this.props.view !== 'settings' && <>
@@ -293,6 +293,10 @@ const Snippetbar = createReactClass({ onClick={()=>this.props.onViewChange('meta')}>
+
this.props.onViewChange('settings')}> + +
diff --git a/client/homebrew/editor/snippetbar/snippetbar.less b/client/homebrew/editor/snippetbar/snippetbar.less index e49e02044..879693c2b 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.less +++ b/client/homebrew/editor/snippetbar/snippetbar.less @@ -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;} @@ -208,7 +208,7 @@ } } } -@container editor (width < 816px) { +@container editor (width < 841px) { .snippetBar { .editors { flex : 1; From a83ac83ed35e674dbac2015eb1f596cc6c50a0be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Mon, 24 Aug 2026 18:46:22 +0200 Subject: [PATCH 03/17] full functionality --- client/components/codeEditor/codeEditor.jsx | 44 ++++++++++++++++--- client/homebrew/editor/editor.jsx | 2 +- .../editor/settingsEditor/settingsEditor.jsx | 15 ++++++- 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index 25bf4f88e..1a9bb0213 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -1,4 +1,4 @@ -/* eslint max-lines: ["error", { "max": 405 }] */ +/* eslint max-lines: ["error", { "max": 455 }] */ import './codeEditor.less'; import React, { useEffect, useRef, forwardRef, useImperativeHandle } from 'react'; @@ -42,6 +42,7 @@ import cm5Themes from 'codemirror-5-themes'; const themes = { default: defaultCM5Theme, ...cm5Themes, darkbrewery }; const themeCompartment = new Compartment(); const highlightCompartment = new Compartment(); +const settingsCompartment = new Compartment(); import { generalKeymap, markdownKeymap } from './extensions/customKeyMaps.js'; import foldOnPages from './extensions/customFolding.js'; @@ -78,6 +79,20 @@ const programmaticCursorLineField = StateField.define({ provide : (decorationSet)=>EditorView.decorations.from(decorationSet) }); +const createSettingsExtensions = (settings)=>[ + ...(settings.autoCloseBrackets ? [autoCloseBrackets] : []), + ...(settings.lineNumbers ? [lineNumbers()] : []), + ...(settings.activeLineShading ? [highlightActiveLine(), + highlightActiveLineGutter()] : []), + ...(settings.fontSize + ? [EditorView.theme({ + '&, .cm-content' : { + fontSize : `${settings.fontSize}px`, + }, + })] + : []), +]; + const CodeEditor = forwardRef( ( { @@ -91,6 +106,7 @@ const CodeEditor = forwardRef( editorTheme = 'default', style, renderer, + settings = {}, ...props }, ref, @@ -163,8 +179,7 @@ const CodeEditor = forwardRef( EditorView.lineWrapping, setEventListeners, languageExtension, - autoCloseBrackets, - lineNumbers(), + settingsCompartment.of(createSettingsExtensions(settings)), scrollPastEnd(), search(), history(), //allows for undo and redo @@ -180,8 +195,6 @@ const CodeEditor = forwardRef( //highlights highlightCompartment.of([customHighlightPlugin(renderer, tab), highlightExtension]), themeCompartment.of(themeExtension), - highlightActiveLine(), - highlightActiveLineGutter(), //keyboard shortcut keymap.of([...defaultKeymap, foldKeymap, ...searchKeymap]), @@ -271,6 +284,14 @@ const CodeEditor = forwardRef( } view.setState(nextState); + view.dispatch({ + effects : settingsCompartment.reconfigure( + createSettingsExtensions(settings) + ), + }); + + //load settings too + restoreFolds(view, foldsRef.current[tab]); const savedScroll = scrollRef.current[tab]; @@ -320,10 +341,21 @@ const CodeEditor = forwardRef( : syntaxHighlighting(legacyCustomHighlightStyle); view.dispatch({ - effects : highlightCompartment.reconfigure([customHighlightPlugin(renderer, tab), highlightExtension]), + effects : highlightCompartment.reconfigure([customHighlightPlugin(renderer, tab), highlightExtension]) }); }, [renderer, tab]); + useEffect(()=>{ + const view = viewRef.current; + if(!view) return; + + view.dispatch({ + effects : settingsCompartment.reconfigure( + createSettingsExtensions(settings) + ), + }); + }, [settings]); + useImperativeHandle(ref, ()=>({ injectText : (text)=>{ diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 797a452fb..f03dc0e4e 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -78,7 +78,7 @@ const Editor = forwardRef( blockShading: false, activeLineShading: true, lineNumbers: true, - fontSize: 14 + fontSize: 13 }) const editor = useRef(null); diff --git a/client/homebrew/editor/settingsEditor/settingsEditor.jsx b/client/homebrew/editor/settingsEditor/settingsEditor.jsx index c3566f8de..1acb64598 100644 --- a/client/homebrew/editor/settingsEditor/settingsEditor.jsx +++ b/client/homebrew/editor/settingsEditor/settingsEditor.jsx @@ -52,7 +52,7 @@ const SettingsEditor = ({settings, updateSettings = () => {} }) => { return (

Editor Settings

- +{/*
*/} + +
+ + handleFieldChange('autoCloseBrackets', e)} + />
From 2fc135724f083e96fde08de63b66a5c60571b756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 26 Aug 2026 21:49:21 +0200 Subject: [PATCH 04/17] add image previews to settings --- client/components/codeEditor/codeEditor.jsx | 4 ++-- .../codeEditor/extensions/customHighlight.js | 6 +++--- client/homebrew/editor/editor.jsx | 4 +++- .../editor/settingsEditor/settingsEditor.jsx | 13 +++++++++++++ 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index 1a9bb0213..6ee6fcb9e 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -193,7 +193,7 @@ const CodeEditor = forwardRef( }), //highlights - highlightCompartment.of([customHighlightPlugin(renderer, tab), highlightExtension]), + highlightCompartment.of([customHighlightPlugin(renderer, tab, settings), highlightExtension]), themeCompartment.of(themeExtension), //keyboard shortcut @@ -341,7 +341,7 @@ const CodeEditor = forwardRef( : syntaxHighlighting(legacyCustomHighlightStyle); view.dispatch({ - effects : highlightCompartment.reconfigure([customHighlightPlugin(renderer, tab), highlightExtension]) + effects : highlightCompartment.reconfigure([customHighlightPlugin(renderer, tab, settings), highlightExtension]) }); }, [renderer, tab]); diff --git a/client/components/codeEditor/extensions/customHighlight.js b/client/components/codeEditor/extensions/customHighlight.js index 33c68da7a..10bb8fd54 100644 --- a/client/components/codeEditor/extensions/customHighlight.js +++ b/client/components/codeEditor/extensions/customHighlight.js @@ -367,7 +367,7 @@ class ImageWidget extends WidgetType { } } -export function customHighlightPlugin(renderer, tab) { +export function customHighlightPlugin(renderer, tab, settings) { //this function takes the custom tokens created in the tokenize function in customhighlight files //takes the tokens defined by that function and assigns classes to them //it also creates page number and snippet number widgets @@ -399,7 +399,7 @@ export function customHighlightPlugin(renderer, tab) { const tree = ensureSyntaxTree(view.state, view.state.doc.length, 50) || syntaxTree(view.state); tree.iterate({ enter : (node)=>{ - if(node.name === 'Image') { + if(node.name === 'Image' && settings.showImagePreviews) { const url = getUrl(node, view.state.doc); const widgetPosition = node.node.lastChild.from; @@ -432,7 +432,7 @@ export function customHighlightPlugin(renderer, tab) { const to = line.from + token.to; const attrs = {}; - if(token.type === 'Image' && token.url) { + if(token.type === 'Image' && token.url && settings.showImagePreviews) { attrs['data-url'] = token.url; } diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index f03dc0e4e..dc799f263 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -14,6 +14,7 @@ const EDITOR_SETTINGS_KEY = 'HB_edit_settings'; import defaultCM5Theme from '@themes/codeMirror/default.js'; import darkbrewery from '@themes/codeMirror/darkbrewery.js'; import cm5Themes from 'codemirror-5-themes'; +import { autoCloseTags } from '@codemirror/lang-javascript'; const themes = { default: defaultCM5Theme, ...cm5Themes, darkbrewery }; @@ -75,7 +76,8 @@ const Editor = forwardRef( const [view, setView] = useState('text'); // 'text', 'style', 'meta', 'snippet' const [snippetBarHeight, setSnippetBarHeight] = useState(26); const [editorSettings, setEditorSettings] = useState({ - blockShading: false, + autoCloseBrackets: true, + showImagePreviews: true, activeLineShading: true, lineNumbers: true, fontSize: 13 diff --git a/client/homebrew/editor/settingsEditor/settingsEditor.jsx b/client/homebrew/editor/settingsEditor/settingsEditor.jsx index 1acb64598..65353296e 100644 --- a/client/homebrew/editor/settingsEditor/settingsEditor.jsx +++ b/client/homebrew/editor/settingsEditor/settingsEditor.jsx @@ -79,6 +79,19 @@ const SettingsEditor = ({settings, updateSettings = () => {} }) => { />
+
+ + handleFieldChange('showImagePreviews', e)} + /> +
+