mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-03-22 08:58:11 +00:00
Merge branch 'master' into codeMirror-skipExtraKeys
This commit is contained in:
4609
changelog.md
4609
changelog.md
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
import diceFont from '../../../themes/fonts/iconFonts/diceFont.js';
|
import diceFont from 'themes/fonts/iconFonts/diceFont.js';
|
||||||
import elderberryInn from '../../../themes/fonts/iconFonts/elderberryInn.js';
|
import elderberryInn from 'themes/fonts/iconFonts/elderberryInn.js';
|
||||||
import fontAwesome from '../../../themes/fonts/iconFonts/fontAwesome.js';
|
import fontAwesome from 'themes/fonts/iconFonts/fontAwesome.js';
|
||||||
import gameIcons from '../../../themes/fonts/iconFonts/gameIcons.js';
|
import gameIcons from 'themes/fonts/iconFonts/gameIcons.js';
|
||||||
|
|
||||||
const emojis = {
|
const emojis = {
|
||||||
...diceFont,
|
...diceFont,
|
||||||
@@ -3,7 +3,7 @@ const React = require('react');
|
|||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|
||||||
import Dialog from '../../../client/components/dialog.jsx';
|
import Dialog from '../dialog.jsx';
|
||||||
|
|
||||||
const RenderWarnings = createClass({
|
const RenderWarnings = createClass({
|
||||||
displayName : 'RenderWarnings',
|
displayName : 'RenderWarnings',
|
||||||
@@ -25,7 +25,7 @@ const RenderWarnings = createClass({
|
|||||||
if(!isChrome){
|
if(!isChrome){
|
||||||
return <li key='chrome'>
|
return <li key='chrome'>
|
||||||
<em>Built for Chrome </em> <br />
|
<em>Built for Chrome </em> <br />
|
||||||
Other browsers have not been tested for compatiblilty. If you
|
Other browsers have not been tested for compatibility. If you
|
||||||
experience issues with your document not rendering or printing
|
experience issues with your document not rendering or printing
|
||||||
properly, please try using the latest version of Chrome before
|
properly, please try using the latest version of Chrome before
|
||||||
submitting a bug report.
|
submitting a bug report.
|
||||||
@@ -4,13 +4,13 @@ const React = require('react');
|
|||||||
const { useState, useRef, useMemo, useEffect } = React;
|
const { useState, useRef, useMemo, useEffect } = React;
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
|
|
||||||
const MarkdownLegacy = require('naturalcrit/markdownLegacy.js');
|
const MarkdownLegacy = require('markdownLegacy.js');
|
||||||
import Markdown from 'naturalcrit/markdown.js';
|
import Markdown from 'markdown.js';
|
||||||
const ErrorBar = require('./errorBar/errorBar.jsx');
|
const ErrorBar = require('./errorBar/errorBar.jsx');
|
||||||
const ToolBar = require('./toolBar/toolBar.jsx');
|
const ToolBar = require('./toolBar/toolBar.jsx');
|
||||||
|
|
||||||
//TODO: move to the brew renderer
|
//TODO: move to the brew renderer
|
||||||
const RenderWarnings = require('homebrewery/renderWarnings/renderWarnings.jsx');
|
const RenderWarnings = require('client/components/renderWarnings/renderWarnings.jsx');
|
||||||
const NotificationPopup = require('./notificationPopup/notificationPopup.jsx');
|
const NotificationPopup = require('./notificationPopup/notificationPopup.jsx');
|
||||||
const Frame = require('react-frame-component').default;
|
const Frame = require('react-frame-component').default;
|
||||||
const dedent = require('dedent-tabs').default;
|
const dedent = require('dedent-tabs').default;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
require('./notificationPopup.less');
|
require('./notificationPopup.less');
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import request from '../../utils/request-middleware.js';
|
import request from '../../utils/request-middleware.js';
|
||||||
import Markdown from 'naturalcrit/markdown.js';
|
import Markdown from 'markdown.js';
|
||||||
|
|
||||||
import Dialog from '../../../components/dialog.jsx';
|
import Dialog from '../../../components/dialog.jsx';
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ const React = require('react');
|
|||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const dedent = require('dedent-tabs').default;
|
const dedent = require('dedent-tabs').default;
|
||||||
import Markdown from '../../../shared/naturalcrit/markdown.js';
|
import Markdown from '../../../shared/markdown.js';
|
||||||
|
|
||||||
const CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx');
|
const CodeEditor = require('client/components/codeEditor/codeEditor.jsx');
|
||||||
const SnippetBar = require('./snippetbar/snippetbar.jsx');
|
const SnippetBar = require('./snippetbar/snippetbar.jsx');
|
||||||
const MetadataEditor = require('./metadataEditor/metadataEditor.jsx');
|
const MetadataEditor = require('./metadataEditor/metadataEditor.jsx');
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ const Editor = createClass({
|
|||||||
return {
|
return {
|
||||||
editorTheme : this.props.editorTheme,
|
editorTheme : this.props.editorTheme,
|
||||||
view : 'text', //'text', 'style', 'meta', 'snippet'
|
view : 'text', //'text', 'style', 'meta', 'snippet'
|
||||||
snippetbarHeight : 25
|
snippetBarHeight : 26,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -85,7 +85,15 @@ const Editor = createClass({
|
|||||||
editorTheme : editorTheme
|
editorTheme : editorTheme
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.setState({ snippetbarHeight: document.querySelector('.editor > .snippetBar').offsetHeight });
|
const snippetBar = document.querySelector('.editor > .snippetBar');
|
||||||
|
if (!snippetBar) return;
|
||||||
|
|
||||||
|
this.resizeObserver = new ResizeObserver(entries => {
|
||||||
|
const height = document.querySelector('.editor > .snippetBar').offsetHeight;
|
||||||
|
this.setState({ snippetBarHeight: height });
|
||||||
|
});
|
||||||
|
|
||||||
|
this.resizeObserver.observe(snippetBar);
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidUpdate : function(prevProps, prevState, snapshot) {
|
componentDidUpdate : function(prevProps, prevState, snapshot) {
|
||||||
@@ -108,6 +116,10 @@ const Editor = createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.resizeObserver) this.resizeObserver.disconnect();
|
||||||
|
},
|
||||||
|
|
||||||
handleControlKeys : function(e){
|
handleControlKeys : function(e){
|
||||||
if(!(e.ctrlKey && e.metaKey && e.shiftKey)) return;
|
if(!(e.ctrlKey && e.metaKey && e.shiftKey)) return;
|
||||||
const LEFTARROW_KEY = 37;
|
const LEFTARROW_KEY = 37;
|
||||||
@@ -408,11 +420,7 @@ const Editor = createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
//Called when there are changes to the editor's dimensions
|
//Called when there are changes to the editor's dimensions
|
||||||
update : function(){
|
update : function(){},
|
||||||
const snipHeight = document.querySelector('.editor > .snippetBar').offsetHeight;
|
|
||||||
if(snipHeight !== this.state.snippetbarHeight)
|
|
||||||
this.setState({ snippetbarHeight: snipHeight });
|
|
||||||
},
|
|
||||||
|
|
||||||
updateEditorTheme : function(newTheme){
|
updateEditorTheme : function(newTheme){
|
||||||
window.localStorage.setItem(EDITOR_THEME_KEY, newTheme);
|
window.localStorage.setItem(EDITOR_THEME_KEY, newTheme);
|
||||||
@@ -437,7 +445,7 @@ const Editor = createClass({
|
|||||||
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)` }} />
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
if(this.isStyle()){
|
if(this.isStyle()){
|
||||||
@@ -451,7 +459,7 @@ const Editor = createClass({
|
|||||||
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)` }} />
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
if(this.isMeta()){
|
if(this.isMeta()){
|
||||||
@@ -468,7 +476,6 @@ const Editor = createClass({
|
|||||||
userThemes={this.props.userThemes}/>
|
userThemes={this.props.userThemes}/>
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.isSnip()){
|
if(this.isSnip()){
|
||||||
if(!this.props.brew.snippets) { this.props.brew.snippets = DEFAULT_SNIPPET_TEXT; }
|
if(!this.props.brew.snippets) { this.props.brew.snippets = DEFAULT_SNIPPET_TEXT; }
|
||||||
return <>
|
return <>
|
||||||
@@ -481,7 +488,7 @@ const Editor = createClass({
|
|||||||
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)` }} />
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
.snippets {
|
.snippets {
|
||||||
display : flex;
|
display : flex;
|
||||||
justify-content : flex-start;
|
justify-content : flex-start;
|
||||||
min-width : 432.18px; //must be controlled every time an item is added, must be hardcoded for the wrapping as it is applied
|
min-width : 499.35px; //must be controlled every time an item is added, must be hardcoded for the wrapping as it is applied
|
||||||
}
|
}
|
||||||
|
|
||||||
.editors {
|
.editors {
|
||||||
@@ -237,7 +237,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@container editor (width < 683px) {
|
@container editor (width < 750px) {
|
||||||
.snippetBar {
|
.snippetBar {
|
||||||
.editors {
|
.editors {
|
||||||
flex : 1;
|
flex : 1;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('client/homebrew/navbar/nav.jsx');
|
||||||
const request = require('superagent');
|
const request = require('superagent');
|
||||||
|
|
||||||
const Account = createClass({
|
const Account = createClass({
|
||||||
@@ -70,7 +70,7 @@ const Account = createClass({
|
|||||||
{global.account.username}
|
{global.account.username}
|
||||||
</Nav.item>
|
</Nav.item>
|
||||||
<Nav.item
|
<Nav.item
|
||||||
href={`/user/${encodeURI(global.account.username)}`}
|
href={`/user/${encodeURIComponent(global.account.username)}`}
|
||||||
color='yellow'
|
color='yellow'
|
||||||
icon='fas fa-beer'
|
icon='fas fa-beer'
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
require('./error-navitem.less');
|
require('./error-navitem.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('client/homebrew/navbar/nav.jsx');
|
||||||
|
|
||||||
const ErrorNavItem = ({ error = '', clearError })=>{
|
const ErrorNavItem = ({ error = '', clearError })=>{
|
||||||
const response = error.response;
|
const response = error.response;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const dedent = require('dedent-tabs').default;
|
const dedent = require('dedent-tabs').default;
|
||||||
|
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('client/homebrew/navbar/nav.jsx');
|
||||||
|
|
||||||
module.exports = function(props){
|
module.exports = function(props){
|
||||||
return <Nav.dropdown>
|
return <Nav.dropdown>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ const React = require('react');
|
|||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
const Moment = require('moment');
|
const Moment = require('moment');
|
||||||
|
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('client/homebrew/navbar/nav.jsx');
|
||||||
|
|
||||||
|
|
||||||
const MetadataNav = createClass({
|
const MetadataNav = createClass({
|
||||||
@@ -32,7 +32,7 @@ const MetadataNav = createClass({
|
|||||||
return <>
|
return <>
|
||||||
{this.props.brew.authors.map((author, idx, arr)=>{
|
{this.props.brew.authors.map((author, idx, arr)=>{
|
||||||
const spacer = arr.length - 1 == idx ? <></> : <span>, </span>;
|
const spacer = arr.length - 1 == idx ? <></> : <span>, </span>;
|
||||||
return <span key={idx}><a className='userPageLink' href={`/user/${author}`}>{author}</a>{spacer}</span>;
|
return <span key={idx}><a className='userPageLink' href={`/user/${encodeURIComponent(author)}`}>{author}</a>{spacer}</span>;
|
||||||
})}
|
})}
|
||||||
</>;
|
</>;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const createClass = require('create-react-class');
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const cx = require('classnames');
|
const cx = require('classnames');
|
||||||
|
|
||||||
const NaturalCritIcon = require('naturalcrit/svg/naturalcrit.svg.jsx');
|
const NaturalCritIcon = require('client/components/svg/naturalcrit-d20.svg.jsx');
|
||||||
|
|
||||||
const Nav = {
|
const Nav = {
|
||||||
base : createClass({
|
base : createClass({
|
||||||
@@ -2,7 +2,7 @@ require('./navbar.less');
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
|
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('client/homebrew/navbar/nav.jsx');
|
||||||
const PatreonNavItem = require('./patreon.navitem.jsx');
|
const PatreonNavItem = require('./patreon.navitem.jsx');
|
||||||
|
|
||||||
const Navbar = createClass({
|
const Navbar = createClass({
|
||||||
|
|||||||
@@ -1,21 +1,18 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('client/homebrew/navbar/nav.jsx');
|
||||||
const { splitTextStyleAndMetadata } = require('../../../shared/helpers.js'); // Importing the function from helpers.js
|
const { splitTextStyleAndMetadata } = require('../../../shared/helpers.js'); // Importing the function from helpers.js
|
||||||
|
|
||||||
const BREWKEY = 'homebrewery-new';
|
const BREWKEY = 'HB_newPage_content';
|
||||||
const STYLEKEY = 'homebrewery-new-style';
|
const STYLEKEY = 'HB_newPage_style';
|
||||||
const METAKEY = 'homebrewery-new-meta';
|
const METAKEY = 'HB_newPage_meta';
|
||||||
|
|
||||||
const NewBrew = ()=>{
|
const NewBrew = ()=>{
|
||||||
const handleFileChange = (e)=>{
|
const handleFileChange = (e)=>{
|
||||||
const file = e.target.files[0];
|
const file = e.target.files[0];
|
||||||
if(!file) return;
|
if(!file) return;
|
||||||
|
|
||||||
const currentNew = localStorage.getItem(BREWKEY);
|
if(!confirmLocalStorageChange()) return;
|
||||||
if(currentNew && !confirm(
|
|
||||||
`You have some text in the new brew space, if you load a file that text will be lost, are you sure you want to load the file?`
|
|
||||||
)) return;
|
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = (e)=>{
|
reader.onload = (e)=>{
|
||||||
@@ -37,12 +34,35 @@ const NewBrew = ()=>{
|
|||||||
|
|
||||||
alert(`This file is invalid: ${!type ? 'Missing file extension' :`.${type} files are not supported`}. Only .txt files exported from the Homebrewery are allowed.`);
|
alert(`This file is invalid: ${!type ? 'Missing file extension' :`.${type} files are not supported`}. Only .txt files exported from the Homebrewery are allowed.`);
|
||||||
|
|
||||||
|
|
||||||
console.log(file);
|
console.log(file);
|
||||||
};
|
};
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const confirmLocalStorageChange = ()=>{
|
||||||
|
const currentText = localStorage.getItem(BREWKEY);
|
||||||
|
const currentStyle = localStorage.getItem(STYLEKEY);
|
||||||
|
const currentMeta = localStorage.getItem(METAKEY);
|
||||||
|
|
||||||
|
// TRUE if no data in any local storage key
|
||||||
|
// TRUE if data in any local storage key AND approval given
|
||||||
|
// FALSE if data in any local storage key AND approval declined
|
||||||
|
return (!(currentText || currentStyle || currentMeta) || confirm(
|
||||||
|
`You have made changes in the new brew space. If you continue, that information will be PERMANENTLY LOST.\nAre you sure you wish to continue?`
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearLocalStorage = ()=>{
|
||||||
|
if(!confirmLocalStorageChange()) return;
|
||||||
|
|
||||||
|
localStorage.removeItem(BREWKEY);
|
||||||
|
localStorage.removeItem(STYLEKEY);
|
||||||
|
localStorage.removeItem(METAKEY);
|
||||||
|
|
||||||
|
window.location.href = '/new';
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Nav.dropdown>
|
<Nav.dropdown>
|
||||||
@@ -50,24 +70,31 @@ const NewBrew = ()=>{
|
|||||||
className='new'
|
className='new'
|
||||||
color='purple'
|
color='purple'
|
||||||
icon='fa-solid fa-plus-square'>
|
icon='fa-solid fa-plus-square'>
|
||||||
new
|
new
|
||||||
</Nav.item>
|
</Nav.item>
|
||||||
<Nav.item
|
<Nav.item
|
||||||
className='fromBlank'
|
className='new'
|
||||||
href='/new'
|
href='/new'
|
||||||
newTab={true}
|
newTab={true}
|
||||||
color='purple'
|
color='purple'
|
||||||
icon='fa-solid fa-file'>
|
icon='fa-solid fa-file'>
|
||||||
from blank
|
resume draft
|
||||||
|
</Nav.item>
|
||||||
|
<Nav.item
|
||||||
|
className='fromBlank'
|
||||||
|
newTab={true}
|
||||||
|
color='yellow'
|
||||||
|
icon='fa-solid fa-file-circle-plus'
|
||||||
|
onClick={()=>{ clearLocalStorage(); }}>
|
||||||
|
from blank
|
||||||
</Nav.item>
|
</Nav.item>
|
||||||
|
|
||||||
<Nav.item
|
<Nav.item
|
||||||
className='fromFile'
|
className='fromFile'
|
||||||
color='purple'
|
color='green'
|
||||||
icon='fa-solid fa-upload'
|
icon='fa-solid fa-upload'
|
||||||
onClick={()=>{ document.getElementById('uploadTxt').click(); }}>
|
onClick={()=>{ document.getElementById('uploadTxt').click(); }}>
|
||||||
<input id='uploadTxt' className='newFromLocal' type='file' onChange={handleFileChange} style={{ display: 'none' }} />
|
<input id='uploadTxt' className='newFromLocal' type='file' onChange={handleFileChange} style={{ display: 'none' }} />
|
||||||
from file
|
from file
|
||||||
</Nav.item>
|
</Nav.item>
|
||||||
</Nav.dropdown>
|
</Nav.dropdown>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('client/homebrew/navbar/nav.jsx');
|
||||||
|
|
||||||
module.exports = function(props){
|
module.exports = function(props){
|
||||||
return <Nav.item
|
return <Nav.item
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('client/homebrew/navbar/nav.jsx');
|
||||||
const { printCurrentBrew } = require('../../../shared/helpers.js');
|
const { printCurrentBrew } = require('../../../shared/helpers.js');
|
||||||
|
|
||||||
module.exports = function(){
|
module.exports = function(){
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const createClass = require('create-react-class');
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const Moment = require('moment');
|
const Moment = require('moment');
|
||||||
|
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('client/homebrew/navbar/nav.jsx');
|
||||||
|
|
||||||
const EDIT_KEY = 'HB_nav_recentlyEdited';
|
const EDIT_KEY = 'HB_nav_recentlyEdited';
|
||||||
const VIEW_KEY = 'HB_nav_recentlyViewed';
|
const VIEW_KEY = 'HB_nav_recentlyViewed';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import dedent from 'dedent-tabs';
|
import dedent from 'dedent-tabs';
|
||||||
import Nav from 'naturalcrit/nav/nav.jsx';
|
import Nav from 'client/homebrew/navbar/nav.jsx';
|
||||||
|
|
||||||
const getShareId = (brew)=>(
|
const getShareId = (brew)=>(
|
||||||
brew.googleId && !brew.stubbed
|
brew.googleId && !brew.stubbed
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('client/homebrew/navbar/nav.jsx');
|
||||||
|
|
||||||
module.exports = function (props) {
|
module.exports = function (props) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const UIPage = require('../basePages/uiPage/uiPage.jsx');
|
const UIPage = require('../basePages/uiPage/uiPage.jsx');
|
||||||
const NaturalCritIcon = require('naturalcrit/svg/naturalcrit.svg.jsx');
|
const NaturalCritIcon = require('client/components/svg/naturalcrit-d20.svg.jsx');
|
||||||
|
|
||||||
let SAVEKEY = '';
|
let SAVEKEY = '';
|
||||||
|
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ const BrewItem = ({
|
|||||||
<span title="Username contained an email address; hidden to protect user's privacy">
|
<span title="Username contained an email address; hidden to protect user's privacy">
|
||||||
{author}
|
{author}
|
||||||
</span>
|
</span>
|
||||||
) : (<a href={`/user/${author}`}>{author}</a>)}
|
) : (<a href={`/user/${encodeURIComponent(author)}`}>{author}</a>)}
|
||||||
{index < brew.authors.length - 1 && ', '}
|
{index < brew.authors.length - 1 && ', '}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ require('./uiPage.less');
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
|
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('client/homebrew/navbar/nav.jsx');
|
||||||
const Navbar = require('../../../navbar/navbar.jsx');
|
const Navbar = require('client/homebrew/navbar/navbar.jsx');
|
||||||
const NewBrewItem = require('../../../navbar/newbrew.navitem.jsx');
|
const NewBrewItem = require('client/homebrew/navbar/newbrew.navitem.jsx');
|
||||||
const HelpNavItem = require('../../../navbar/help.navitem.jsx');
|
const HelpNavItem = require('client/homebrew/navbar/help.navitem.jsx');
|
||||||
const RecentNavItem = require('../../../navbar/recent.navitem.jsx').both;
|
const RecentNavItem = require('client/homebrew/navbar/recent.navitem.jsx').both;
|
||||||
const Account = require('../../../navbar/account.navitem.jsx');
|
const Account = require('client/homebrew/navbar/account.navitem.jsx');
|
||||||
|
|
||||||
|
|
||||||
const UIPage = createClass({
|
const UIPage = createClass({
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import './editPage.less';
|
|||||||
// Common imports
|
// Common imports
|
||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import request from '../../utils/request-middleware.js';
|
import request from '../../utils/request-middleware.js';
|
||||||
import Markdown from 'naturalcrit/markdown.js';
|
import Markdown from 'markdown.js';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import { DEFAULT_BREW_LOAD } from '../../../../server/brewDefaults.js';
|
import { DEFAULT_BREW_LOAD } from '../../../../server/brewDefaults.js';
|
||||||
@@ -14,15 +14,15 @@ import SplitPane from 'client/components/splitPane/splitPane.jsx';
|
|||||||
import Editor from '../../editor/editor.jsx';
|
import Editor from '../../editor/editor.jsx';
|
||||||
import BrewRenderer from '../../brewRenderer/brewRenderer.jsx';
|
import BrewRenderer from '../../brewRenderer/brewRenderer.jsx';
|
||||||
|
|
||||||
import Nav from 'naturalcrit/nav/nav.jsx';
|
import Nav from 'client/homebrew/navbar/nav.jsx';
|
||||||
import Navbar from '../../navbar/navbar.jsx';
|
import Navbar from 'client/homebrew/navbar/navbar.jsx';
|
||||||
import NewBrewItem from '../../navbar/newbrew.navitem.jsx';
|
import NewBrewItem from 'client/homebrew/navbar/newbrew.navitem.jsx';
|
||||||
import AccountNavItem from '../../navbar/account.navitem.jsx';
|
import AccountNavItem from 'client/homebrew/navbar/account.navitem.jsx';
|
||||||
import ErrorNavItem from '../../navbar/error-navitem.jsx';
|
import ErrorNavItem from 'client/homebrew/navbar/error-navitem.jsx';
|
||||||
import HelpNavItem from '../../navbar/help.navitem.jsx';
|
import HelpNavItem from 'client/homebrew/navbar/help.navitem.jsx';
|
||||||
import VaultNavItem from '../../navbar/vault.navitem.jsx';
|
import VaultNavItem from 'client/homebrew/navbar/vault.navitem.jsx';
|
||||||
import PrintNavItem from '../../navbar/print.navitem.jsx';
|
import PrintNavItem from 'client/homebrew/navbar/print.navitem.jsx';
|
||||||
import { both as RecentNavItem } from '../../navbar/recent.navitem.jsx';
|
import { both as RecentNavItem } from 'client/homebrew/navbar/recent.navitem.jsx';
|
||||||
|
|
||||||
// Page specific imports
|
// Page specific imports
|
||||||
import { Meta } from 'vitreum/headtags';
|
import { Meta } from 'vitreum/headtags';
|
||||||
@@ -30,7 +30,7 @@ import { md5 } from 'hash-wasm';
|
|||||||
import { gzipSync, strToU8 } from 'fflate';
|
import { gzipSync, strToU8 } from 'fflate';
|
||||||
import { makePatches, stringifyPatches } from '@sanity/diff-match-patch';
|
import { makePatches, stringifyPatches } from '@sanity/diff-match-patch';
|
||||||
|
|
||||||
import ShareNavItem from '../../navbar/share.navitem.jsx';
|
import ShareNavItem from 'client/homebrew/navbar/share.navitem.jsx';
|
||||||
import LockNotification from './lockNotification/lockNotification.jsx';
|
import LockNotification from './lockNotification/lockNotification.jsx';
|
||||||
import { updateHistory, versionHistoryGarbageCollection } from '../../utils/versionHistory.js';
|
import { updateHistory, versionHistoryGarbageCollection } from '../../utils/versionHistory.js';
|
||||||
import googleDriveIcon from '../../googleDrive.svg';
|
import googleDriveIcon from '../../googleDrive.svg';
|
||||||
@@ -119,6 +119,10 @@ const EditPage = (props)=>{
|
|||||||
if(autoSaveEnabled) trySave(false, hasChange);
|
if(autoSaveEnabled) trySave(false, hasChange);
|
||||||
}, [currentBrew]);
|
}, [currentBrew]);
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
trySave(true);
|
||||||
|
}, [saveGoogle]);
|
||||||
|
|
||||||
const handleSplitMove = ()=>{
|
const handleSplitMove = ()=>{
|
||||||
editorRef.current?.update();
|
editorRef.current?.update();
|
||||||
};
|
};
|
||||||
@@ -179,7 +183,6 @@ const EditPage = (props)=>{
|
|||||||
const toggleGoogleStorage = ()=>{
|
const toggleGoogleStorage = ()=>{
|
||||||
setSaveGoogle((prev)=>!prev);
|
setSaveGoogle((prev)=>!prev);
|
||||||
setError(null);
|
setError(null);
|
||||||
trySave(true);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const trySave = (immediate = false, hasChanges = true)=>{
|
const trySave = (immediate = false, hasChanges = true)=>{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
require('./errorPage.less');
|
require('./errorPage.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const UIPage = require('../basePages/uiPage/uiPage.jsx');
|
const UIPage = require('../basePages/uiPage/uiPage.jsx');
|
||||||
import Markdown from '../../../../shared/naturalcrit/markdown.js';
|
import Markdown from '../../../../shared/markdown.js';
|
||||||
const ErrorIndex = require('./errors/errorIndex.js');
|
const ErrorIndex = require('./errors/errorIndex.js');
|
||||||
|
|
||||||
const ErrorPage = ({ brew })=>{
|
const ErrorPage = ({ brew })=>{
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ const errorIndex = (props)=>{
|
|||||||
|
|
||||||
**Brew Title:** ${escape(props.brew.brewTitle) || 'Unable to show title'}
|
**Brew Title:** ${escape(props.brew.brewTitle) || 'Unable to show title'}
|
||||||
|
|
||||||
**Current Authors:** ${props.brew.authors?.map((author)=>{return `[${author}](/user/${author})`;}).join(', ') || 'Unable to list authors'}
|
**Current Authors:** ${props.brew.authors?.map((author)=>{return `[${author}](/user/${encodeURIComponent(author)})`;}).join(', ') || 'Unable to list authors'}
|
||||||
|
|
||||||
[Click here to be redirected to the brew's share page.](/share/${props.brew.shareId})`,
|
[Click here to be redirected to the brew's share page.](/share/${props.brew.shareId})`,
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ const errorIndex = (props)=>{
|
|||||||
|
|
||||||
**Brew Title:** ${escape(props.brew.brewTitle) || 'Unable to show title'}
|
**Brew Title:** ${escape(props.brew.brewTitle) || 'Unable to show title'}
|
||||||
|
|
||||||
**Current Authors:** ${props.brew.authors?.map((author)=>{return `[${author}](/user/${author})`;}).join(', ') || 'Unable to list authors'}
|
**Current Authors:** ${props.brew.authors?.map((author)=>{return `[${author}](/user/${encodeURIComponent(author)})`;}).join(', ') || 'Unable to list authors'}
|
||||||
|
|
||||||
[Click here to be redirected to the brew's share page.](/share/${props.brew.shareId})`,
|
[Click here to be redirected to the brew's share page.](/share/${props.brew.shareId})`,
|
||||||
|
|
||||||
@@ -222,7 +222,7 @@ const errorIndex = (props)=>{
|
|||||||
|
|
||||||
**Brew Title:** ${escape(props.brew.brewTitle)}
|
**Brew Title:** ${escape(props.brew.brewTitle)}
|
||||||
|
|
||||||
**Brew Authors:** ${props.brew.authors?.map((author)=>{return `[${author}](/user/${author})`;}).join(', ') || 'Unable to list authors'}`,
|
**Brew Authors:** ${props.brew.authors?.map((author)=>{return `[${author}](/user/${encodeURIComponent(author)})`;}).join(', ') || 'Unable to list authors'}`,
|
||||||
|
|
||||||
// ####### Admin page error #######
|
// ####### Admin page error #######
|
||||||
'52' : dedent`
|
'52' : dedent`
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import './homePage.less';
|
|||||||
// Common imports
|
// Common imports
|
||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import request from '../../utils/request-middleware.js';
|
import request from '../../utils/request-middleware.js';
|
||||||
import Markdown from 'naturalcrit/markdown.js';
|
import Markdown from 'markdown.js';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import { DEFAULT_BREW } from '../../../../server/brewDefaults.js';
|
import { DEFAULT_BREW } from '../../../../server/brewDefaults.js';
|
||||||
@@ -14,15 +14,15 @@ import SplitPane from 'client/components/splitPane/splitPane.jsx';
|
|||||||
import Editor from '../../editor/editor.jsx';
|
import Editor from '../../editor/editor.jsx';
|
||||||
import BrewRenderer from '../../brewRenderer/brewRenderer.jsx';
|
import BrewRenderer from '../../brewRenderer/brewRenderer.jsx';
|
||||||
|
|
||||||
import Nav from 'naturalcrit/nav/nav.jsx';
|
import Nav from 'client/homebrew/navbar/nav.jsx';
|
||||||
import Navbar from '../../navbar/navbar.jsx';
|
import Navbar from 'client/homebrew/navbar/navbar.jsx';
|
||||||
import NewBrewItem from '../../navbar/newbrew.navitem.jsx';
|
import NewBrewItem from 'client/homebrew/navbar/newbrew.navitem.jsx';
|
||||||
import AccountNavItem from '../../navbar/account.navitem.jsx';
|
import AccountNavItem from 'client/homebrew/navbar/account.navitem.jsx';
|
||||||
import ErrorNavItem from '../../navbar/error-navitem.jsx';
|
import ErrorNavItem from 'client/homebrew/navbar/error-navitem.jsx';
|
||||||
import HelpNavItem from '../../navbar/help.navitem.jsx';
|
import HelpNavItem from 'client/homebrew/navbar/help.navitem.jsx';
|
||||||
import VaultNavItem from '../../navbar/vault.navitem.jsx';
|
import VaultNavItem from 'client/homebrew/navbar/vault.navitem.jsx';
|
||||||
import PrintNavItem from '../../navbar/print.navitem.jsx';
|
import PrintNavItem from 'client/homebrew/navbar/print.navitem.jsx';
|
||||||
import { both as RecentNavItem } from '../../navbar/recent.navitem.jsx';
|
import { both as RecentNavItem } from 'client/homebrew/navbar/recent.navitem.jsx';
|
||||||
|
|
||||||
// Page specific imports
|
// Page specific imports
|
||||||
import { Meta } from 'vitreum/headtags';
|
import { Meta } from 'vitreum/headtags';
|
||||||
@@ -53,8 +53,9 @@ const HomePage =(props)=>{
|
|||||||
const [isSaving , setIsSaving] = useState(false);
|
const [isSaving , setIsSaving] = useState(false);
|
||||||
const [autoSaveEnabled , setAutoSaveEnable] = useState(false);
|
const [autoSaveEnabled , setAutoSaveEnable] = useState(false);
|
||||||
|
|
||||||
const editorRef = useRef(null);
|
const editorRef = useRef(null);
|
||||||
const lastSavedBrew = useRef(_.cloneDeep(props.brew));
|
const lastSavedBrew = useRef(_.cloneDeep(props.brew));
|
||||||
|
const unsavedChangesRef = useRef(unsavedChanges);
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
fetchThemeBundle(setError, setThemeBundle, currentBrew.renderer, currentBrew.theme);
|
fetchThemeBundle(setError, setThemeBundle, currentBrew.renderer, currentBrew.theme);
|
||||||
@@ -70,12 +71,20 @@ const HomePage =(props)=>{
|
|||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('keydown', handleControlKeys);
|
document.addEventListener('keydown', handleControlKeys);
|
||||||
|
window.onbeforeunload = ()=>{
|
||||||
|
if(unsavedChangesRef.current)
|
||||||
|
return 'You have unsaved changes!';
|
||||||
|
};
|
||||||
return ()=>{
|
return ()=>{
|
||||||
document.removeEventListener('keydown', handleControlKeys);
|
document.removeEventListener('keydown', handleControlKeys);
|
||||||
|
window.onbeforeunload = null;
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
unsavedChangesRef.current = unsavedChanges;
|
||||||
|
}, [unsavedChanges]);
|
||||||
|
|
||||||
const save = ()=>{
|
const save = ()=>{
|
||||||
request.post('/api')
|
request.post('/api')
|
||||||
.send(currentBrew)
|
.send(currentBrew)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import './newPage.less';
|
|||||||
// Common imports
|
// Common imports
|
||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import request from '../../utils/request-middleware.js';
|
import request from '../../utils/request-middleware.js';
|
||||||
import Markdown from 'naturalcrit/markdown.js';
|
import Markdown from 'markdown.js';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import { DEFAULT_BREW } from '../../../../server/brewDefaults.js';
|
import { DEFAULT_BREW } from '../../../../server/brewDefaults.js';
|
||||||
@@ -14,15 +14,15 @@ import SplitPane from 'client/components/splitPane/splitPane.jsx';
|
|||||||
import Editor from '../../editor/editor.jsx';
|
import Editor from '../../editor/editor.jsx';
|
||||||
import BrewRenderer from '../../brewRenderer/brewRenderer.jsx';
|
import BrewRenderer from '../../brewRenderer/brewRenderer.jsx';
|
||||||
|
|
||||||
import Nav from 'naturalcrit/nav/nav.jsx';
|
import Nav from 'client/homebrew/navbar/nav.jsx';
|
||||||
import Navbar from '../../navbar/navbar.jsx';
|
import Navbar from 'client/homebrew/navbar/navbar.jsx';
|
||||||
import NewBrewItem from '../../navbar/newbrew.navitem.jsx';
|
import NewBrewItem from 'client/homebrew/navbar/newbrew.navitem.jsx';
|
||||||
import AccountNavItem from '../../navbar/account.navitem.jsx';
|
import AccountNavItem from 'client/homebrew/navbar/account.navitem.jsx';
|
||||||
import ErrorNavItem from '../../navbar/error-navitem.jsx';
|
import ErrorNavItem from 'client/homebrew/navbar/error-navitem.jsx';
|
||||||
import HelpNavItem from '../../navbar/help.navitem.jsx';
|
import HelpNavItem from 'client/homebrew/navbar/help.navitem.jsx';
|
||||||
import VaultNavItem from '../../navbar/vault.navitem.jsx';
|
import VaultNavItem from 'client/homebrew/navbar/vault.navitem.jsx';
|
||||||
import PrintNavItem from '../../navbar/print.navitem.jsx';
|
import PrintNavItem from 'client/homebrew/navbar/print.navitem.jsx';
|
||||||
import { both as RecentNavItem } from '../../navbar/recent.navitem.jsx';
|
import { both as RecentNavItem } from 'client/homebrew/navbar/recent.navitem.jsx';
|
||||||
|
|
||||||
// Page specific imports
|
// Page specific imports
|
||||||
import { Meta } from 'vitreum/headtags';
|
import { Meta } from 'vitreum/headtags';
|
||||||
@@ -56,6 +56,10 @@ const NewPage = (props)=>{
|
|||||||
|
|
||||||
const editorRef = useRef(null);
|
const editorRef = useRef(null);
|
||||||
const lastSavedBrew = useRef(_.cloneDeep(props.brew));
|
const lastSavedBrew = useRef(_.cloneDeep(props.brew));
|
||||||
|
// const saveTimeout = useRef(null);
|
||||||
|
// const warnUnsavedTimeout = useRef(null);
|
||||||
|
const trySaveRef = useRef(trySave); // CTRL+S listener lives outside React and needs ref to use trySave with latest copy of brew
|
||||||
|
const unsavedChangesRef = useRef(unsavedChanges); // Similarly, onBeforeUnload lives outside React and needs ref to unsavedChanges
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
loadBrew();
|
loadBrew();
|
||||||
@@ -114,6 +118,11 @@ const NewPage = (props)=>{
|
|||||||
if(autoSaveEnabled) trySave(false, hasChange);
|
if(autoSaveEnabled) trySave(false, hasChange);
|
||||||
}, [currentBrew]);
|
}, [currentBrew]);
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
trySaveRef.current = trySave;
|
||||||
|
unsavedChangesRef.current = unsavedChanges;
|
||||||
|
});
|
||||||
|
|
||||||
const handleSplitMove = ()=>{
|
const handleSplitMove = ()=>{
|
||||||
editorRef.current.update();
|
editorRef.current.update();
|
||||||
};
|
};
|
||||||
@@ -141,7 +150,7 @@ const NewPage = (props)=>{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const save = async ()=>{
|
const trySave = async ()=>{
|
||||||
setIsSaving(true);
|
setIsSaving(true);
|
||||||
|
|
||||||
const updatedBrew = { ...currentBrew };
|
const updatedBrew = { ...currentBrew };
|
||||||
@@ -190,7 +199,7 @@ const NewPage = (props)=>{
|
|||||||
|
|
||||||
// #3 - Unsaved changes exist, click to save, show SAVE NOW
|
// #3 - Unsaved changes exist, click to save, show SAVE NOW
|
||||||
if(unsavedChanges)
|
if(unsavedChanges)
|
||||||
return <Nav.item className='save' onClick={save} color='blue' icon='fas fa-save'>save now</Nav.item>;
|
return <Nav.item className='save' onClick={trySave} color='blue' icon='fas fa-save'>save now</Nav.item>;
|
||||||
|
|
||||||
// #4 - No unsaved changes, autosave is ON, show AUTO-SAVED
|
// #4 - No unsaved changes, autosave is ON, show AUTO-SAVED
|
||||||
if(autoSaveEnabled)
|
if(autoSaveEnabled)
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
.newPage {
|
.newPage {
|
||||||
.navItem.save {
|
.navItem.save {
|
||||||
.fadeInRight();
|
|
||||||
.transition(opacity);
|
|
||||||
background-color : @orange;
|
background-color : @orange;
|
||||||
|
transition:all 0.2s;
|
||||||
&:hover { background-color : @green; }
|
&:hover { background-color : @green; }
|
||||||
|
|
||||||
&.neverSaved {
|
&.neverSaved {
|
||||||
.fadeOutRight();
|
translate:-100%;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
background-color :#333;
|
||||||
|
cursor:auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ const React = require('react');
|
|||||||
const { useState, useEffect, useCallback } = React;
|
const { useState, useEffect, useCallback } = React;
|
||||||
const { Meta } = require('vitreum/headtags');
|
const { Meta } = require('vitreum/headtags');
|
||||||
|
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('client/homebrew/navbar/nav.jsx');
|
||||||
const Navbar = require('../../navbar/navbar.jsx');
|
const Navbar = require('client/homebrew/navbar/navbar.jsx');
|
||||||
const MetadataNav = require('../../navbar/metadata.navitem.jsx');
|
const MetadataNav = require('client/homebrew/navbar/metadata.navitem.jsx');
|
||||||
const PrintNavItem = require('../../navbar/print.navitem.jsx');
|
const PrintNavItem = require('client/homebrew/navbar/print.navitem.jsx');
|
||||||
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
const RecentNavItem = require('client/homebrew/navbar/recent.navitem.jsx').both;
|
||||||
const Account = require('../../navbar/account.navitem.jsx');
|
const Account = require('client/homebrew/navbar/account.navitem.jsx');
|
||||||
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
|
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
|
||||||
|
|
||||||
const { DEFAULT_BREW_LOAD } = require('../../../../server/brewDefaults.js');
|
const { DEFAULT_BREW_LOAD } = require('../../../../server/brewDefaults.js');
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ const _ = require('lodash');
|
|||||||
|
|
||||||
const ListPage = require('../basePages/listPage/listPage.jsx');
|
const ListPage = require('../basePages/listPage/listPage.jsx');
|
||||||
|
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('client/homebrew/navbar/nav.jsx');
|
||||||
const Navbar = require('../../navbar/navbar.jsx');
|
const Navbar = require('client/homebrew/navbar/navbar.jsx');
|
||||||
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
const RecentNavItem = require('client/homebrew/navbar/recent.navitem.jsx').both;
|
||||||
const Account = require('../../navbar/account.navitem.jsx');
|
const Account = require('client/homebrew/navbar/account.navitem.jsx');
|
||||||
const NewBrew = require('../../navbar/newbrew.navitem.jsx');
|
const NewBrew = require('client/homebrew/navbar/newbrew.navitem.jsx');
|
||||||
const HelpNavItem = require('../../navbar/help.navitem.jsx');
|
const HelpNavItem = require('client/homebrew/navbar/help.navitem.jsx');
|
||||||
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
|
const ErrorNavItem = require('client/homebrew/navbar/error-navitem.jsx');
|
||||||
const VaultNavitem = require('../../navbar/vault.navitem.jsx');
|
const VaultNavitem = require('client/homebrew/navbar/vault.navitem.jsx');
|
||||||
|
|
||||||
const UserPage = (props)=>{
|
const UserPage = (props)=>{
|
||||||
props = {
|
props = {
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ require('./vaultPage.less');
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { useState, useEffect, useRef } = React;
|
const { useState, useEffect, useRef } = React;
|
||||||
|
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('client/homebrew/navbar/nav.jsx');
|
||||||
const Navbar = require('../../navbar/navbar.jsx');
|
const Navbar = require('client/homebrew/navbar/navbar.jsx');
|
||||||
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
|
const RecentNavItem = require('client/homebrew/navbar/recent.navitem.jsx').both;
|
||||||
const Account = require('../../navbar/account.navitem.jsx');
|
const Account = require('client/homebrew/navbar/account.navitem.jsx');
|
||||||
const NewBrew = require('../../navbar/newbrew.navitem.jsx');
|
const NewBrew = require('client/homebrew/navbar/newbrew.navitem.jsx');
|
||||||
const HelpNavItem = require('../../navbar/help.navitem.jsx');
|
const HelpNavItem = require('client/homebrew/navbar/help.navitem.jsx');
|
||||||
const BrewItem = require('../basePages/listPage/brewItem/brewItem.jsx');
|
const BrewItem = require('../basePages/listPage/brewItem/brewItem.jsx');
|
||||||
const SplitPane = require('client/components/splitPane/splitPane.jsx');
|
const SplitPane = require('client/components/splitPane/splitPane.jsx');
|
||||||
const ErrorIndex = require('../errorPage/errors/errorIndex.js');
|
const ErrorIndex = require('../errorPage/errors/errorIndex.js');
|
||||||
|
|||||||
2343
package-lock.json
generated
2343
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
27
package.json
27
package.json
@@ -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.19.3",
|
"version": "3.20.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
"npm": "^10.8.x",
|
"npm": "^10.8.x",
|
||||||
@@ -44,7 +44,9 @@
|
|||||||
"phb": "node --experimental-require-module scripts/phb.js",
|
"phb": "node --experimental-require-module scripts/phb.js",
|
||||||
"prod": "set NODE_ENV=production && npm run build",
|
"prod": "set NODE_ENV=production && npm run build",
|
||||||
"postinstall": "npm run build",
|
"postinstall": "npm run build",
|
||||||
"start": "node --experimental-require-module server.js"
|
"start": "node --experimental-require-module server.js",
|
||||||
|
"docker:build": "docker build -t ${DOCKERID}/homebrewery:$npm_package_version .",
|
||||||
|
"docker:publish": "docker login && docker push ${DOCKERID}/homebrewery:$npm_package_version"
|
||||||
},
|
},
|
||||||
"author": "stolksdorf",
|
"author": "stolksdorf",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -86,16 +88,16 @@
|
|||||||
"@babel/core": "^7.28.4",
|
"@babel/core": "^7.28.4",
|
||||||
"@babel/plugin-transform-runtime": "^7.28.3",
|
"@babel/plugin-transform-runtime": "^7.28.3",
|
||||||
"@babel/preset-env": "^7.28.3",
|
"@babel/preset-env": "^7.28.3",
|
||||||
"@babel/preset-react": "^7.27.1",
|
"@babel/preset-react": "^7.28.5",
|
||||||
"@babel/runtime": "^7.28.4",
|
"@babel/runtime": "^7.28.4",
|
||||||
"@dmsnell/diff-match-patch": "^1.1.0",
|
"@dmsnell/diff-match-patch": "^1.1.0",
|
||||||
"@googleapis/drive": "^18.0.0",
|
"@googleapis/drive": "^19.2.0",
|
||||||
"@sanity/diff-match-patch": "^3.2.0",
|
"@sanity/diff-match-patch": "^3.2.0",
|
||||||
"body-parser": "^2.2.0",
|
"body-parser": "^2.2.0",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
"codemirror": "^5.65.6",
|
"codemirror": "^5.65.6",
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
"core-js": "^3.46.0",
|
"core-js": "^3.47.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"create-react-class": "^15.7.0",
|
"create-react-class": "^15.7.0",
|
||||||
"dedent-tabs": "^0.10.3",
|
"dedent-tabs": "^0.10.3",
|
||||||
@@ -107,28 +109,29 @@
|
|||||||
"fs-extra": "11.3.2",
|
"fs-extra": "11.3.2",
|
||||||
"hash-wasm": "^4.12.0",
|
"hash-wasm": "^4.12.0",
|
||||||
"idb-keyval": "^6.2.2",
|
"idb-keyval": "^6.2.2",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.1",
|
||||||
"jwt-simple": "^0.5.6",
|
"jwt-simple": "^0.5.6",
|
||||||
"less": "^3.13.1",
|
"less": "^3.13.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"marked": "15.0.12",
|
"marked": "15.0.12",
|
||||||
"marked-alignment-paragraphs": "^1.0.0",
|
"marked-alignment-paragraphs": "^1.0.0",
|
||||||
"marked-definition-lists": "^1.0.1",
|
"marked-definition-lists": "^1.0.1",
|
||||||
"marked-emoji": "^2.0.1",
|
"marked-emoji": "^2.0.2",
|
||||||
"marked-extended-tables": "^2.0.1",
|
"marked-extended-tables": "^2.0.1",
|
||||||
"marked-gfm-heading-id": "^4.1.2",
|
"marked-gfm-heading-id": "^4.1.3",
|
||||||
"marked-nonbreaking-spaces": "^1.0.1",
|
"marked-nonbreaking-spaces": "^1.0.1",
|
||||||
"marked-smartypants-lite": "^1.0.3",
|
"marked-smartypants-lite": "^1.0.3",
|
||||||
"marked-subsuper-text": "^1.0.4",
|
"marked-subsuper-text": "^1.0.4",
|
||||||
|
"marked-variables": "^1.0.4",
|
||||||
"markedLegacy": "npm:marked@^0.3.19",
|
"markedLegacy": "npm:marked@^0.3.19",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"mongoose": "^8.19.1",
|
"mongoose": "^8.20.0",
|
||||||
"nanoid": "5.1.6",
|
"nanoid": "5.1.6",
|
||||||
"nconf": "^0.13.0",
|
"nconf": "^0.13.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-frame-component": "^4.1.3",
|
"react-frame-component": "^4.1.3",
|
||||||
"react-router": "^7.9.4",
|
"react-router": "^7.9.6",
|
||||||
"romans": "^3.1.0",
|
"romans": "^3.1.0",
|
||||||
"sanitize-filename": "1.6.3",
|
"sanitize-filename": "1.6.3",
|
||||||
"superagent": "^10.2.1",
|
"superagent": "^10.2.1",
|
||||||
@@ -138,8 +141,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@stylistic/stylelint-plugin": "^4.0.0",
|
"@stylistic/stylelint-plugin": "^4.0.0",
|
||||||
"babel-plugin-transform-import-meta": "^2.3.3",
|
"babel-plugin-transform-import-meta": "^2.3.3",
|
||||||
"eslint": "^9.37.0",
|
"eslint": "^9.39.1",
|
||||||
"eslint-plugin-jest": "^29.0.1",
|
"eslint-plugin-jest": "^29.1.0",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"globals": "^16.4.0",
|
"globals": "^16.4.0",
|
||||||
"jest": "^30.2.0",
|
"jest": "^30.2.0",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import config from './config.js';
|
|||||||
|
|
||||||
|
|
||||||
let serviceAuth;
|
let serviceAuth;
|
||||||
|
let clientEmail;
|
||||||
if(!config.get('service_account')){
|
if(!config.get('service_account')){
|
||||||
const reset = '\x1b[0m'; // Reset to default style
|
const reset = '\x1b[0m'; // Reset to default style
|
||||||
const yellow = '\x1b[33m'; // yellow color
|
const yellow = '\x1b[33m'; // yellow color
|
||||||
@@ -15,6 +16,10 @@ if(!config.get('service_account')){
|
|||||||
JSON.parse(config.get('service_account')) :
|
JSON.parse(config.get('service_account')) :
|
||||||
config.get('service_account');
|
config.get('service_account');
|
||||||
|
|
||||||
|
if(keys?.client_email) {
|
||||||
|
clientEmail = keys.client_email;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
serviceAuth = googleDrive.auth.fromJSON(keys);
|
serviceAuth = googleDrive.auth.fromJSON(keys);
|
||||||
serviceAuth.scopes = ['https://www.googleapis.com/auth/drive'];
|
serviceAuth.scopes = ['https://www.googleapis.com/auth/drive'];
|
||||||
@@ -227,14 +232,30 @@ const GoogleActions = {
|
|||||||
|
|
||||||
if(!obj) return;
|
if(!obj) return;
|
||||||
|
|
||||||
|
if(clientEmail) {
|
||||||
|
await drive.permissions.create({
|
||||||
|
resource : {
|
||||||
|
type : 'user',
|
||||||
|
emailAddress : clientEmail,
|
||||||
|
role : 'writer'
|
||||||
|
},
|
||||||
|
fileId : obj.data.id,
|
||||||
|
fields : 'id',
|
||||||
|
})
|
||||||
|
.catch((err)=>{
|
||||||
|
console.log('Error adding Service Account permissions on Google Drive file');
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await drive.permissions.create({
|
await drive.permissions.create({
|
||||||
resource : { type : 'anyone',
|
resource : { type : 'anyone',
|
||||||
role : 'writer' },
|
role : 'writer' },
|
||||||
fileId : obj.data.id,
|
fileId : obj.data.id,
|
||||||
fields : 'id',
|
fields : 'id',
|
||||||
})
|
})
|
||||||
.catch((err)=>{
|
.catch((err)=>{
|
||||||
console.log('Error updating permissions');
|
console.log('Error adding "Anyone" permissions on Google Drive file');
|
||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { model as HomebrewModel } from './homebrew.model.js';
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import zlib from 'zlib';
|
import zlib from 'zlib';
|
||||||
import GoogleActions from './googleActions.js';
|
import GoogleActions from './googleActions.js';
|
||||||
import Markdown from '../shared/naturalcrit/markdown.js';
|
import Markdown from '../shared/markdown.js';
|
||||||
import yaml from 'js-yaml';
|
import yaml from 'js-yaml';
|
||||||
import asyncHandler from 'express-async-handler';
|
import asyncHandler from 'express-async-handler';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
|
|||||||
@@ -1,117 +1,29 @@
|
|||||||
/* eslint-disable max-depth */
|
/* eslint-disable max-depth */
|
||||||
/* eslint-disable max-lines */
|
/* eslint-disable max-lines */
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { Parser as MathParser } from 'expr-eval';
|
|
||||||
import { marked as Marked } from 'marked';
|
import { marked as Marked } from 'marked';
|
||||||
import MarkedExtendedTables from 'marked-extended-tables';
|
import MarkedExtendedTables from 'marked-extended-tables';
|
||||||
import MarkedDefinitionLists from 'marked-definition-lists';
|
import MarkedDefinitionLists from 'marked-definition-lists';
|
||||||
import MarkedAlignedParagraphs from 'marked-alignment-paragraphs';
|
import MarkedAlignedParagraphs from 'marked-alignment-paragraphs';
|
||||||
import MarkedNonbreakingSpaces from 'marked-nonbreaking-spaces';
|
import MarkedNonbreakingSpaces from 'marked-nonbreaking-spaces';
|
||||||
import MarkedSubSuperText from 'marked-subsuper-text';
|
import MarkedSubSuperText from 'marked-subsuper-text';
|
||||||
|
import { markedVariables,
|
||||||
|
setMarkedVariablePage,
|
||||||
|
setMarkedVariable,
|
||||||
|
getMarkedVariable } from 'marked-variables';
|
||||||
import { markedSmartypantsLite as MarkedSmartypantsLite } from 'marked-smartypants-lite';
|
import { markedSmartypantsLite as MarkedSmartypantsLite } from 'marked-smartypants-lite';
|
||||||
import { gfmHeadingId as MarkedGFMHeadingId, resetHeadings as MarkedGFMResetHeadingIDs } from 'marked-gfm-heading-id';
|
import { gfmHeadingId as MarkedGFMHeadingId, resetHeadings as MarkedGFMResetHeadingIDs } from 'marked-gfm-heading-id';
|
||||||
import { markedEmoji as MarkedEmojis } from 'marked-emoji';
|
import { markedEmoji as MarkedEmojis } from 'marked-emoji';
|
||||||
import { romanize } from 'romans';
|
|
||||||
import writtenNumber from 'written-number';
|
|
||||||
|
|
||||||
//Icon fonts included so they can appear in emoji autosuggest dropdown
|
//Icon fonts included so they can appear in emoji autosuggest dropdown
|
||||||
import diceFont from '../../themes/fonts/iconFonts/diceFont.js';
|
import diceFont from '../themes/fonts/iconFonts/diceFont.js';
|
||||||
import elderberryInn from '../../themes/fonts/iconFonts/elderberryInn.js';
|
import elderberryInn from '../themes/fonts/iconFonts/elderberryInn.js';
|
||||||
import gameIcons from '../../themes/fonts/iconFonts/gameIcons.js';
|
import gameIcons from '../themes/fonts/iconFonts/gameIcons.js';
|
||||||
import fontAwesome from '../../themes/fonts/iconFonts/fontAwesome.js';
|
import fontAwesome from '../themes/fonts/iconFonts/fontAwesome.js';
|
||||||
|
|
||||||
const renderer = new Marked.Renderer();
|
const renderer = new Marked.Renderer();
|
||||||
const tokenizer = new Marked.Tokenizer();
|
const tokenizer = new Marked.Tokenizer();
|
||||||
|
|
||||||
//Limit math features to simple items
|
|
||||||
const mathParser = new MathParser({
|
|
||||||
operators : {
|
|
||||||
// These default to true, but are included to be explicit
|
|
||||||
add : true,
|
|
||||||
subtract : true,
|
|
||||||
multiply : true,
|
|
||||||
divide : true,
|
|
||||||
power : true,
|
|
||||||
round : true,
|
|
||||||
floor : true,
|
|
||||||
ceil : true,
|
|
||||||
abs : true,
|
|
||||||
|
|
||||||
sin : false, cos : false, tan : false, asin : false, acos : false,
|
|
||||||
atan : false, sinh : false, cosh : false, tanh : false, asinh : false,
|
|
||||||
acosh : false, atanh : false, sqrt : false, cbrt : false, log : false,
|
|
||||||
log2 : false, ln : false, lg : false, log10 : false, expm1 : false,
|
|
||||||
log1p : false, trunc : false, join : false, sum : false, indexOf : false,
|
|
||||||
'-' : false, '+' : false, exp : false, not : false, length : false,
|
|
||||||
'!' : false, sign : false, random : false, fac : false, min : false,
|
|
||||||
max : false, hypot : false, pyt : false, pow : false, atan2 : false,
|
|
||||||
'if' : false, gamma : false, roundTo : false, map : false, fold : false,
|
|
||||||
filter : false,
|
|
||||||
|
|
||||||
remainder : false, factorial : false,
|
|
||||||
comparison : false, concatenate : false,
|
|
||||||
logical : false, assignment : false,
|
|
||||||
array : false, fndef : false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Add sign function
|
|
||||||
mathParser.functions.sign = function (a) {
|
|
||||||
if(a >= 0) return '+';
|
|
||||||
return '-';
|
|
||||||
};
|
|
||||||
// Add signed function
|
|
||||||
mathParser.functions.signed = function (a) {
|
|
||||||
if(a >= 0) return `+${a}`;
|
|
||||||
return `${a}`;
|
|
||||||
};
|
|
||||||
// Add Roman numeral functions
|
|
||||||
mathParser.functions.toRomans = function (a) {
|
|
||||||
return romanize(a);
|
|
||||||
};
|
|
||||||
mathParser.functions.toRomansUpper = function (a) {
|
|
||||||
return romanize(a).toUpperCase();
|
|
||||||
};
|
|
||||||
mathParser.functions.toRomansLower = function (a) {
|
|
||||||
return romanize(a).toLowerCase();
|
|
||||||
};
|
|
||||||
// Add character functions
|
|
||||||
mathParser.functions.toChar = function (a) {
|
|
||||||
if(a <= 0) return a;
|
|
||||||
const genChars = function (i) {
|
|
||||||
return (i > 26 ? genChars(Math.floor((i - 1) / 26)) : '') + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[(i - 1) % 26];
|
|
||||||
};
|
|
||||||
return genChars(a);
|
|
||||||
};
|
|
||||||
mathParser.functions.toCharUpper = function (a) {
|
|
||||||
return mathParser.functions.toChar(a).toUpperCase();
|
|
||||||
};
|
|
||||||
mathParser.functions.toCharLower = function (a) {
|
|
||||||
return mathParser.functions.toChar(a).toLowerCase();
|
|
||||||
};
|
|
||||||
// Add word functions
|
|
||||||
mathParser.functions.toWords = function (a) {
|
|
||||||
return writtenNumber(a);
|
|
||||||
};
|
|
||||||
mathParser.functions.toWordsUpper = function (a) {
|
|
||||||
return mathParser.functions.toWords(a).toUpperCase();
|
|
||||||
};
|
|
||||||
mathParser.functions.toWordsLower = function (a) {
|
|
||||||
return mathParser.functions.toWords(a).toLowerCase();
|
|
||||||
};
|
|
||||||
mathParser.functions.toWordsCaps = function (a) {
|
|
||||||
const words = mathParser.functions.toWords(a).split(' ');
|
|
||||||
return words.map((word)=>{
|
|
||||||
return word.replace(/(?:^|\b|\s)(\w)/g, function(w, index) {
|
|
||||||
return index === 0 ? w.toLowerCase() : w.toUpperCase();
|
|
||||||
});
|
|
||||||
}).join(' ');
|
|
||||||
};
|
|
||||||
|
|
||||||
// Normalize variable names; trim edge spaces and shorten blocks of whitespace to 1 space
|
|
||||||
const normalizeVarNames = (label)=>{
|
|
||||||
return label.trim().replace(/\s+/g, ' ');
|
|
||||||
};
|
|
||||||
|
|
||||||
//Processes the markdown within an HTML block if it's just a class-wrapper
|
//Processes the markdown within an HTML block if it's just a class-wrapper
|
||||||
renderer.html = function (token) {
|
renderer.html = function (token) {
|
||||||
let html = token.text;
|
let html = token.text;
|
||||||
@@ -119,7 +31,12 @@ renderer.html = function (token) {
|
|||||||
const openTag = html.substring(0, html.indexOf('>')+1);
|
const openTag = html.substring(0, html.indexOf('>')+1);
|
||||||
html = html.substring(html.indexOf('>')+1);
|
html = html.substring(html.indexOf('>')+1);
|
||||||
html = html.substring(0, html.lastIndexOf('</div>'));
|
html = html.substring(0, html.lastIndexOf('</div>'));
|
||||||
return `${openTag} ${Marked.parse(html)} </div>`;
|
|
||||||
|
// Repeat the markdown processing for content inside the div, minus the preprocessing and postprocessing hooks which should only run once globally
|
||||||
|
const opts = Marked.defaults;
|
||||||
|
const tokens = Marked.lexer(html, opts);
|
||||||
|
Marked.walkTokens(tokens, opts.walkTokens);
|
||||||
|
return `${openTag} ${Marked.parser(tokens, opts)} </div>`;
|
||||||
}
|
}
|
||||||
return html;
|
return html;
|
||||||
};
|
};
|
||||||
@@ -411,248 +328,6 @@ const forcedParagraphBreaks = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//v=====--------------------< Variable Handling >-------------------=====v// 242 lines
|
|
||||||
const replaceVar = function(input, hoist=false, allowUnresolved=false) {
|
|
||||||
const regex = /([!$]?)\[((?!\s*\])(?:\\.|[^\[\]\\])+)\]/g;
|
|
||||||
const match = regex.exec(input);
|
|
||||||
|
|
||||||
const prefix = match[1];
|
|
||||||
const label = normalizeVarNames(match[2]); // Ensure the label name is normalized as it should be in the var stack.
|
|
||||||
|
|
||||||
//v=====--------------------< HANDLE MATH >-------------------=====v//
|
|
||||||
const mathRegex = /[a-z]+\(|[+\-*/^(),]/g;
|
|
||||||
const matches = label.split(mathRegex);
|
|
||||||
const mathVars = matches.filter((match)=>isNaN(match))?.map((s)=>s.trim()); // Capture any variable names
|
|
||||||
|
|
||||||
let replacedLabel = label;
|
|
||||||
|
|
||||||
if(prefix[0] == '$' && mathVars?.[0] !== label.trim()) {// If there was mathy stuff not captured, let's do math!
|
|
||||||
mathVars?.forEach((variable)=>{
|
|
||||||
const foundVar = lookupVar(variable, globalPageNumber, hoist);
|
|
||||||
if(foundVar && foundVar.resolved && foundVar.content && !isNaN(foundVar.content)) // Only subsitute math values if fully resolved, not empty strings, and numbers
|
|
||||||
replacedLabel = replacedLabel.replaceAll(new RegExp(`(?<!\\w)(${variable})(?!\\w)`, 'g'), foundVar.content);
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
return mathParser.evaluate(replacedLabel);
|
|
||||||
} catch {
|
|
||||||
return undefined; // Return undefined if invalid math result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//^=====--------------------< HANDLE MATH >-------------------=====^//
|
|
||||||
|
|
||||||
const foundVar = lookupVar(label, globalPageNumber, hoist);
|
|
||||||
|
|
||||||
if(!foundVar || (!foundVar.resolved && !allowUnresolved))
|
|
||||||
return undefined; // Return undefined if not found, or parially-resolved vars are not allowed
|
|
||||||
|
|
||||||
// url or <url> "title" or 'title' or (title)
|
|
||||||
const linkRegex = /^([^<\s][^\s]*|<.*?>)(?: ("(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\((?:\\\(|\\\)|[^()])*\)))?$/m;
|
|
||||||
const linkMatch = linkRegex.exec(foundVar.content);
|
|
||||||
|
|
||||||
const href = linkMatch ? linkMatch[1] : null; //TODO: TRIM OFF < > IF PRESENT
|
|
||||||
const title = linkMatch ? linkMatch[2]?.slice(1, -1) : null;
|
|
||||||
|
|
||||||
if(!prefix[0] && href) // Link
|
|
||||||
return `[${label}](${href}${title ? ` "${title}"` : ''})`;
|
|
||||||
|
|
||||||
if(prefix[0] == '!' && href) // Image
|
|
||||||
return ``;
|
|
||||||
|
|
||||||
if(prefix[0] == '$') // Variable
|
|
||||||
return foundVar.content;
|
|
||||||
};
|
|
||||||
|
|
||||||
const lookupVar = function(label, index, hoist=false) {
|
|
||||||
while (index >= 0) {
|
|
||||||
if(globalVarsList[index]?.[label] !== undefined)
|
|
||||||
return globalVarsList[index][label];
|
|
||||||
index--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(hoist) { //If normal lookup failed, attempt hoisting
|
|
||||||
index = Object.keys(globalVarsList).length; // Move index to start from last page
|
|
||||||
while (index >= 0) {
|
|
||||||
if(globalVarsList[index]?.[label] !== undefined)
|
|
||||||
return globalVarsList[index][label];
|
|
||||||
index--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
const processVariableQueue = function() {
|
|
||||||
let resolvedOne = true;
|
|
||||||
let finalLoop = false;
|
|
||||||
while (resolvedOne || finalLoop) { // Loop through queue until no more variable calls can be resolved
|
|
||||||
resolvedOne = false;
|
|
||||||
for (const item of varsQueue) {
|
|
||||||
if(item.type == 'text')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(item.type == 'varDefBlock') {
|
|
||||||
const regex = /[!$]?\[((?!\s*\])(?:\\.|[^\[\]\\])+)\]/g;
|
|
||||||
let match;
|
|
||||||
let resolved = true;
|
|
||||||
let tempContent = item.content;
|
|
||||||
while (match = regex.exec(item.content)) { // regex to find variable calls
|
|
||||||
const value = replaceVar(match[0], true);
|
|
||||||
|
|
||||||
if(value == undefined)
|
|
||||||
resolved = false;
|
|
||||||
else
|
|
||||||
tempContent = tempContent.replaceAll(match[0], value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(resolved == true || item.content != tempContent) {
|
|
||||||
resolvedOne = true;
|
|
||||||
item.content = tempContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
globalVarsList[globalPageNumber][item.varName] = {
|
|
||||||
content : item.content,
|
|
||||||
resolved : resolved
|
|
||||||
};
|
|
||||||
|
|
||||||
if(resolved)
|
|
||||||
item.type = 'resolved';
|
|
||||||
}
|
|
||||||
|
|
||||||
if(item.type == 'varCallBlock' || item.type == 'varCallInline') {
|
|
||||||
const value = replaceVar(item.content, true, finalLoop); // final loop will just use the best value so far
|
|
||||||
|
|
||||||
if(value == undefined)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
resolvedOne = true;
|
|
||||||
item.content = value;
|
|
||||||
item.type = 'text';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
varsQueue = varsQueue.filter((item)=>item.type !== 'resolved'); // Remove any fully-resolved variable definitions
|
|
||||||
|
|
||||||
if(finalLoop)
|
|
||||||
break;
|
|
||||||
if(!resolvedOne)
|
|
||||||
finalLoop = true;
|
|
||||||
}
|
|
||||||
varsQueue = varsQueue.filter((item)=>item.type !== 'varDefBlock');
|
|
||||||
};
|
|
||||||
|
|
||||||
function MarkedVariables() {
|
|
||||||
return {
|
|
||||||
hooks : {
|
|
||||||
preprocess(src) {
|
|
||||||
const codeBlockSkip = /^(?: {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+|^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})(?:[^\n]*)(?:\n|$)(?:|(?:[\s\S]*?)(?:\n|$))(?: {0,3}\2[~`]* *(?=\n|$))|`[^`]*?`/;
|
|
||||||
const blockDefRegex = /^[!$]?\[((?!\s*\])(?:\\.|[^\[\]\\])+)\]:(?!\() *((?:\n? *[^\s].*)+)(?=\n+|$)/; //Matches 3, [4]:5
|
|
||||||
const blockCallRegex = /^[!$]?\[((?!\s*\])(?:\\.|[^\[\]\\])+)\](?=\n|$)/; //Matches 6, [7]
|
|
||||||
const inlineDefRegex = /([!$]?\[((?!\s*\])(?:\\.|[^\[\]\\])+)\])\(([^\n]+)\)/; //Matches 8, 9[10](11)
|
|
||||||
const inlineCallRegex = /[!$]?\[((?!\s*\])(?:\\.|[^\[\]\\])+)\](?!\()/; //Matches 12, [13]
|
|
||||||
|
|
||||||
// Combine regexes and wrap in parens like so: (regex1)|(regex2)|(regex3)|(regex4)
|
|
||||||
const combinedRegex = new RegExp([codeBlockSkip, blockDefRegex, blockCallRegex, inlineDefRegex, inlineCallRegex].map((s)=>`(${s.source})`).join('|'), 'gm');
|
|
||||||
|
|
||||||
let lastIndex = 0;
|
|
||||||
let match;
|
|
||||||
while ((match = combinedRegex.exec(src)) !== null) {
|
|
||||||
// Format any matches into tokens and store
|
|
||||||
if(match.index > lastIndex) { // Any non-variable stuff
|
|
||||||
varsQueue.push(
|
|
||||||
{ type : 'text',
|
|
||||||
varName : null,
|
|
||||||
content : src.slice(lastIndex, match.index)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if(match[1]) {
|
|
||||||
varsQueue.push(
|
|
||||||
{ type : 'text',
|
|
||||||
varName : null,
|
|
||||||
content : match[0]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if(match[3]) { // Block Definition
|
|
||||||
const label = match[4] ? normalizeVarNames(match[4]) : null;
|
|
||||||
const content = match[5] ? match[5].trim().replace(/[ \t]+/g, ' ') : null; // Normalize text content (except newlines for block-level content)
|
|
||||||
|
|
||||||
varsQueue.push(
|
|
||||||
{ type : 'varDefBlock',
|
|
||||||
varName : label,
|
|
||||||
content : content
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if(match[6]) { // Block Call
|
|
||||||
const label = match[7] ? normalizeVarNames(match[7]) : null;
|
|
||||||
|
|
||||||
varsQueue.push(
|
|
||||||
{ type : 'varCallBlock',
|
|
||||||
varName : label,
|
|
||||||
content : match[0]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if(match[8]) { // Inline Definition
|
|
||||||
const label = match[10] ? normalizeVarNames(match[10]) : null;
|
|
||||||
let content = match[11] || null;
|
|
||||||
|
|
||||||
// In case of nested (), find the correct matching end )
|
|
||||||
let level = 0;
|
|
||||||
let i;
|
|
||||||
for (i = 0; i < content.length; i++) {
|
|
||||||
if(content[i] === '\\') {
|
|
||||||
i++;
|
|
||||||
} else if(content[i] === '(') {
|
|
||||||
level++;
|
|
||||||
} else if(content[i] === ')') {
|
|
||||||
level--;
|
|
||||||
if(level < 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
combinedRegex.lastIndex = combinedRegex.lastIndex - (content.length - i);
|
|
||||||
content = content.slice(0, i).trim().replace(/\s+/g, ' ');
|
|
||||||
|
|
||||||
varsQueue.push(
|
|
||||||
{ type : 'varDefBlock',
|
|
||||||
varName : label,
|
|
||||||
content : content
|
|
||||||
});
|
|
||||||
varsQueue.push(
|
|
||||||
{ type : 'varCallInline',
|
|
||||||
varName : label,
|
|
||||||
content : match[9]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if(match[12]) { // Inline Call
|
|
||||||
const label = match[13] ? normalizeVarNames(match[13]) : null;
|
|
||||||
|
|
||||||
varsQueue.push(
|
|
||||||
{ type : 'varCallInline',
|
|
||||||
varName : label,
|
|
||||||
content : match[0]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
lastIndex = combinedRegex.lastIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(lastIndex < src.length) {
|
|
||||||
varsQueue.push(
|
|
||||||
{ type : 'text',
|
|
||||||
varName : null,
|
|
||||||
content : src.slice(lastIndex)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
processVariableQueue();
|
|
||||||
|
|
||||||
const output = varsQueue.map((item)=>item.content).join('');
|
|
||||||
varsQueue = []; // Must clear varsQueue because custom HTML renderer uses Marked.parse which will preprocess again without clearing the array
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
//^=====--------------------< Variable Handling >-------------------=====^//
|
|
||||||
|
|
||||||
// Emoji options
|
// Emoji options
|
||||||
// To add more icon fonts, need to do these things
|
// To add more icon fonts, need to do these things
|
||||||
// 1) Add the font file as .woff2 to themes/fonts/iconFonts folder
|
// 1) Add the font file as .woff2 to themes/fonts/iconFonts folder
|
||||||
@@ -678,7 +353,7 @@ const tableTerminators = [
|
|||||||
` *{{[^{\n]*\n.*?\n}}` // mustacheDiv
|
` *{{[^{\n]*\n.*?\n}}` // mustacheDiv
|
||||||
];
|
];
|
||||||
|
|
||||||
Marked.use(MarkedVariables());
|
Marked.use(markedVariables());
|
||||||
Marked.use(MarkedDefinitionLists());
|
Marked.use(MarkedDefinitionLists());
|
||||||
Marked.use({ extensions: [forcedParagraphBreaks, mustacheSpans, mustacheDivs, mustacheInjectInline] });
|
Marked.use({ extensions: [forcedParagraphBreaks, mustacheSpans, mustacheDivs, mustacheInjectInline] });
|
||||||
Marked.use(mustacheInjectBlock);
|
Marked.use(mustacheInjectBlock);
|
||||||
@@ -805,25 +480,17 @@ const mergeHTMLTags = (originalTags, newTags)=>{
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const globalVarsList = {};
|
|
||||||
let varsQueue = [];
|
|
||||||
let globalPageNumber = 0;
|
|
||||||
|
|
||||||
const Markdown = {
|
const Markdown = {
|
||||||
marked : Marked,
|
marked : Marked,
|
||||||
render : (rawBrewText, pageNumber=0)=>{
|
render : (rawBrewText, pageNumber=0)=>{
|
||||||
const lastPageNumber = pageNumber > 0 ? globalVarsList[pageNumber - 1].HB_pageNumber.content : 0;
|
setMarkedVariablePage(pageNumber);
|
||||||
globalVarsList[pageNumber] = { //Reset global links for current page, to ensure values are parsed in order
|
|
||||||
'HB_pageNumber' : { //Add document variables for this page
|
const lastPageNumber = pageNumber > 0 ? getMarkedVariable('HB_pageNumber', pageNumber - 1) : 0;
|
||||||
content : !isNaN(Number(lastPageNumber)) ? Number(lastPageNumber) + 1 : lastPageNumber,
|
setMarkedVariable('HB_pageNumber', //Add document variables for this page
|
||||||
resolved : true
|
!isNaN(Number(lastPageNumber)) ? Number(lastPageNumber) + 1 : lastPageNumber,
|
||||||
}
|
pageNumber);
|
||||||
};
|
|
||||||
varsQueue = []; //Could move into MarkedVariables()
|
if(pageNumber==0) MarkedGFMResetHeadingIDs();
|
||||||
globalPageNumber = pageNumber;
|
|
||||||
if(pageNumber==0) {
|
|
||||||
MarkedGFMResetHeadingIDs();
|
|
||||||
}
|
|
||||||
|
|
||||||
rawBrewText = rawBrewText.replace(/^\\column(?:break)?$/gm, `\n<div class='columnSplit'></div>\n`);
|
rawBrewText = rawBrewText.replace(/^\\column(?:break)?$/gm, `\n<div class='columnSplit'></div>\n`);
|
||||||
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
module.exports = function(props){
|
|
||||||
return <svg version='1.1' x='0px' y='0px' viewBox='0 0 80 100' enableBackground='new 0 0 80 80'><g><g><polygon fill='#000000' points='12.9,71.4 7.6,66.1 19.3,54.4 20.7,55.8 10.4,66.1 12.9,68.6 23.2,58.3 24.6,59.7 '/></g><g><path fill='#000000' d='M29,61.6c-1.7,0-3.4-0.7-4.6-1.9l-5.1-5.1c-2.5-2.5-2.5-6.6,0-9.2l0.7-0.7L34.3,59l-0.7,0.7 C32.4,60.9,30.8,61.6,29,61.6z M20.1,47.6c-1.1,1.7-0.9,4.1,0.6,5.6l5.1,5.1c0.8,0.8,2,1.3,3.2,1.3c0.9,0,1.7-0.2,2.4-0.7 L20.1,47.6z'/></g><g><path fill='#000000' d='M12.3,74.8c-0.8,0-1.5-0.3-2-0.8l-5.2-5.2c-0.5-0.5-0.8-1.2-0.8-2c0-0.8,0.3-1.5,0.8-2 c1.1-1.1,2.9-1.1,4,0l5.2,5.2c1.1,1.1,1.1,2.9,0,4C13.8,74.5,13.1,74.8,12.3,74.8z M7.1,65.9c-0.2,0-0.4,0.1-0.6,0.2 c-0.2,0.2-0.2,0.4-0.2,0.6s0.1,0.4,0.2,0.6l5.2,5.2c0.3,0.3,0.9,0.3,1.2,0c0.3-0.3,0.3-0.8,0-1.2l-5.2-5.2 C7.5,66,7.3,65.9,7.1,65.9z'/></g><g><polygon fill='#000000' points='31.7,58.7 30.3,57.3 70,17.6 70,9 62.4,9 23.3,49.4 21.9,48 61.6,7 72,7 72,18.4 '/></g><g><rect x='46' y='6.7' transform='matrix(0.7168 0.6973 -0.6973 0.7168 35.9716 -23.568)' fill='#000000' width='2' height='51.6'/></g><g><rect x='13' y='61' fill='#000000' width='2' height='7'/></g><g><rect x='17' y='57' fill='#000000' width='2' height='7'/></g></g><g><g><polygon fill='#000000' points='68.4,71.4 56.7,59.7 58.1,58.3 68.4,68.6 70.8,66.1 60.5,55.8 61.9,54.4 73.6,66.1 '/></g><g><path fill='#000000' d='M52.2,61.6c-1.7,0-3.4-0.7-4.6-1.9L46.9,59l14.3-14.3l0.7,0.7c2.5,2.5,2.5,6.6,0,9.2l-5.1,5.1 C55.6,60.9,53.9,61.6,52.2,61.6z M49.8,58.9c0.7,0.4,1.5,0.7,2.4,0.7c1.2,0,2.3-0.5,3.2-1.3l5.1-5.1c1.5-1.5,1.7-3.8,0.6-5.6 L49.8,58.9z'/></g><g><path fill='#000000' d='M68.9,74.8c-0.8,0-1.5-0.3-2-0.8c-1.1-1.1-1.1-2.9,0-4l5.2-5.2c1.1-1.1,2.9-1.1,4,0c0.5,0.5,0.8,1.2,0.8,2 c0,0.8-0.3,1.5-0.8,2L70.9,74C70.4,74.5,69.7,74.8,68.9,74.8z M74.2,65.9c-0.2,0-0.4,0.1-0.6,0.2l-5.2,5.2c-0.3,0.3-0.3,0.8,0,1.2 c0.3,0.3,0.9,0.3,1.2,0l5.2-5.2c0.2-0.2,0.2-0.4,0.2-0.6s-0.1-0.4-0.2-0.6C74.6,66,74.4,65.9,74.2,65.9z'/></g><g><rect x='38.6' y='52.3' transform='matrix(0.7082 0.706 -0.706 0.7082 50.8397 -16.4875)' fill='#000000' width='13.4' height='2'/></g><g><polygon fill='#000000' points='30.6,39.9 9,18.4 9,7 19.7,7 41.1,29.1 39.7,30.5 18.8,9 11,9 11,17.6 32,38.5 '/></g><g><rect x='47.8' y='43.1' transform='matrix(0.6959 0.7181 -0.7181 0.6959 48.1381 -25.5246)' fill='#000000' width='12.8' height='2'/></g><g><rect x='12' y='23.1' transform='matrix(0.6974 0.7167 -0.7167 0.6974 25.1384 -11.3825)' fill='#000000' width='28.1' height='2'/></g><g><rect x='43.8' y='46.4' transform='matrix(0.6974 0.7167 -0.7167 0.6974 48.7492 -20.5985)' fill='#000000' width='10' height='2'/></g><g><rect x='66' y='61' fill='#000000' width='2' height='7'/></g><g><rect x='62' y='57' fill='#000000' width='2' height='7'/></g></g></svg>;
|
|
||||||
};
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
import Markdown from 'naturalcrit/markdown.js';
|
import Markdown from 'markdown.js';
|
||||||
|
|
||||||
test('Processes the markdown within an HTML block if its just a class wrapper', function() {
|
test('Processes the markdown within an HTML block if its just a class wrapper', function() {
|
||||||
const source = '<div>*Bold text*</div>';
|
const source = '<div>*Bold text*</div>';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
import Markdown from 'naturalcrit/markdown.js';
|
import Markdown from 'markdown.js';
|
||||||
|
|
||||||
describe('Inline Definition Lists', ()=>{
|
describe('Inline Definition Lists', ()=>{
|
||||||
test('No Term 1 Definition', function() {
|
test('No Term 1 Definition', function() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import Markdown from 'naturalcrit/markdown.js';
|
import Markdown from 'markdown.js';
|
||||||
const dedent = require('dedent-tabs').default;
|
const dedent = require('dedent-tabs').default;
|
||||||
|
|
||||||
// Marked.js adds line returns after closing tags on some default tokens.
|
// Marked.js adds line returns after closing tags on some default tokens.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
import Markdown from 'naturalcrit/markdown.js';
|
import Markdown from 'markdown.js';
|
||||||
|
|
||||||
describe('Hard Breaks', ()=>{
|
describe('Hard Breaks', ()=>{
|
||||||
test('Single Break', function() {
|
test('Single Break', function() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable max-lines */
|
/* eslint-disable max-lines */
|
||||||
|
|
||||||
const dedent = require('dedent-tabs').default;
|
const dedent = require('dedent-tabs').default;
|
||||||
import Markdown from 'naturalcrit/markdown.js';
|
import Markdown from 'markdown.js';
|
||||||
|
|
||||||
// Marked.js adds line returns after closing tags on some default tokens.
|
// Marked.js adds line returns after closing tags on some default tokens.
|
||||||
// This removes those line returns for comparison sake.
|
// This removes those line returns for comparison sake.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
import Markdown from 'naturalcrit/markdown.js';
|
import Markdown from 'markdown.js';
|
||||||
|
|
||||||
describe('Non-Breaking Spaces Interactions', ()=>{
|
describe('Non-Breaking Spaces Interactions', ()=>{
|
||||||
test('I am actually a single-line definition list!', function() {
|
test('I am actually a single-line definition list!', function() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
import Markdown from 'naturalcrit/markdown.js';
|
import Markdown from 'markdown.js';
|
||||||
|
|
||||||
describe('Justification', ()=>{
|
describe('Justification', ()=>{
|
||||||
test('Left Justify', function() {
|
test('Left Justify', function() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable max-lines */
|
/* eslint-disable max-lines */
|
||||||
|
|
||||||
const dedent = require('dedent-tabs').default;
|
const dedent = require('dedent-tabs').default;
|
||||||
import Markdown from 'naturalcrit/markdown.js';
|
import Markdown from 'markdown.js';
|
||||||
|
|
||||||
// Marked.js adds line returns after closing tags on some default tokens.
|
// Marked.js adds line returns after closing tags on some default tokens.
|
||||||
// This removes those line returns for comparison sake.
|
// This removes those line returns for comparison sake.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import Markdown from '../../../../shared/naturalcrit/markdown.js';
|
import Markdown from '../../../../shared/markdown.js';
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createFooterFunc : function(headerSize=1){
|
createFooterFunc : function(headerSize=1){
|
||||||
|
|||||||
@@ -492,7 +492,7 @@ body { counter-reset : page-numbers 0; }
|
|||||||
.pageNumber { left : 30px; }
|
.pageNumber { left : 30px; }
|
||||||
}
|
}
|
||||||
|
|
||||||
.resetCounting { counter-set : page-numbers 1; }
|
&:has(.resetCounting) { counter-set : page-numbers 1; }
|
||||||
|
|
||||||
&:not(:has(.skipCounting)) { counter-increment : page-numbers; }
|
&:not(:has(.skipCounting)) { counter-increment : page-numbers; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name" : "UnearthedArcana",
|
"name" : "UnearthedArcana",
|
||||||
"renderer" : "V3",
|
"renderer" : "V3",
|
||||||
"baseTheme" : false,
|
"baseTheme" : "Blank",
|
||||||
"baseSnippets" : false
|
"baseSnippets" : false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
"UnearthedArcana": {
|
"UnearthedArcana": {
|
||||||
"name": "UnearthedArcana",
|
"name": "UnearthedArcana",
|
||||||
"renderer": "V3",
|
"renderer": "V3",
|
||||||
"baseTheme": false,
|
"baseTheme": "Blank",
|
||||||
"baseSnippets": false,
|
"baseSnippets": false,
|
||||||
"path": "UnearthedArcana"
|
"path": "UnearthedArcana"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user