mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-06-22 04:58:40 +00:00
Merge branch 'master' of github.com:naturalcrit/homebrewery
This commit is contained in:
+29
-3
@@ -85,14 +85,40 @@ pre {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.page .df {
|
.page .df {
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## changelog
|
## changelog
|
||||||
For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery).
|
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
|
### Friday 1/11/2026 - v3.20.1
|
||||||
|
|
||||||
{{taskList
|
{{taskList
|
||||||
@@ -2358,4 +2384,4 @@ Massive changelog incoming:
|
|||||||
|
|
||||||
* Added `phb.standalone.css` plus a build system for creating it
|
* Added `phb.standalone.css` plus a build system for creating it
|
||||||
* Added page numbers and footer text
|
* Added page numbers and footer text
|
||||||
* Page accent now flips each page
|
* Page accent now flips each page
|
||||||
@@ -16,6 +16,7 @@ const CodeEditor = createReactClass({
|
|||||||
value : '',
|
value : '',
|
||||||
wrap : true,
|
wrap : true,
|
||||||
onChange : ()=>{},
|
onChange : ()=>{},
|
||||||
|
onReady : ()=>{},
|
||||||
enableFolding : true,
|
enableFolding : true,
|
||||||
editorTheme : 'default'
|
editorTheme : 'default'
|
||||||
};
|
};
|
||||||
@@ -177,7 +178,7 @@ const CodeEditor = createReactClass({
|
|||||||
// return el;
|
// return el;
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
this.props.onReady?.(this.codeMirror);
|
||||||
// Add custom behaviors (auto-close curlies and auto-complete emojis)
|
// Add custom behaviors (auto-close curlies and auto-complete emojis)
|
||||||
closeTag.autoCloseCurlyBraces(CodeMirror, this.codeMirror);
|
closeTag.autoCloseCurlyBraces(CodeMirror, this.codeMirror);
|
||||||
autoCompleteEmoji.showAutocompleteEmoji(CodeMirror, this.codeMirror);
|
autoCompleteEmoji.showAutocompleteEmoji(CodeMirror, this.codeMirror);
|
||||||
@@ -189,7 +190,8 @@ const CodeEditor = createReactClass({
|
|||||||
|
|
||||||
// Use for GFM tabs that use common hot-keys
|
// Use for GFM tabs that use common hot-keys
|
||||||
isGFM : function() {
|
isGFM : function() {
|
||||||
if((this.props.tab === 'brewText') || (this.props.tab === 'brewSnippets')) return true;
|
console.log(this.props.tab);
|
||||||
|
if( this.props.tab === 'brewText' || this.props.tab === 'brewSnippets') return true;
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -226,7 +228,9 @@ const CodeEditor = createReactClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
makeBold : function() {
|
makeBold : function() {
|
||||||
|
console.log('hello');
|
||||||
if(!this.isGFM()) return;
|
if(!this.isGFM()) return;
|
||||||
|
console.log(this.isGFM());
|
||||||
const selection = this.codeMirror?.getSelection(), t = selection.slice(0, 2) === '**' && selection.slice(-2) === '**';
|
const selection = this.codeMirror?.getSelection(), t = selection.slice(0, 2) === '**' && selection.slice(-2) === '**';
|
||||||
this.codeMirror?.replaceSelection(t ? selection.slice(2, -2) : `**${selection}**`, 'around');
|
this.codeMirror?.replaceSelection(t ? selection.slice(2, -2) : `**${selection}**`, 'around');
|
||||||
if(selection.length === 0){
|
if(selection.length === 0){
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const INITIAL_CONTENT = dedent`
|
|||||||
<link href='/homebrew/bundle.css' type="text/css" rel='stylesheet' />
|
<link href='/homebrew/bundle.css' type="text/css" rel='stylesheet' />
|
||||||
<link href="${brewRendererStylesUrl}" rel="stylesheet" />
|
<link href="${brewRendererStylesUrl}" rel="stylesheet" />
|
||||||
<link href="${headerNavStylesUrl}" rel="stylesheet" />
|
<link href="${headerNavStylesUrl}" rel="stylesheet" />
|
||||||
<base target=_blank>
|
<base target="_top">
|
||||||
</head><body style='overflow: hidden'><div></div></body></html>`;
|
</head><body style='overflow: hidden'><div></div></body></html>`;
|
||||||
|
|
||||||
|
|
||||||
@@ -272,6 +272,13 @@ const BrewRenderer = (props)=>{
|
|||||||
const frameDidMount = ()=>{ //This triggers when iFrame finishes internal "componentDidMount"
|
const frameDidMount = ()=>{ //This triggers when iFrame finishes internal "componentDidMount"
|
||||||
scrollToHash(window.location.hash);
|
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
|
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
|
renderPages(); //Make sure page is renderable before showing
|
||||||
setState((prevState)=>({
|
setState((prevState)=>({
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ const HeaderNavItem = ({ link, text, depth, className })=>{
|
|||||||
if(!link || !text) return;
|
if(!link || !text) return;
|
||||||
|
|
||||||
return <li>
|
return <li>
|
||||||
<a href={`#${link}`} target='_self' className={`depth-${depth} ${className ?? ''}`}>
|
<a href={`#${link}`} className={`depth-${depth} ${className ?? ''}`}>
|
||||||
{trimString(text, depth)}
|
{trimString(text, depth)}
|
||||||
</a>
|
</a>
|
||||||
</li>;
|
</li>;
|
||||||
|
|||||||
@@ -76,9 +76,6 @@ const Editor = createReactClass({
|
|||||||
document.getElementById('BrewRenderer').addEventListener('keydown', this.handleControlKeys);
|
document.getElementById('BrewRenderer').addEventListener('keydown', this.handleControlKeys);
|
||||||
document.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);
|
const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY);
|
||||||
if(editorTheme) {
|
if(editorTheme) {
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -436,6 +433,29 @@ const Editor = createReactClass({
|
|||||||
this.forceUpdate();
|
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(){
|
renderEditor : function(){
|
||||||
if(this.isText()){
|
if(this.isText()){
|
||||||
return <>
|
return <>
|
||||||
@@ -448,7 +468,8 @@ const Editor = createReactClass({
|
|||||||
onChange={this.props.onBrewChange('text')}
|
onChange={this.props.onBrewChange('text')}
|
||||||
editorTheme={this.state.editorTheme}
|
editorTheme={this.state.editorTheme}
|
||||||
rerenderParent={this.rerenderParent}
|
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()){
|
if(this.isStyle()){
|
||||||
@@ -463,7 +484,8 @@ const Editor = createReactClass({
|
|||||||
enableFolding={true}
|
enableFolding={true}
|
||||||
editorTheme={this.state.editorTheme}
|
editorTheme={this.state.editorTheme}
|
||||||
rerenderParent={this.rerenderParent}
|
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()){
|
if(this.isMeta()){
|
||||||
@@ -493,7 +515,8 @@ const Editor = createReactClass({
|
|||||||
enableFolding={true}
|
enableFolding={true}
|
||||||
editorTheme={this.state.editorTheme}
|
editorTheme={this.state.editorTheme}
|
||||||
rerenderParent={this.rerenderParent}
|
rerenderParent={this.rerenderParent}
|
||||||
style={{ height: `calc(100% -${this.state.snippetBarHeight}px)` }} />
|
style={{ height: `calc(100% -${this.state.snippetBarHeight}px)` }}
|
||||||
|
onReady={this.attachCodeMirrorListeners}/>
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+4
-4
@@ -25,18 +25,18 @@
|
|||||||
|
|
||||||
let prefix = '';
|
let prefix = '';
|
||||||
|
|
||||||
if (url.includes('://homebrewery-stage.')) {
|
if (url && url?.includes('://homebrewery-stage.')) {
|
||||||
prefix = `Stage `;
|
prefix = `Stage `;
|
||||||
} else if (url.includes('://homebrewery-pr-')) {
|
} else if (url?.includes('://homebrewery-pr-')) {
|
||||||
const match = url.match(/pr-(\d+)/);
|
const match = url.match(/pr-(\d+)/);
|
||||||
if (match) prefix = `PR-${match[1]} `;
|
if (match) prefix = `PR-${match[1]} `;
|
||||||
} else if (url.includes('://localhost')) {
|
} else if (url?.includes('://localhost')) {
|
||||||
prefix = 'Local ';
|
prefix = 'Local ';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (title) {
|
if (title) {
|
||||||
document.title = `${prefix} - ${title} - The Homebrewery`;
|
document.title = `${prefix} - ${title} - The Homebrewery`;
|
||||||
} else {
|
} else if (prefix) {
|
||||||
document.title = `${prefix} - The Homebrewery`;
|
document.title = `${prefix} - The Homebrewery`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Generated
+542
-550
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "homebrewery",
|
"name": "homebrewery",
|
||||||
"description": "Create authentic looking D&D homebrews using only markdown",
|
"description": "Create authentic looking D&D homebrews using only markdown",
|
||||||
"version": "3.20.1",
|
"version": "3.21.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
"npm": ">=10.8 <12",
|
"npm": ">=10.8 <12",
|
||||||
|
|||||||
+3
-3
@@ -70,9 +70,9 @@ renderer.link = function (token) {
|
|||||||
if(title) {
|
if(title) {
|
||||||
out += ` title="${escape(title)}"`;
|
out += ` title="${escape(title)}"`;
|
||||||
}
|
}
|
||||||
if(self) {
|
// if(self) {
|
||||||
out += ' target="_self"';
|
// out += ' target="_self"';
|
||||||
}
|
// }
|
||||||
out += `>${text}</a>`;
|
out += `>${text}</a>`;
|
||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ renderer.link = function (href, title, text) {
|
|||||||
if(title) {
|
if(title) {
|
||||||
out += ` title="${title}"`;
|
out += ` title="${title}"`;
|
||||||
}
|
}
|
||||||
if(self) {
|
// if(self) {
|
||||||
out += ' target="_self"';
|
// out += ' target="_self"';
|
||||||
}
|
// }
|
||||||
out += `>${text}</a>`;
|
out += `>${text}</a>`;
|
||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ test('Processes the markdown within an HTML block if its just a class wrapper',
|
|||||||
expect(rendered).toBe('<div> <p><em>Bold text</em></p>\n </div>');
|
expect(rendered).toBe('<div> <p><em>Bold text</em></p>\n </div>');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Check markdown is using the custom renderer; specifically that it adds target=_self attribute to internal links in HTML blocks', function() {
|
// TEST REMOVED AS IT IS NO LONGER REQUIRED
|
||||||
const source = '<div>[Has _self Attribute?](#p1)</div>';
|
//
|
||||||
const rendered = Markdown.render(source);
|
// test('Check markdown is using the custom renderer; specifically that it adds target=_self attribute to internal links in HTML blocks', function() {
|
||||||
expect(rendered).toBe('<div> <p><a href="#p1" target="_self">Has _self Attribute?</a></p>\n </div>');
|
// const source = '<div>[Has _self Attribute?](#p1)</div>';
|
||||||
});
|
// const rendered = Markdown.render(source);
|
||||||
|
// expect(rendered).toBe('<div> <p><a href="#p1" target="_self">Has _self Attribute?</a></p>\n </div>');
|
||||||
|
// });
|
||||||
|
|||||||
Reference in New Issue
Block a user