diff --git a/changelog.md b/changelog.md index 1e1ac70e2..22af73b80 100644 --- a/changelog.md +++ b/changelog.md @@ -85,14 +85,40 @@ pre { } .page .df { - font-size: 2em; - vertical-align: middle; + font-size: 2em; + vertical-align: middle; } ``` ## changelog For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery). +### Saturday 4/04/2026 - v3.21.0 + +{{taskList +##### Gazook89 +* [x] Allow custom {{openSans **:fas_table_list: SNIPPETS**}} to be inserted mid-line + +##### abquintic +* [x] Move example snippet images out of imgur (for folks without imgur access) + +##### 5e-Cleric +* [x] Add auto-suggest to tag entry input box +* [x] Replace all example artwork with +* [x] Added tooltips to the {{openSans :fas_circle_info: **Properties**}} menu +* [x] Removed {{openSans **SYSTEMS**}} checkboxes from {{openSans :fas_circle_info: **Properties**}} menu; instead {{openSans **TAGS**}} should be used for this purpose +* [x] Replace all AI-generated art with public domain art +* [x] Major backend refactor to use Vite + +##### A1Asriel (new contributor!) +* [x] Add fix for column breaks on Firefox + +Fixes issues [#543](https://github.com/naturalcrit/homebrewery/issues/543), [#2473](https://github.com/naturalcrit/homebrewery/issues/2473), [#3712](https://github.com/naturalcrit/homebrewery/issues/3712) + +##### G-Ambatte, abquintic, 5e-Cleric +* [x] Multiple other backend fixes and refactors +}} + ### Friday 1/11/2026 - v3.20.1 {{taskList @@ -2358,4 +2384,4 @@ Massive changelog incoming: * Added `phb.standalone.css` plus a build system for creating it * Added page numbers and footer text -* Page accent now flips each page +* Page accent now flips each page \ No newline at end of file diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index cd140ad07..5e82de156 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -16,6 +16,7 @@ const CodeEditor = createReactClass({ value : '', wrap : true, onChange : ()=>{}, + onReady : ()=>{}, enableFolding : true, editorTheme : 'default' }; @@ -177,7 +178,7 @@ const CodeEditor = createReactClass({ // return el; // } }); - + this.props.onReady?.(this.codeMirror); // Add custom behaviors (auto-close curlies and auto-complete emojis) closeTag.autoCloseCurlyBraces(CodeMirror, this.codeMirror); autoCompleteEmoji.showAutocompleteEmoji(CodeMirror, this.codeMirror); @@ -189,7 +190,8 @@ const CodeEditor = createReactClass({ // Use for GFM tabs that use common hot-keys isGFM : function() { - if((this.isGFM()) || (this.props.tab === 'brewSnippets')) return true; + console.log(this.props.tab); + if( this.props.tab === 'brewText' || this.props.tab === 'brewSnippets') return true; return false; }, @@ -226,7 +228,9 @@ const CodeEditor = createReactClass({ }, makeBold : function() { + console.log('hello'); if(!this.isGFM()) return; + console.log(this.isGFM()); const selection = this.codeMirror?.getSelection(), t = selection.slice(0, 2) === '**' && selection.slice(-2) === '**'; this.codeMirror?.replaceSelection(t ? selection.slice(2, -2) : `**${selection}**`, 'around'); if(selection.length === 0){ diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 8e74473b3..efdfce5c9 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -33,7 +33,7 @@ const INITIAL_CONTENT = dedent` - +
`; @@ -272,6 +272,13 @@ const BrewRenderer = (props)=>{ const frameDidMount = ()=>{ //This triggers when iFrame finishes internal "componentDidMount" scrollToHash(window.location.hash); + navigation.addEventListener('navigate', (e)=>{ + if(e.hashChange && e.destination.sameDocument){ + const dest = e.destination.url.slice(e.destination.url.indexOf('#')); + scrollToHash(dest); + } + }); + setTimeout(()=>{ //We still see a flicker where the style isn't applied yet, so wait 100ms before showing iFrame renderPages(); //Make sure page is renderable before showing setState((prevState)=>({ diff --git a/client/homebrew/brewRenderer/headerNav/headerNav.jsx b/client/homebrew/brewRenderer/headerNav/headerNav.jsx index 3b184aff0..080ce5ad5 100644 --- a/client/homebrew/brewRenderer/headerNav/headerNav.jsx +++ b/client/homebrew/brewRenderer/headerNav/headerNav.jsx @@ -104,7 +104,7 @@ const HeaderNavItem = ({ link, text, depth, className })=>{ if(!link || !text) return; return
  • - + {trimString(text, depth)}
  • ; diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 06fd469a0..ced40f48f 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -76,9 +76,6 @@ const Editor = createReactClass({ document.getElementById('BrewRenderer').addEventListener('keydown', this.handleControlKeys); document.addEventListener('keydown', this.handleControlKeys); - this.codeEditor.current.codeMirror?.on('cursorActivity', (cm)=>{this.updateCurrentCursorPage(cm.getCursor());}); - this.codeEditor.current.codeMirror?.on('scroll', _.throttle(()=>{this.updateCurrentViewPage(this.codeEditor.current.getTopVisibleLine());}, 200)); - const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY); if(editorTheme) { this.setState({ @@ -436,6 +433,29 @@ const Editor = createReactClass({ this.forceUpdate(); }, + //temporary fix until cm6 comes next update + attachCodeMirrorListeners : function(cm) { + if(!cm) return; + // detach previous (important on remount / view switch) + if(this._cm) { + this._cm.off('cursorActivity', this._onCursor); + this._cm.off('scroll', this._onScroll); + } + + this._cm = cm; + + this._onCursor = ()=>{ + this.updateCurrentCursorPage(cm.getCursor()); + }; + + this._onScroll = _.throttle(()=>{ + const topLine = cm.lineAtHeight(cm.getScrollInfo().top, 'local'); + this.updateCurrentViewPage(topLine); + }, 200); + + cm.on('cursorActivity', this._onCursor); + cm.on('scroll', this._onScroll); + }, renderEditor : function(){ if(this.isText()){ return <> @@ -448,7 +468,8 @@ const Editor = createReactClass({ onChange={this.props.onBrewChange('text')} editorTheme={this.state.editorTheme} rerenderParent={this.rerenderParent} - style={{ height: `calc(100% - ${this.state.snippetBarHeight}px)` }} /> + style={{ height: `calc(100% - ${this.state.snippetBarHeight}px)` }} + onReady={this.attachCodeMirrorListeners}/> ; } if(this.isStyle()){ @@ -463,7 +484,8 @@ const Editor = createReactClass({ enableFolding={true} editorTheme={this.state.editorTheme} rerenderParent={this.rerenderParent} - style={{ height: `calc(100% - ${this.state.snippetBarHeight}px)` }} /> + style={{ height: `calc(100% - ${this.state.snippetBarHeight}px)` }} + onReady={this.attachCodeMirrorListeners}/> ; } if(this.isMeta()){ @@ -493,7 +515,8 @@ const Editor = createReactClass({ enableFolding={true} editorTheme={this.state.editorTheme} rerenderParent={this.rerenderParent} - style={{ height: `calc(100% -${this.state.snippetBarHeight}px)` }} /> + style={{ height: `calc(100% -${this.state.snippetBarHeight}px)` }} + onReady={this.attachCodeMirrorListeners}/> ; } }, diff --git a/client/homebrew/editor/tagInput/curatedTagSuggestionList.js b/client/homebrew/editor/tagInput/curatedTagSuggestionList.js index 9b9afce6f..7d692ecb0 100644 --- a/client/homebrew/editor/tagInput/curatedTagSuggestionList.js +++ b/client/homebrew/editor/tagInput/curatedTagSuggestionList.js @@ -1,4 +1,4 @@ -export default [ +export const tagSuggestionList = [ // ############################## Systems // D&D 'system:D&D Original', @@ -208,3 +208,12 @@ export default [ 'SW5e', 'Star Wars 5e', ]; + +// substrings to be normalized to the first value on the array +export const canonizationList = [ + ['5e 2024', '5.5e', '5e\'24', '5.24', '5e24', '5.5'], + ['5e', '5th Edition'], + ['Dungeons & Dragons', 'Dungeons and Dragons', 'Dungeons n dragons'], + ['D&D', 'DnD', 'dnd', 'Dnd', 'dnD', 'd&d', 'd&D', 'D&d'], + ['P2e', 'p2e', 'P2E', 'Pathfinder 2e'], +]; \ No newline at end of file diff --git a/client/homebrew/editor/tagInput/tagInput.jsx b/client/homebrew/editor/tagInput/tagInput.jsx index c875114f5..b43797424 100644 --- a/client/homebrew/editor/tagInput/tagInput.jsx +++ b/client/homebrew/editor/tagInput/tagInput.jsx @@ -2,7 +2,7 @@ import './tagInput.less'; import React, { useState, useEffect } from 'react'; import Combobox from '../../../components/combobox.jsx'; -import tagSuggestionList from './curatedTagSuggestionList.js'; +import { tagSuggestionList, canonizationList } from './curatedTagSuggestionList.js'; const TagInput = ({ tooltip, label, valuePatterns, values = [], unique = true, placeholder = '', smallText = '', onChange })=>{ const [tagList, setTagList] = useState( @@ -35,20 +35,11 @@ const TagInput = ({ tooltip, label, valuePatterns, values = [], unique = true, p }); }, [tagList]); - // substrings to be normalized to the first value on the array - const duplicateGroups = [ - ['5e 2024', '5.5e', '5e\'24', '5.24', '5e24', '5.5'], - ['5e', '5th Edition'], - ['Dungeons & Dragons', 'Dungeons and Dragons', 'Dungeons n dragons'], - ['D&D', 'DnD', 'dnd', 'Dnd', 'dnD', 'd&d', 'd&D', 'D&d'], - ['P2e', 'p2e', 'P2E', 'Pathfinder 2e'], - ]; - const normalizeValue = (input)=>{ const lowerInput = input.toLowerCase(); let normalizedTag = input; - for (const group of duplicateGroups) { + for (const group of canonizationList) { for (const tag of group) { if(!tag) continue; diff --git a/client/homebrew/homebrew.jsx b/client/homebrew/homebrew.jsx index 9ab69074b..252b5ac11 100644 --- a/client/homebrew/homebrew.jsx +++ b/client/homebrew/homebrew.jsx @@ -52,6 +52,19 @@ const Homebrew = (props)=>{ updateLocalStorage(); + if(brew.pureError) { + return ( + +
    + + } /> + +
    +
    + ); + } + + return (
    diff --git a/client/homebrew/navbar/metadata.navitem.jsx b/client/homebrew/navbar/metadata.navitem.jsx index bfea2e81a..8ee6b72c0 100644 --- a/client/homebrew/navbar/metadata.navitem.jsx +++ b/client/homebrew/navbar/metadata.navitem.jsx @@ -46,11 +46,6 @@ const MetadataNav = createReactClass({ ; }, - getSystems : function(){ - if(!this.props.brew.systems || this.props.brew.systems.length == 0) return 'No systems'; - return this.props.brew.systems.join(', '); - }, - renderMetaWindow : function(){ return
    @@ -65,10 +60,6 @@ const MetadataNav = createReactClass({

    Tags

    {this.getTags()}

    -
    -

    Systems

    -

    {this.getSystems()}

    -

    Updated

    {Moment(this.props.brew.updatedAt).fromNow()}

    diff --git a/client/homebrew/navbar/newbrew.navitem.jsx b/client/homebrew/navbar/newbrew.navitem.jsx index ac72121f1..5c91f8465 100644 --- a/client/homebrew/navbar/newbrew.navitem.jsx +++ b/client/homebrew/navbar/newbrew.navitem.jsx @@ -24,7 +24,7 @@ const NewBrew = ()=>{ localStorage.setItem(BREWKEY, newBrew.text); localStorage.setItem(STYLEKEY, newBrew.style); localStorage.setItem(METAKEY, JSON.stringify( - _.pick(newBrew, ['title', 'description', 'tags', 'systems', 'renderer', 'theme', 'lang']) + _.pick(newBrew, ['title', 'description', 'tags', 'renderer', 'theme', 'lang']) )); window.location.href = '/new'; return; diff --git a/client/homebrew/pages/errorPage/errorPage.less b/client/homebrew/pages/errorPage/errorPage.less index 2d10301e0..df8dcf98d 100644 --- a/client/homebrew/pages/errorPage/errorPage.less +++ b/client/homebrew/pages/errorPage/errorPage.less @@ -1,7 +1,6 @@ .homebrew { - .uiPage.sitePage { + .uiPage.sitePage:has(.errorTitle) { .errorTitle { - //background-color: @orange; color : #D02727; text-align : center; } diff --git a/index.html b/index.html index 5ee864b28..fad6fd43a 100644 --- a/index.html +++ b/index.html @@ -19,7 +19,28 @@