Merge branch 'master' into fix-brew-card-padding-5134

This commit is contained in:
Trevor Buckner
2026-10-29 15:07:36 -04:00
committed by GitHub
13 changed files with 475 additions and 492 deletions
+28 -24
View File
@@ -156,6 +156,31 @@ const CodeEditor = forwardRef(
return folds;
};
const foldAll = (view = viewRef.current)=>{
if(!view) return false;
const doc = view.state.doc;
const pages = pageMap.current;
const effects = pages.map((start, i)=>{
const next = pages[i + 1] || doc.length;
const from = i ? doc.line(doc.lineAt(start).number + 1).from : 0;
const to = doc.line(doc.lineAt(next).number).from - 1;
return to > from ? foldEffect.of({ from, to }) : null;
}).filter(Boolean);
view.dispatch({ effects });
return true;
};
const unfoldAll = (view = viewRef.current)=>{
if(!view) return false;
view.dispatch(unfoldAllCmd(view));
return true;
};
const createExtensions = ({ onChange, language, editorTheme })=>{
const setEventListeners = EditorView.updateListener.of((update)=>{
if(update.docChanged) {
@@ -199,7 +224,7 @@ const CodeEditor = forwardRef(
//keyboard shortcut
keymap.of([...defaultKeymap, foldKeymap, ...searchKeymap]),
generalKeymap,
generalKeymap(foldAll, unfoldAll),
...(tab === 'brewStyles' ? [cssKeymap] : [markdownKeymap]),
//multiple cursors and selections
@@ -404,29 +429,8 @@ const CodeEditor = forwardRef(
undone : redoDepth(view.state),
};
},
foldAll : ()=>{
const view = viewRef.current;
if(!view) return;
const doc = view.state.doc;
const pages = pageMap.current;
const effects = pages.map((start, i)=>{
const next = pages[i + 1] || doc.length;
const from = i ? doc.line(doc.lineAt(start).number + 1).from : 0;
const to = doc.line(doc.lineAt(next).number).from - 1;
return to > from ? foldEffect.of({ from, to }) : null;
}).filter(Boolean);
view.dispatch({ effects });
},
unfoldAll : ()=>{
const view = viewRef.current;
if(!view) return;
view.dispatch(unfoldAllCmd(view));
},
foldAll : ()=>foldAll(),
unfoldAll : ()=>unfoldAll(),
focus : ()=>viewRef.current.focus(),
}));
@@ -215,13 +215,15 @@ const newBreak = (type)=>(view)=>forEachSelection(view, (state, from, to, select
};
});
export const generalKeymap = Prec.high(keymap.of([
export const generalKeymap = (foldAll, unfoldAll) => Prec.high(keymap.of([
{ key: 'Tab', run: insertTab },
{ key: 'Shift-Tab', run: indentLess },
{ key: 'Mod-z', run: undo }, //it may be unnecessary
{ key: 'Mod-Shift-z', run: redo },
{ key: 'Mod-y', run: redo }, //user asked, so double keybind
{ key: 'Mod-d', run: deleteLine }, //annoyingly overrides "selectNextOccurrence" because users asked
{ key: 'Mod-Shift-[', run: foldAll },
{ key: 'Mod-Shift-]', run: unfoldAll },
]));
export const cssKeymap = Prec.highest(keymap.of([
@@ -2,13 +2,14 @@
import '../uiEditor.less';
import React, { useState } from 'react';
import _ from 'lodash';
import request from '../../utils/request-middleware.js';
import Combobox from '@components/combobox.jsx';
import TagInput from '../tagInput/tagInput.jsx';
import request from '../../utils/request-middleware.js';
import Themes from '@themes/themes.json';
import validations from './validations.js';
import Combobox from '@components/combobox.jsx';
import TagInput from '../tagInput/tagInput.jsx';
import homebreweryThumbnail from '../../thumbnail.png';
const callIfExists = (val, fn, ...args)=>{
@@ -19,32 +20,25 @@ const callIfExists = (val, fn, ...args)=>{
const MetadataEditor = ({
metadata = {
editId : null,
shareId : null,
editId : null,
title : '',
description : '',
thumbnail : '',
tags : [],
published : false,
authors : [],
renderer : 'legacy',
theme : '5ePHB',
lang : 'en',
renderer : 'legacy',
authors : [],
published : false,
thumbnail : '',
theme : '5ePHB',
},
onChange = ()=>{},
reportError = ()=>{},
...props
userThemes = {},
themeBundle = {}
})=>{
const [showThumbnail, setShowThumbnail] = useState(true);
const toggleThumbnailDisplay = ()=>{
setShowThumbnail(!showThumbnail);
};
const renderThumbnail = ()=>{
if(!showThumbnail) return;
return <img className='thumbnail-preview' src={metadata.thumbnail || homebreweryThumbnail}></img>;
};
const handleFieldChange = (name, e)=>{
// load validation rules, and check input value against them
const inputRules = validations[name] ?? [];
@@ -90,13 +84,11 @@ const MetadataEditor = ({
const handleTheme = (theme)=>{
metadata.renderer = theme.renderer;
metadata.theme = theme.path;
onChange(metadata, 'theme');
};
const handleThemeWritein = (e)=>{
const shareId = e.target.value.split('/').pop(); //Extract just the ID if a URL was pasted in
metadata.theme = shareId;
onChange(metadata, 'theme');
};
const handleLanguage = (languageCode)=>{
@@ -141,18 +133,6 @@ const MetadataEditor = ({
</button>;
}
};
const renderDelete = ()=>{
if(!metadata.editId) return;
return <div className='field delete'>
<label htmlFor='delete-button'>delete</label>
<div className='value'>
<button id='delete-button' onClick={handleDelete}>
<i className='fas fa-trash-alt' /> delete brew
</button>
</div>
</div>;
};
const renderAuthors = ()=>{
const authors = metadata.authors;
const isOwner = global.account?.username && global.account?.username === authors[0];
@@ -204,7 +184,7 @@ const MetadataEditor = ({
);
};
const renderThemeDropdown = ()=>{
const mergedThemes = _.merge(Themes, props.userThemes);
const mergedThemes = _.merge(Themes, userThemes);
const listThemes = (renderer)=>{
return _.map(_.values(mergedThemes[renderer]), (theme)=>{
@@ -225,7 +205,7 @@ const MetadataEditor = ({
};
const currentRenderer = metadata.renderer;
const currentThemeDisplay = props.themeBundle?.name ? `${props.themeBundle.author ?? currentRenderer} : ${props.themeBundle.name}` : 'No Theme Selected';
const currentThemeDisplay = themeBundle?.name ? `${themeBundle.author ?? currentRenderer} : ${themeBundle.name}` : 'No Theme Selected';
let dropdown;
if(currentRenderer == 'legacy') {
@@ -296,7 +276,6 @@ const MetadataEditor = ({
}}
/>
</div>
</div>;
};
const renderRenderOptions = ()=>{
@@ -364,15 +343,14 @@ const MetadataEditor = ({
/>
<button
className='display'
onClick={toggleThumbnailDisplay}
onClick={()=>setShowThumbnail(!showThumbnail)}
aria-label={`${showThumbnail ? 'hide thumbnail' : 'show thumbnail'}`}>
<i className={`fas fa-caret-${showThumbnail ? 'right' : 'left'}`} />
</button>
</div>
</div>
{renderThumbnail()}
{showThumbnail && <img className='thumbnail-preview' src={metadata.thumbnail || homebreweryThumbnail}></img>}
</fieldset>
<div className='field tags'>
<label htmlFor='combobox-tags'>Tags</label>
<div className='value'>
@@ -388,17 +366,12 @@ const MetadataEditor = ({
/>
</div>
</div>
{renderLanguageDropdown()}
{renderThemeDropdown()}
{renderRenderOptions()}
<h2>Authors</h2>
{renderAuthors()}
<div className='field invitedAuthors'>
<label htmlFor='combobox-invited-authors'>Invited authors</label>
<div className='value'>
@@ -418,7 +391,6 @@ const MetadataEditor = ({
</div>
<h2>Privacy</h2>
<div className='field publish'>
<label htmlFor='publish-button'>publish</label>
<div className='value'>
@@ -430,8 +402,14 @@ const MetadataEditor = ({
</small>
</div>
</div>
{renderDelete()}
{metadata.editId && <div className='field delete'>
<label htmlFor='delete-button'>delete</label>
<div className='value'>
<button id='delete-button' onClick={handleDelete}>
<i className='fas fa-trash-alt' /> delete brew
</button>
</div>
</div>}
</div>
);
};
+18 -14
View File
@@ -69,25 +69,29 @@ const Homebrew = (props)=>{
);
}
return (
<Router>
<div className={`homebrew${(config?.deployment || config?.developmentStyle) ? ' deployment' : ''}`} style={backgroundObject()}>
<Routes>
<Route path='/edit/:id' element={<WithRoute el={EditPage} brew={brew} userThemes={userThemes}/>} />
<Route path='/share/:id' element={<WithRoute el={SharePage} brew={brew} />} />
<Route path='/new/:id' element={<WithRoute el={NewPage} brew={brew} userThemes={userThemes}/>} />
<Route path='/new' element={<WithRoute el={NewPage} userThemes={userThemes}/> } />
<Route path='/user/:username' element={<WithRoute el={UserPage} brews={brews} />} />
<Route path='/vault' element={<WithRoute el={VaultPage}/>}/>
<Route path='/changelog' element={<WithRoute el={SharePage} brew={brew} disableMeta={true} />} />
<Route path='/faq' element={<WithRoute el={SharePage} brew={brew} disableMeta={true} />} />
<Route path='/migrate' element={<WithRoute el={SharePage} brew={brew} disableMeta={true} />} />
<Route path='/account' element={<WithRoute el={AccountPage} brew={brew} accountDetails={brew.accountDetails} />} />
<Route path='/' element={<WithRoute el={HomePage} brew={brew} />} />
<Route path='/*' element={<WithRoute el={HomePage} brew={brew} />} />
<Route path='/legacy' element={<WithRoute el={HomePage} brew={brew} />} />
<Route path='/error' element={<WithRoute el={ErrorPage} brew={brew} />} />
<Route path='/' element={<WithRoute el={HomePage} brew={brew} />} />
<Route path='/*' element={<WithRoute el={HomePage} brew={brew} />} />
<Route path='/new/:id' element={<WithRoute el={NewPage} brew={brew} userThemes={userThemes}/>} />
<Route path='/new' element={<WithRoute el={NewPage} userThemes={userThemes}/> } />
<Route path='/edit/:id' element={<WithRoute el={EditPage} brew={brew} userThemes={userThemes}/>} />
<Route path='/share/:id' element={<WithRoute el={SharePage} brew={brew} />} />
<Route path='/changelog' element={<WithRoute el={SharePage} brew={brew} disableMeta={true} />} />
<Route path='/faq' element={<WithRoute el={SharePage} brew={brew} disableMeta={true} />} />
<Route path='/migrate' element={<WithRoute el={SharePage} brew={brew} disableMeta={true} />} />
<Route path='/account' element={<WithRoute el={AccountPage} brew={brew} accountDetails={brew.accountDetails} />} />
<Route path='/error' element={<WithRoute el={ErrorPage} brew={brew} />} />
<Route path='/user/:username' element={<WithRoute el={UserPage} brews={brews} />} />
<Route path='/vault' element={<WithRoute el={VaultPage}/>}/>
</Routes>
</div>
</Router>
@@ -112,12 +112,15 @@ const EditorPage = (props)=>{
const snipStorage = localStorage.getItem(SNIPKEY);
const metaStorage = JSON.parse(localStorage.getItem(METAKEY));
brew.text = brewStorage ?? brew.text;
brew.style = styleStorage ?? brew.style;
brew.snippets = snipStorage ?? brew.snippets;
brew.renderer = metaStorage?.renderer ?? brew.renderer;
brew.theme = metaStorage?.theme ?? brew.theme;
brew.lang = metaStorage?.lang ?? brew.lang;
brew.title = metaStorage?.title ?? brew.title;
brew.description = metaStorage?.description ?? brew.description;
brew.tags = metaStorage?.tags ?? brew.tags;
brew.text = brewStorage ?? brew.text;
brew.style = styleStorage ?? brew.style;
brew.snippets = snipStorage ?? brew.snippets;
brew.renderer = metaStorage?.renderer ?? brew.renderer;
brew.theme = metaStorage?.theme ?? brew.theme;
brew.lang = metaStorage?.lang ?? brew.lang;
}
const SAVEKEY = `${SAVEKEY_PREFIX}${global.account?.username}`;
@@ -130,7 +133,7 @@ const EditorPage = (props)=>{
localStorage.setItem(BREWKEY, brew.text);
if(brew.style) localStorage.setItem(STYLEKEY, brew.style);
if(brew.snippets) localStorage.setItem(SNIPKEY, brew.snippets);
localStorage.setItem(METAKEY, JSON.stringify({ renderer: brew.renderer, theme: brew.theme, lang: brew.lang }));
localStorage.setItem(METAKEY, JSON.stringify({ title: brew.title, description: brew.description, tags: brew.tags, renderer: brew.renderer, theme: brew.theme, lang: brew.lang }));
if(window.location.pathname !== '/new')
window.history.replaceState({}, window.location.title, '/new/');
};
@@ -177,9 +180,12 @@ const EditorPage = (props)=>{
if(field == 'style') localStorage.setItem(STYLEKEY, value);
if(field == 'snippets') localStorage.setItem(SNIPKEY, value);
if(field == 'metadata') localStorage.setItem(METAKEY, JSON.stringify({
renderer : value.renderer,
theme : value.theme,
lang : value.lang
title : value.title,
description : value.description,
tags : value.tags,
renderer : value.renderer,
theme : value.theme,
lang : value.lang
}));
}
};