0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-27 16:03:07 +00:00
* Legacy renderer (#1184)

* Include two versions of Marked.js

* Include two versions of Marked.js

* Working two different render pipelines

Adds stylesheet "styleLegacy.less"
Adds markdownHandler "markdownLegacy.js"
The BrewRenderer will switch between these and the new pipeline dependent on the "version" prop passed in.

* Mustache-style div blocks

* Legacy snippets & columnbreak

* Codemirror styling for Div Blocks

* Lint

* Codemirror highlights for inline Divs as well

These will turn red `{{class Content}}`

Multi-line divs will turn purple

```
{{class,class2
content
}}
```

No real need for these to be different colors. Just for testing.

* More lint

* Update dependencies.

* Adding Button to switch render pipelines

* Update Marked.js

* Popup alert to refresh page when renderer changed

* Don't compress files in Development (very slow)

* Block DIV or inline Span depending on {{ placement

* \column emits a Div instead of Span

* Allow share page to use new renderer

* {{ divs no longer need empty lines. Spans work in lists.

* Typo

* Typo

* Enforce \page must be at start of line. Code cleanup.

* Inject newlines after/before {{/}} to avoid needing blank lines

* Fixes issues with tables.

* Remove console.log

* Fix spacing issue for Spans

* Move things from Brewrenderer to Markdown

Try to keep all custom text fiddling in one spot.

* Rename variables

* Update Font-Awesome to v5.15. Fix style issues on popups.

* Update {{ Divs/Spans, Fix nested hilighting

* Fixed Spans/divs with no tags or just commas

* Use blacklist for {{ to allow more characters

* Update package-lock.json

* Update all icons to Font-awesome 5

* V3 hidden behind config variable

Add "globalThis.enable_v3 = true" in the console to enable.

* lint

* Legacy renderer (#1229)

* Include two versions of Marked.js

* Include two versions of Marked.js

* Working two different render pipelines

Adds stylesheet "styleLegacy.less"
Adds markdownHandler "markdownLegacy.js"
The BrewRenderer will switch between these and the new pipeline dependent on the "version" prop passed in.

* Mustache-style div blocks

* Legacy snippets & columnbreak

* Codemirror styling for Div Blocks

* Lint

* Codemirror highlights for inline Divs as well

These will turn red `{{class Content}}`

Multi-line divs will turn purple

```
{{class,class2
content
}}
```

No real need for these to be different colors. Just for testing.

* More lint

* Update dependencies.

* Adding Button to switch render pipelines

* Update Marked.js

* Popup alert to refresh page when renderer changed

* Don't compress files in Development (very slow)

* Block DIV or inline Span depending on {{ placement

* \column emits a Div instead of Span

* Allow share page to use new renderer

* {{ divs no longer need empty lines. Spans work in lists.

* Typo

* Typo

* Enforce \page must be at start of line. Code cleanup.

* Inject newlines after/before {{/}} to avoid needing blank lines

* Fixes issues with tables.

* Remove console.log

* Fix spacing issue for Spans

* Move things from Brewrenderer to Markdown

Try to keep all custom text fiddling in one spot.

* Rename variables

* Update Font-Awesome to v5.15. Fix style issues on popups.

* Update {{ Divs/Spans, Fix nested hilighting

* Fixed Spans/divs with no tags or just commas

* Use blacklist for {{ to allow more characters

* Update package-lock.json

* Update all icons to Font-awesome 5

* V3 hidden behind config variable

Add "globalThis.enable_v3 = true" in the console to enable.

* lint

* Give user styles higher priority to still allow overrides

* Apply style priority to *all* user styles

* Change .legacy .v3 to .phb, .phb3

* Revert accidental color change

* Fix brew styles overwriting each other. (#1230)

* Fix /page not working in legacy mode. (#1233)

* Fix brew styles overwriting each other.

* Word wrapping, start fixing spacing on Title letter

* Fix \page in legacy brews when not at line start

* Default 'legacy' if not set. Auto-change styles.

* Fix brew styles overwriting each other.

* Word wrapping, start fixing spacing on Title letter

* Fix \page in legacy brews when not at line start

* Fix Page Padding

* Set 'legacy' as default value if not set in brew saved file.

* Apply Legacy\v3 renderer to print page (#1235)

* Update robots.txt (#1239)

* Enable caching of static assets (#1217)

* Enable caching of static assets

* Remove dependency on mime package

Since we only care about two file extensions at the moment,
there is no need to grab the whole package just to avoid
calling 'endsWith' twice.

* Add QR-Code as snippet under Editor (#539)

* Add snippet for QR-code

* Add snippet for QR-code

* Refactor to expose metadata to snippets

* Lint

Co-authored-by: Rasmus Bækgaard <git@bakgaard.net>
Co-authored-by: Trevor Buckner <calculuschild@gmail.com>

* Unify brew structure in all pages

* Implementing magic item snippet from Issue 671. (#842)

* Implementing magic item snippet from Issue 671.

* Fixes syntax errors. Function moved into existing magic module.

* Implementing magic item snippet from Issue 671.

* Fixes syntax errors. Function moved into existing magic module.

* Magic Item Snippet, <dl>, `:` for blank line

Co-authored-by: Trevor Buckner <calculuschild@gmail.com>

* Bump codemirror from 5.59.2 to 5.59.4 (#1258)

Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.59.2 to 5.59.4.
- [Release notes](https://github.com/codemirror/CodeMirror/releases)
- [Changelog](https://github.com/codemirror/CodeMirror/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codemirror/CodeMirror/compare/5.59.2...5.59.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>

* Bump query-string from 6.13.8 to 6.14.0 (#1236)

Bumps [query-string](https://github.com/sindresorhus/query-string) from 6.13.8 to 6.14.0.
- [Release notes](https://github.com/sindresorhus/query-string/releases)
- [Commits](https://github.com/sindresorhus/query-string/compare/v6.13.8...v6.14.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>

* Bump @babel/preset-env from 7.12.11 to 7.13.5 (#1257)

Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.12.11 to 7.13.5.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.5/packages/babel-preset-env)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>

* Bump lodash from 4.17.20 to 4.17.21 (#1252)

Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>

* Bump mongoose from 5.11.13 to 5.11.18 (#1256)

Bumps [mongoose](https://github.com/Automattic/mongoose) from 5.11.13 to 5.11.18.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/History.md)
- [Commits](https://github.com/Automattic/mongoose/compare/5.11.13...5.11.18)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>

* Bump googleapis from 67.0.0 to 67.1.0 (#1245)

Bumps [googleapis](https://github.com/googleapis/google-api-nodejs-client) from 67.0.0 to 67.1.0.
- [Release notes](https://github.com/googleapis/google-api-nodejs-client/releases)
- [Changelog](https://github.com/googleapis/google-api-nodejs-client/blob/master/CHANGELOG.md)
- [Commits](https://github.com/googleapis/google-api-nodejs-client/compare/v67.0.0...v67.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>

* Bump eslint from 7.18.0 to 7.20.0 (#1244)

Bumps [eslint](https://github.com/eslint/eslint) from 7.18.0 to 7.20.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v7.18.0...v7.20.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>

* Update Marked.js version

* Bump @babel/preset-react from 7.12.10 to 7.12.13 (#1225)

Bumps [@babel/preset-react](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-react) from 7.12.10 to 7.12.13.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.12.13/packages/babel-preset-react)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>

* Bump @babel/core from 7.12.10 to 7.13.1 (#1254)

Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.12.10 to 7.13.1.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.13.1/packages/babel-core)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>

* Bump nconf from 0.11.1 to 0.11.2 (#1216)

Bumps [nconf](https://github.com/flatiron/nconf) from 0.11.1 to 0.11.2.
- [Release notes](https://github.com/flatiron/nconf/releases)
- [Changelog](https://github.com/indexzero/nconf/blob/master/CHANGELOG.md)
- [Commits](https://github.com/flatiron/nconf/compare/v0.11.1...v0.11.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>

* Fix title issue (#1251)

* Maximum title length set to 100 characters.

* Reverse unnecessary change that was incorrectly included in previous commit.

* Reduced code change to one addition on a single line.

* Revert "Reduced code change to one addition on a single line."

This reverts commit 2a355cf115.

* Use newer syntax to shorten

Co-authored-by: Trevor Buckner <calculuschild@gmail.com>

* Updated extraKeys (bold and italic) and added new shortcut (for span tags) (#1191)

* Updated extraKeys (bold and italic) and added new shortcut (for span)

* Updated makeSpan shortcut to Ctrl/Cmd-M

* ESLint

* Space after {{ so text appears

Co-authored-by: Trevor Buckner <calculuschild@gmail.com>

Co-authored-by: G.Ambatte <sean@robertson-family.nz>
Co-authored-by: Alexey Sachkov <sachkov2011@gmail.com>
Co-authored-by: Rasmus Bækgaard <rasmus@bakgaard.net>
Co-authored-by: Rasmus Bækgaard <git@bakgaard.net>
Co-authored-by: Christian Brickhouse <chrisbrickhouse@users.noreply.github.com>
Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: Rodrigo Kuerten <30464993+RKuerten@users.noreply.github.com>
This commit is contained in:
Trevor Buckner
2021-03-01 15:34:36 -05:00
committed by GitHub
parent d57df84a59
commit e3f9ef0117
21 changed files with 3562 additions and 718 deletions

View File

@@ -1,7 +1,7 @@
module.exports = { module.exports = {
root : true, root : true,
parserOptions : { parserOptions : {
ecmaVersion : 9, ecmaVersion : 2021,
sourceType : 'module', sourceType : 'module',
ecmaFeatures : { ecmaFeatures : {
jsx : true jsx : true

View File

@@ -18,10 +18,11 @@ const SNIPPETBAR_HEIGHT = 25;
const Editor = createClass({ const Editor = createClass({
getDefaultProps : function() { getDefaultProps : function() {
return { return {
value : '', brew : {
text : ''
},
onChange : ()=>{}, onChange : ()=>{},
metadata : {},
onMetadataChange : ()=>{}, onMetadataChange : ()=>{},
showMetaButton : true, showMetaButton : true,
renderer : 'legacy' renderer : 'legacy'
@@ -59,7 +60,7 @@ const Editor = createClass({
this.cursorPosition = curpos; this.cursorPosition = curpos;
}, },
handleInject : function(injectText){ handleInject : function(injectText){
const lines = this.props.value.split('\n'); const lines = this.props.brew.text.split('\n');
lines[this.cursorPosition.line] = splice(lines[this.cursorPosition.line], this.cursorPosition.ch, injectText); lines[this.cursorPosition.line] = splice(lines[this.cursorPosition.line], this.cursorPosition.ch, injectText);
this.handleTextChange(lines.join('\n')); this.handleTextChange(lines.join('\n'));
@@ -72,7 +73,7 @@ const Editor = createClass({
}, },
getCurrentPage : function(){ getCurrentPage : function(){
const lines = this.props.value.split('\n').slice(0, this.cursorPosition.line + 1); const lines = this.props.brew.text.split('\n').slice(0, this.cursorPosition.line + 1);
return _.reduce(lines, (r, line)=>{ return _.reduce(lines, (r, line)=>{
if(line.indexOf('\\page') !== -1) r++; if(line.indexOf('\\page') !== -1) r++;
return r; return r;
@@ -87,7 +88,7 @@ const Editor = createClass({
const customHighlights = codeMirror.getAllMarks(); const customHighlights = codeMirror.getAllMarks();
for (let i=0;i<customHighlights.length;i++) customHighlights[i].clear(); for (let i=0;i<customHighlights.length;i++) customHighlights[i].clear();
const lineNumbers = _.reduce(this.props.value.split('\n'), (r, line, lineNumber)=>{ const lineNumbers = _.reduce(this.props.brew.text.split('\n'), (r, line, lineNumber)=>{
//reset custom line styles //reset custom line styles
codeMirror.removeLineClass(lineNumber, 'background'); codeMirror.removeLineClass(lineNumber, 'background');
@@ -108,21 +109,14 @@ const Editor = createClass({
r.push(lineNumber); r.push(lineNumber);
} }
if(line.startsWith('\\column')){ if(line.match(/^\\column$/)){
codeMirror.addLineClass(lineNumber, 'text', 'columnSplit'); codeMirror.addLineClass(lineNumber, 'text', 'columnSplit');
r.push(lineNumber); r.push(lineNumber);
} }
if(line.startsWith('{{') || line.startsWith('}}')){ // Highlight inline spans {{content}}
let endCh = line.length+1;
const match = line.match(/{{(?:[\w,#-]|="[\w, ]*")*\s*|}}/);
if(match)
endCh = match.index+match[0].length;
codeMirror.markText({ line: lineNumber, ch: 0 }, { line: lineNumber, ch: endCh }, { className: 'block' });
}
if(line.includes('{{') && line.includes('}}')){ if(line.includes('{{') && line.includes('}}')){
const regex = /{{(?:[\w,#-]|="[\w, ]*")*\s*|}}/g; const regex = /{{(?:="[\w,\-. ]*"|[^"'\s])*\s*|}}/g;
let match; let match;
let blockCount = 0; let blockCount = 0;
while ((match = regex.exec(line)) != null) { while ((match = regex.exec(line)) != null) {
@@ -137,6 +131,14 @@ const Editor = createClass({
} }
codeMirror.markText({ line: lineNumber, ch: match.index }, { line: lineNumber, ch: match.index + match[0].length }, { className: 'inline-block' }); codeMirror.markText({ line: lineNumber, ch: match.index }, { line: lineNumber, ch: match.index + match[0].length }, { className: 'inline-block' });
} }
} else if(line.trimLeft().startsWith('{{') || line.trimLeft().startsWith('}}')){
// Highlight block divs {{\n Content \n}}
let endCh = line.length+1;
const match = line.match(/^ *{{(?:="[\w,\-. ]*"|[^"'\s])*$|^ *}}$/);
if(match)
endCh = match.index+match[0].length;
codeMirror.markText({ line: lineNumber, ch: 0 }, { line: lineNumber, ch: endCh }, { className: 'block' });
} }
} }
@@ -159,7 +161,7 @@ const Editor = createClass({
renderMetadataEditor : function(){ renderMetadataEditor : function(){
if(!this.state.showMetadataEditor) return; if(!this.state.showMetadataEditor) return;
return <MetadataEditor return <MetadataEditor
metadata={this.props.metadata} metadata={this.props.brew}
onChange={this.props.onMetadataChange} onChange={this.props.onMetadataChange}
/>; />;
}, },
@@ -169,7 +171,7 @@ const Editor = createClass({
return ( return (
<div className='editor' ref='main'> <div className='editor' ref='main'>
<SnippetBar <SnippetBar
brew={this.props.value} brew={this.props.brew}
onInject={this.handleInject} onInject={this.handleInject}
onToggle={this.handgleToggle} onToggle={this.handgleToggle}
showmeta={this.state.showMetadataEditor} showmeta={this.state.showMetadataEditor}
@@ -180,7 +182,7 @@ const Editor = createClass({
ref='codeEditor' ref='codeEditor'
wrap={true} wrap={true}
language='gfm' language='gfm'
value={this.props.value} value={this.props.brew.text}
onChange={this.handleTextChange} onChange={this.handleTextChange}
onCursorActivity={this.handleCursorActivty} /> onCursorActivity={this.handleCursorActivty} />

View File

@@ -50,7 +50,7 @@ const MetadataEditor = createClass({
}, },
handleDelete : function(){ handleDelete : function(){
if(this.props.metadata.authors.length <= 1){ if(this.props.metadata.authors && this.props.metadata.authors.length <= 1){
if(!confirm('Are you sure you want to delete this brew? Because you are the only owner of this brew, the document will be deleted permanently.')) return; if(!confirm('Are you sure you want to delete this brew? Because you are the only owner of this brew, the document will be deleted permanently.')) return;
if(!confirm('Are you REALLY sure? You will not be able to recover the document.')) return; if(!confirm('Are you REALLY sure? You will not be able to recover the document.')) return;
} else { } else {
@@ -114,7 +114,7 @@ const MetadataEditor = createClass({
renderAuthors : function(){ renderAuthors : function(){
let text = 'None.'; let text = 'None.';
if(this.props.metadata.authors.length){ if(this.props.metadata.authors && this.props.metadata.authors.length){
text = this.props.metadata.authors.join(', '); text = this.props.metadata.authors.join(', ');
} }
return <div className='field authors'> return <div className='field authors'>

View File

@@ -16,7 +16,7 @@ const execute = function(val, brew){
const Snippetbar = createClass({ const Snippetbar = createClass({
getDefaultProps : function() { getDefaultProps : function() {
return { return {
brew : '', brew : {},
onInject : ()=>{}, onInject : ()=>{},
onToggle : ()=>{}, onToggle : ()=>{},
showmeta : false, showmeta : false,
@@ -80,7 +80,7 @@ module.exports = Snippetbar;
const SnippetGroup = createClass({ const SnippetGroup = createClass({
getDefaultProps : function() { getDefaultProps : function() {
return { return {
brew : '', brew : {},
groupName : '', groupName : '',
icon : 'fas fa-rocket', icon : 'fas fa-rocket',
snippets : [], snippets : [],

View File

@@ -47,6 +47,12 @@ const spellNames = [
'Ultimate Rite of the Confetti Angel', 'Ultimate Rite of the Confetti Angel',
'Ultimate Ritual of Mouthwash', 'Ultimate Ritual of Mouthwash',
]; ];
const itemNames = [
'Doorknob of Niceness',
'Paper Armor of Folding',
'Mixtape of Sadness',
'Staff of Endless Confetti',
];
module.exports = { module.exports = {
@@ -87,5 +93,17 @@ module.exports = {
'A *continual flame* can be covered or hidden but not smothered or quenched.', 'A *continual flame* can be covered or hidden but not smothered or quenched.',
'\n\n\n' '\n\n\n'
].join('\n'); ].join('\n');
},
item : function() {
return [
`#### ${_.sample(itemNames)}`,
`*${_.sample(['Wondrous item', 'Armor', 'Weapon'])}, ${_.sample(['Common', 'Uncommon', 'Rare', 'Very Rare', 'Legendary', 'Artifact'])} (requires attunement)*`,
`:`,
`This knob is pretty nice. When attached to a door, it allows a user to`,
`open that door with the strength of the nearest animal. For example, if`,
`there is a cow nearby, the user will have the "strength of a cow" while`,
`opening this door.`
].join('\n');
} }
}; };

View File

@@ -41,8 +41,21 @@ module.exports = [
}, },
{ {
name : 'Background Image', name : 'Background Image',
icon : 'fas fa-times-circle', icon : 'fas fa-tree',
gen : '' gen : `<img src='http://i.imgur.com/hMna6G0.png' ` +
`style='position:absolute; top:50px; right:30px; width:280px'/>`
},
{
name : 'QR Code',
icon : 'fas fa-qrcode',
gen : (brew)=>{
return `<img ` +
`src='https://api.qrserver.com/v1/create-qr-code/?data=` +
`https://homebrewery.naturalcrit.com/share/${brew.shareId}` +
`&amp;size=100x100' ` +
`style='width:100px;mix-blend-mode:multiply'/>`;
}
}, },
{ {
name : 'Page Number', name : 'Page Number',
@@ -130,6 +143,11 @@ module.exports = [
icon : 'fas fa-file-word', icon : 'fas fa-file-word',
gen : CoverPageGen, gen : CoverPageGen,
}, },
{
name : 'Magic Item',
icon : 'fas fa-hat-wizard',
gen : MagicGen.item,
},
] ]
}, },

View File

@@ -48,7 +48,7 @@ const getTOC = (pages)=>{
}; };
module.exports = function(brew){ module.exports = function(brew){
const pages = brew.split('\\page'); const pages = brew.text.split('\\page');
const TOC = getTOC(pages); const TOC = getTOC(pages);
const markdown = _.reduce(TOC, (r, g1, idx1)=>{ const markdown = _.reduce(TOC, (r, g1, idx1)=>{
r.push(`- **[${idx1 + 1} ${g1.title}](#p${g1.page})**`); r.push(`- **[${idx1 + 1} ${g1.title}](#p${g1.page})**`);

View File

@@ -48,7 +48,7 @@ const getTOC = (pages)=>{
}; };
module.exports = function(brew){ module.exports = function(brew){
const pages = brew.split('\\page'); const pages = brew.text.split('\\page');
const TOC = getTOC(pages); const TOC = getTOC(pages);
const markdown = _.reduce(TOC, (r, g1, idx1)=>{ const markdown = _.reduce(TOC, (r, g1, idx1)=>{
r.push(`- **[${idx1 + 1} ${g1.title}](#p${g1.page})**`); r.push(`- **[${idx1 + 1} ${g1.title}](#p${g1.page})**`);

View File

@@ -392,9 +392,8 @@ const EditPage = createClass({
<SplitPane onDragFinish={this.handleSplitMove} ref='pane'> <SplitPane onDragFinish={this.handleSplitMove} ref='pane'>
<Editor <Editor
ref='editor' ref='editor'
value={this.state.brew.text} brew={this.state.brew}
onChange={this.handleTextChange} onChange={this.handleTextChange}
metadata={this.state.brew}
onMetadataChange={this.handleMetadataChange} onMetadataChange={this.handleMetadataChange}
renderer={this.state.brew.renderer} renderer={this.state.brew.renderer}
/> />

View File

@@ -21,6 +21,9 @@ const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
const HomePage = createClass({ const HomePage = createClass({
getDefaultProps : function() { getDefaultProps : function() {
return { return {
brew : {
text : ''
},
welcomeText : '', welcomeText : '',
ver : '0.0.0' ver : '0.0.0'
}; };
@@ -29,13 +32,15 @@ const HomePage = createClass({
}, },
getInitialState : function() { getInitialState : function() {
return { return {
brew : {
text : this.props.welcomeText text : this.props.welcomeText
}
}; };
}, },
handleSave : function(){ handleSave : function(){
request.post('/api') request.post('/api')
.send({ .send({
text : this.state.text text : this.state.brew.text
}) })
.end((err, res)=>{ .end((err, res)=>{
if(err) return; if(err) return;
@@ -48,7 +53,7 @@ const HomePage = createClass({
}, },
handleTextChange : function(text){ handleTextChange : function(text){
this.setState({ this.setState({
text : text brew : { text: text }
}); });
}, },
renderNavbar : function(){ renderNavbar : function(){
@@ -71,12 +76,12 @@ const HomePage = createClass({
<div className='content'> <div className='content'>
<SplitPane onDragFinish={this.handleSplitMove} ref='pane'> <SplitPane onDragFinish={this.handleSplitMove} ref='pane'>
<Editor value={this.state.text} onChange={this.handleTextChange} showMetaButton={false} ref='editor'/> <Editor brew={this.state.brew} onChange={this.handleTextChange} showMetaButton={false} ref='editor'/>
<BrewRenderer text={this.state.text} /> <BrewRenderer text={this.state.brew.text} />
</SplitPane> </SplitPane>
</div> </div>
<div className={cx('floatingSaveButton', { show: this.props.welcomeText != this.state.text })} onClick={this.handleSave}> <div className={cx('floatingSaveButton', { show: this.props.welcomeText != this.state.brew.text })} onClick={this.handleSave}>
Save current <i className='fas fa-save' /> Save current <i className='fas fa-save' />
</div> </div>

View File

@@ -22,7 +22,8 @@ const KEY = 'homebrewery-new';
const NewPage = createClass({ const NewPage = createClass({
getInitialState : function() { getInitialState : function() {
return { return {
metadata : { brew : {
text : '',
gDrive : false, gDrive : false,
title : '', title : '',
description : '', description : '',
@@ -32,7 +33,6 @@ const NewPage = createClass({
systems : [] systems : []
}, },
text : '',
isSaving : false, isSaving : false,
saveGoogle : (global.account && global.account.googleId ? true : false), saveGoogle : (global.account && global.account.googleId ? true : false),
errors : [] errors : []
@@ -43,7 +43,7 @@ const NewPage = createClass({
const storage = localStorage.getItem(KEY); const storage = localStorage.getItem(KEY);
if(storage){ if(storage){
this.setState({ this.setState({
text : storage brew : { text: storage }
}); });
} }
document.addEventListener('keydown', this.handleControlKeys); document.addEventListener('keydown', this.handleControlKeys);
@@ -70,13 +70,13 @@ const NewPage = createClass({
handleMetadataChange : function(metadata){ handleMetadataChange : function(metadata){
this.setState({ this.setState({
metadata : _.merge({}, this.state.metadata, metadata) brew : _.merge({}, this.state.brew, metadata)
}); });
}, },
handleTextChange : function(text){ handleTextChange : function(text){
this.setState({ this.setState({
text : text, brew : { text: text },
errors : Markdown.validate(text) errors : Markdown.validate(text)
}); });
localStorage.setItem(KEY, text); localStorage.setItem(KEY, text);
@@ -92,7 +92,7 @@ const NewPage = createClass({
if(this.state.saveGoogle) { if(this.state.saveGoogle) {
const res = await request const res = await request
.post('/api/newGoogle/') .post('/api/newGoogle/')
.send(_.merge({}, this.state.metadata, { text: this.state.text })) .send(this.state.brew)
.catch((err)=>{ .catch((err)=>{
console.log(err.status === 401 console.log(err.status === 401
? 'Not signed in!' ? 'Not signed in!'
@@ -106,9 +106,7 @@ const NewPage = createClass({
window.location = `/edit/${brew.googleId}${brew.editId}`; window.location = `/edit/${brew.googleId}${brew.editId}`;
} else { } else {
request.post('/api') request.post('/api')
.send(_.merge({}, this.state.metadata, { .send(this.state.brew)
text : this.state.text
}))
.end((err, res)=>{ .end((err, res)=>{
if(err){ if(err){
this.setState({ this.setState({
@@ -122,7 +120,6 @@ const NewPage = createClass({
window.location = `/edit/${brew.editId}`; window.location = `/edit/${brew.editId}`;
}); });
} }
}, },
renderSaveButton : function(){ renderSaveButton : function(){
@@ -138,7 +135,7 @@ const NewPage = createClass({
}, },
print : function(){ print : function(){
localStorage.setItem('print', this.state.text); localStorage.setItem('print', this.state.brew.text);
window.open('/print?dialog=true&local=print', '_blank'); window.open('/print?dialog=true&local=print', '_blank');
}, },
@@ -152,7 +149,7 @@ const NewPage = createClass({
return <Navbar> return <Navbar>
<Nav.section> <Nav.section>
<Nav.item className='brewTitle'>{this.state.metadata.title}</Nav.item> <Nav.item className='brewTitle'>{this.state.brew.title}</Nav.item>
</Nav.section> </Nav.section>
<Nav.section> <Nav.section>
@@ -172,12 +169,11 @@ const NewPage = createClass({
<SplitPane onDragFinish={this.handleSplitMove} ref='pane'> <SplitPane onDragFinish={this.handleSplitMove} ref='pane'>
<Editor <Editor
ref='editor' ref='editor'
value={this.state.text} brew={this.state.brew}
onChange={this.handleTextChange} onChange={this.handleTextChange}
metadata={this.state.metadata}
onMetadataChange={this.handleMetadataChange} onMetadataChange={this.handleMetadataChange}
/> />
<BrewRenderer text={this.state.text} errors={this.state.errors} /> <BrewRenderer text={this.state.brew.text} errors={this.state.errors} />
</SplitPane> </SplitPane>
</div> </div>
</div>; </div>;

View File

@@ -60,10 +60,10 @@ body {
// *****************************/ // *****************************/
p{ p{
overflow-wrap : break-word; overflow-wrap : break-word;
padding-bottom : 0.8em; padding-top : 0em;
line-height : 1.3em; line-height : 1.3em;
&+p{ &+p{
margin-top : -0.8em; padding-top : 0em;
} }
} }
ul{ ul{
@@ -478,3 +478,38 @@ body {
margin-bottom : 10px; margin-bottom : 10px;
} }
} }
//*****************************
// * MUSTACHE DIVS/SPANS
// *****************************/
.phb3 {
.inline-block {
display : block;
}
}
//*****************************
// * DEFINITION LISTS
// *****************************/
.phb3 {
// dl {
// margin-top: 10px;
// }
dt {
float: left;
//clear: left; //Doesn't seem necessary
margin-right: 5px;
}
// dd {
// margin-left: 0px;
// }
}
//*****************************
// * BLANK LINE
// *****************************/
.phb3 {
.blank {
height: 0.8em;
}
}

3950
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
"description": "Create authentic looking D&D homebrews using only markdown", "description": "Create authentic looking D&D homebrews using only markdown",
"version": "2.10.7", "version": "2.10.7",
"engines": { "engines": {
"node": "12.16.x" "node": "14.15.x"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -40,29 +40,29 @@
] ]
}, },
"dependencies": { "dependencies": {
"@babel/core": "^7.12.10", "@babel/core": "^7.13.1",
"@babel/preset-env": "^7.12.11", "@babel/preset-env": "^7.13.5",
"@babel/preset-react": "^7.12.10", "@babel/preset-react": "^7.12.13",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"codemirror": "^5.59.2", "codemirror": "^5.59.4",
"cookie-parser": "^1.4.5", "cookie-parser": "^1.4.5",
"create-react-class": "^15.7.0", "create-react-class": "^15.7.0",
"express": "^4.17.1", "express": "^4.17.1",
"express-static-gzip": "2.1.1", "express-static-gzip": "2.1.1",
"fs-extra": "9.1.0", "fs-extra": "9.1.0",
"googleapis": "67.0.0", "googleapis": "67.1.0",
"jwt-simple": "^0.5.6", "jwt-simple": "^0.5.6",
"less": "^3.13.1", "less": "^3.13.1",
"lodash": "^4.17.20", "lodash": "^4.17.21",
"moment": "^2.29.1", "moment": "^2.29.1",
"mongoose": "^5.11.13", "mongoose": "^5.11.18",
"nanoid": "3.1.20", "nanoid": "3.1.20",
"markedLegacy": "npm:marked@^0.3.19", "markedLegacy": "npm:marked@^0.3.19",
"marked": "npm:marked@^1.2.7", "marked": "2.0.0",
"nconf": "^0.11.1", "nconf": "^0.11.2",
"prop-types": "15.7.2", "prop-types": "15.7.2",
"query-string": "6.13.8", "query-string": "6.14.0",
"react": "^16.14.0", "react": "^16.14.0",
"react-dom": "^16.14.0", "react-dom": "^16.14.0",
"react-frame-component": "4.1.3", "react-frame-component": "4.1.3",
@@ -71,7 +71,7 @@
"vitreum": "github:calculuschild/vitreum#21a8e1c9421f1d3a3b474c12f480feb2fbd28c5b" "vitreum": "github:calculuschild/vitreum#21a8e1c9421f1d3a3b474c12f480feb2fbd28c5b"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^7.18.0", "eslint": "^7.20.0",
"eslint-plugin-react": "^7.22.0", "eslint-plugin-react": "^7.22.0",
"pico-check": "^2.0.3" "pico-check": "^2.0.3"
} }

View File

@@ -1,2 +1,4 @@
# Notes # Notes
User-agent: * User-agent: *
Disallow: /edit/

View File

@@ -19,7 +19,6 @@ const build = async ({ bundle, render, ssr })=>{
await fs.outputFile('./build/homebrew/bundle.css', css); await fs.outputFile('./build/homebrew/bundle.css', css);
await fs.outputFile('./build/homebrew/bundle.js', bundle); await fs.outputFile('./build/homebrew/bundle.js', bundle);
await fs.outputFile('./build/homebrew/ssr.js', ssr); await fs.outputFile('./build/homebrew/ssr.js', ssr);
await fs.outputFile('./build/homebrew/render.js', render);
//compress files in production //compress files in production
if(!isDev){ if(!isDev){
@@ -48,6 +47,6 @@ pack('./client/homebrew/homebrew.jsx', {
if(isDev){ if(isDev){
livereload('./build'); livereload('./build');
watchFile('./server.js', { watchFile('./server.js', {
watch : ['./homebrew'] // Watch additional folders if you want watch : ['./client'] // Watch additional folders if you want
}); });
} }

View File

@@ -1,18 +1,13 @@
const _ = require('lodash'); const _ = require('lodash');
const jwt = require('jwt-simple'); const jwt = require('jwt-simple');
const expressStaticGzip = require('express-static-gzip');
const express = require('express'); const express = require('express');
const app = express(); const app = express();
const homebrewApi = require('./server/homebrew.api.js'); const homebrewApi = require('./server/homebrew.api.js');
const GoogleActions = require('./server/googleActions.js'); const GoogleActions = require('./server/googleActions.js');
const serveCompressedStaticAssets = require('./server/static-assets.mv.js');
// Serve brotli-compressed static files if available app.use('/', serveCompressedStaticAssets(`${__dirname}/build`));
app.use('/', expressStaticGzip(`${__dirname}/build`, {
enableBrotli : true,
orderPreference : ['br'],
index : false
}));
process.chdir(__dirname); process.chdir(__dirname);
@@ -30,7 +25,7 @@ const config = require('nconf')
//DB //DB
const mongoose = require('mongoose'); const mongoose = require('mongoose');
mongoose.connect(config.get('mongodb_uri') || config.get('mongolab_uri') || 'mongodb://localhost/naturalcrit', mongoose.connect(config.get('mongodb_uri') || config.get('mongolab_uri') || 'mongodb://localhost/naturalcrit',
{ retryWrites: false, useNewUrlParser: true }); { retryWrites: false, useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true });
mongoose.connection.on('error', ()=>{ mongoose.connection.on('error', ()=>{
console.log('Error : Could not connect to a Mongo Database.'); console.log('Error : Could not connect to a Mongo Database.');
console.log(' If you are running locally, make sure mongodb.exe is running.'); console.log(' If you are running locally, make sure mongodb.exe is running.');

View File

@@ -11,9 +11,12 @@ const Markdown = require('../shared/naturalcrit/markdown.js');
// }); // });
// }; // };
const MAX_TITLE_LENGTH = 100;
const getGoodBrewTitle = (text)=>{ const getGoodBrewTitle = (text)=>{
const tokens = Markdown.marked.lexer(text); const tokens = Markdown.marked.lexer(text);
return title = (tokens.find((token)=>token.type == 'heading' || token.type == 'paragraph') || { text: 'No Title' }).text; return (tokens.find((token)=>token.type == 'heading' || token.type == 'paragraph')?.text || 'No Title')
.slice(0, MAX_TITLE_LENGTH);
}; };
const newBrew = (req, res)=>{ const newBrew = (req, res)=>{

View File

@@ -0,0 +1,31 @@
const expressStaticGzip = require('express-static-gzip');
// Serve brotli-compressed static files if available
const customCacheControlHandler=(response, path)=>{
if(path.endsWith('.br')) {
// Drop .br suffix to help mime understand the actual type of the file
path = path.slice(0, -3);
}
if(path.endsWith('.js') || path.endsWith('.css')) {
// .js and .css files are allowed to be cached up to 12 hours, but then
// they must be revalidated to see if there are any updates
response.setHeader('Cache-Control', 'public, max-age: 43200, must-revalidate');
} else {
// Everything else is cached up to a months as we don't update our images
// or fonts frequently
response.setHeader('Cache-Control', 'public, max-age=2592000, must-revalidate');
}
};
const init=(pathToAssets)=>{
return expressStaticGzip(pathToAssets, {
enableBrotli : true,
orderPreference : ['br'],
index : false,
serveStatic : {
cacheControl : false, // we are going to use custom cache-control
setHeaders : customCacheControlHandler
} });
};
module.exports = init;

View File

@@ -34,7 +34,11 @@ const CodeEditor = createClass({
mode : this.props.language, mode : this.props.language,
extraKeys : { extraKeys : {
'Ctrl-B' : this.makeBold, 'Ctrl-B' : this.makeBold,
'Ctrl-I' : this.makeItalic 'Cmd-B' : this.makeBold,
'Ctrl-I' : this.makeItalic,
'Cmd-I' : this.makeItalic,
'Ctrl-M' : this.makeSpan,
'Cmd-M' : this.makeSpan,
} }
}); });
@@ -44,8 +48,8 @@ const CodeEditor = createClass({
}, },
makeBold : function() { makeBold : function() {
const selection = this.codeMirror.getSelection(); const selection = this.codeMirror.getSelection(), t = selection.slice(0, 2) === '**' && selection.slice(-2) === '**';
this.codeMirror.replaceSelection(`**${selection}**`, 'around'); this.codeMirror.replaceSelection(t ? selection.slice(2, -2) : `**${selection}**`, 'around');
if(selection.length === 0){ if(selection.length === 0){
const cursor = this.codeMirror.getCursor(); const cursor = this.codeMirror.getCursor();
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 2 }); this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 2 });
@@ -53,14 +57,23 @@ const CodeEditor = createClass({
}, },
makeItalic : function() { makeItalic : function() {
const selection = this.codeMirror.getSelection(); const selection = this.codeMirror.getSelection(), t = selection.slice(0, 1) === '_' && selection.slice(-1) === '_';
this.codeMirror.replaceSelection(`*${selection}*`, 'around'); this.codeMirror.replaceSelection(t ? selection.slice(1, -1) : `_${selection}_`, 'around');
if(selection.length === 0){ if(selection.length === 0){
const cursor = this.codeMirror.getCursor(); const cursor = this.codeMirror.getCursor();
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 1 }); this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 1 });
} }
}, },
makeSpan : function() {
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){
const cursor = this.codeMirror.getCursor();
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 2 });
}
},
componentDidUpdate : function(prevProps) { componentDidUpdate : function(prevProps) {
if(this.codeMirror && this.codeMirror.getValue() != this.props.value) { if(this.codeMirror && this.codeMirror.getValue() != this.props.value) {
this.codeMirror.setValue(this.props.value); this.codeMirror.setValue(this.props.value);

View File

@@ -11,36 +11,56 @@ renderer.html = function (html) {
html = html.substring(0, html.lastIndexOf('</div>')); html = html.substring(0, html.lastIndexOf('</div>'));
return `${openTag} ${Markdown(html)} </div>`; return `${openTag} ${Markdown(html)} </div>`;
} }
// if(_.startsWith(_.trim(html), '<style') && _.endsWith(_.trim(html), '</style>')){
// const openTag = html.substring(0, html.indexOf('>')+1);
// html = html.substring(html.indexOf('>')+1);
// html = html.substring(0, html.lastIndexOf('</style>'));
// html = html.replaceAll(/\s(\.[^{]*)/gm, '.V3 $1');
// return `${openTag} ${html} </style>`;
// }
return html; return html;
}; };
// Ensure {{ Divs don't confuse paragraph parsing (else it renders empty paragraphs) // Don't wrap {{ Divs or {{ empty Spans in <p> tags
renderer.paragraph = function(text){ renderer.paragraph = function(text){
let match;
if(text.startsWith('<div') || text.startsWith('</div')) if(text.startsWith('<div') || text.startsWith('</div'))
return `${text}`; return `${text}`;
else if(match = text.match(/(^|^.*?\n)<span class="inline([^>]*><\/span>)$/))
return `<p>${match[1]}</p><span class="inline-block"${match[2]}`;
else else
return `<p>${text}</p>\n`; return `<p>${text}</p>\n`;
}; };
// Mustache-style Divs {{class \n content ... \n}} // Mustache-style Divs {{class \n content ... \n}}
let blockCount = 0; let blockCount = 0;
const blockRegex = /^ *{{(?:="[\w, ]*"|[^"'\s])*$|^ *}}$/gm; const blockRegex = /^ *{{(?:="[\w,\-. ]*"|[^"'\s])*$|^ *}}$/gm;
const inlineFullRegex = /{{[^\n]*}}/g; const inlineFullRegex = /{{[^\n]*}}/g;
const inlineRegex = /{{(?:="[\w, ]*"|[^"'\s])*\s*|}}/g; const inlineRegex = /{{(?:="[\w,\-. ]*"|[^"'{}}\s])*\s*|}}/g;
renderer.text = function(text){ renderer.text = function(text){
const newText = text.replaceAll('&quot;', '"'); const newText = text.replaceAll('&quot;', '"');
let matches; let matches;
if(matches = newText.match(inlineFullRegex)) {
//SPAN - INLINE
matches = newText.match(inlineRegex);
let matchIndex = 0;
const res = _.reduce(newText.split(inlineRegex), (r, splitText)=>{
if(splitText) r.push(Markdown.parseInline(splitText, { renderer: renderer }));
const block = matches[matchIndex] ? matches[matchIndex].trimLeft() : '';
if(block && block.startsWith('{{')) {
const values = processStyleTags(block.substring(2));
r.push(`<span class="inline ${values}>`);
blockCount++;
} else if(block == '}}' && blockCount !== 0){
r.push('</span>');
blockCount--;
}
matchIndex++;
return r;
}, []).join('');
return `${res}`;
} else if(matches = newText.match(blockRegex)) {
//DIV - BLOCK-LEVEL //DIV - BLOCK-LEVEL
if(matches = newText.match(blockRegex)) {
let matchIndex = 0; let matchIndex = 0;
const res = _.reduce(newText.split(blockRegex), (r, splitText)=>{ const res = _.reduce(newText.split(blockRegex), (r, splitText)=>{
if(splitText) r.push(Markdown.parseInline(splitText, { renderer: renderer })); if(splitText) r.push(Markdown.parseInline(splitText, { renderer: renderer }));
@@ -60,30 +80,6 @@ renderer.text = function(text){
return r; return r;
}, []).join(''); }, []).join('');
return res; return res;
} else if(matches = newText.match(inlineFullRegex)) {
//SPAN - INLINE
matches = newText.match(inlineRegex);
let matchIndex = 0;
const res = _.reduce(newText.split(inlineRegex), (r, splitText)=>{
if(splitText) r.push(Markdown.parseInline(splitText, { renderer: renderer }));
const block = matches[matchIndex] ? matches[matchIndex].trimLeft() : '';
if(block && block.startsWith('{{')) {
const values = processStyleTags(block.substring(2));
r.push(`<span class="inline-block ${values}>`);
blockCount++;
} else if(block == '}}' && blockCount !== 0){
r.push('</span>');
blockCount--;
}
matchIndex++;
return r;
}, []).join('');
return `${res}`;
} else { } else {
if(!matches) { if(!matches) {
return `${text}`; return `${text}`;
@@ -188,9 +184,13 @@ module.exports = {
marked : Markdown, marked : Markdown,
render : (rawBrewText)=>{ render : (rawBrewText)=>{
blockCount = 0; blockCount = 0;
rawBrewText = rawBrewText.replace(/^\\column/gm, `<div class='columnSplit'></div>`) rawBrewText = rawBrewText.replace(/^\\column$/gm, `<div class='columnSplit'></div>`)
.replace(/^(:+)$/gm, (match)=>`${`<div class='blank'></div>`.repeat(match.length)}\n`)
.replace(/(?:^|>) *:([^:\n]*):([^\n]*)\n/gm, (match, term, def)=>`<dt>${Markdown.parseInline(term)}</dt><dd>${def}</dd>`)
.replace(/(<dt>.*<\/dt><dd>.*<\/dd>\n?)+/gm, `<dl>$1</dl>\n\n`)
.replace(/^}}/gm, '\n}}') .replace(/^}}/gm, '\n}}')
.replace(/^({{[^\n]*)$/gm, '$1\n'); .replace(/^({{[^\n]*)$/gm, '$1\n');
console.log(rawBrewText);
return Markdown( return Markdown(
sanatizeScriptTags(rawBrewText), sanatizeScriptTags(rawBrewText),
{ renderer: renderer } { renderer: renderer }