Merge branch 'master' into pr/2714
@@ -48,7 +48,7 @@ jobs:
|
||||
- image: cimg/node:16.11.0
|
||||
|
||||
working_directory: ~/homebrewery
|
||||
parallelism: 4
|
||||
parallelism: 1
|
||||
|
||||
steps:
|
||||
- attach_workspace:
|
||||
@@ -61,15 +61,15 @@ jobs:
|
||||
- run:
|
||||
name: Test - Basic
|
||||
command: npm run test:basic
|
||||
- run:
|
||||
name: Test - Coverage
|
||||
command: npm run test:coverage
|
||||
- run:
|
||||
name: Test - Mustache Spans
|
||||
command: npm run test:mustache-span
|
||||
- run:
|
||||
name: Test - Routes
|
||||
command: npm run test:route
|
||||
- run:
|
||||
name: Test - Coverage
|
||||
command: npm run test:coverage
|
||||
|
||||
workflows:
|
||||
build_and_test:
|
||||
|
||||
47
changelog.md
@@ -80,6 +80,50 @@ pre {
|
||||
## changelog
|
||||
For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery).
|
||||
|
||||
### XXXXday DD/MM/2023 - v3.8.0
|
||||
{{taskList
|
||||
##### G-Ambatte
|
||||
|
||||
* [x] Update server build scripts to fix Admin page
|
||||
|
||||
Fixes issues [#2657](https://github.com/naturalcrit/homebrewery/issues/2657)
|
||||
|
||||
* [x] Fix internal links inside `<div>` blocks not automatically receiving the `target=_self` attribute
|
||||
|
||||
Fixes issues [#2680](https://github.com/naturalcrit/homebrewery/issues/2680)
|
||||
}}
|
||||
|
||||
### Monday 13/03/2023 - v3.7.2
|
||||
{{taskList
|
||||
|
||||
##### Calculuschild
|
||||
|
||||
* [x] Fix wide Monster Stat Blocks not spanning columns on Legacy
|
||||
}}
|
||||
|
||||
### Thursday 09/03/2023 - v3.7.1
|
||||
{{taskList
|
||||
|
||||
##### Lucastucious (new contributor!)
|
||||
|
||||
* [x] Changed `filter: drop-shadow` to `box-shadow` on text boxes, making text selectable in PDFs again.
|
||||
|
||||
Fixes issues [#1569](https://github.com/naturalcrit/homebrewery/issues/1569)
|
||||
|
||||
{{note
|
||||
**NOTE:** If you create your PDF on a computer with an old version of Mac Preview (v10 or older) you may see shadows appear as solid gray.
|
||||
}}
|
||||
|
||||
##### MichielDeMey
|
||||
|
||||
* [x] Updated the Google Drive icon
|
||||
* [x] Backend fix to unit tests failing intermittently
|
||||
|
||||
##### Calculuschild
|
||||
|
||||
* [x] Fix PDF pixelation on CoverPage text outlines
|
||||
}}
|
||||
|
||||
|
||||
### Tuesday 28/02/2023 - v3.7.0
|
||||
{{taskList
|
||||
@@ -115,7 +159,6 @@ Fixes issues [#2687](https://github.com/naturalcrit/homebrewery/issues/2687)
|
||||
Fixes issues [#2674](https://github.com/naturalcrit/homebrewery/issues/2674)
|
||||
}}
|
||||
|
||||
|
||||
### Monday 23/01/2023 - v3.6.0
|
||||
{{taskList
|
||||
##### calculuschild
|
||||
@@ -141,8 +184,6 @@ Fixes issues [#2583](https://github.com/naturalcrit/homebrewery/issues/2583)
|
||||
* [x] Fix cloned brews inheriting the parent view count
|
||||
}}
|
||||
|
||||
\column
|
||||
|
||||
### Friday 23/12/2022 - v3.5.0
|
||||
{{taskList
|
||||
|
||||
|
||||
129
client/components/combobox.jsx
Normal file
@@ -0,0 +1,129 @@
|
||||
const React = require('react');
|
||||
const createClass = require('create-react-class');
|
||||
const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
require('./combobox.less');
|
||||
|
||||
const Combobox = createClass({
|
||||
displayName : 'Combobox',
|
||||
getDefaultProps : function() {
|
||||
return {
|
||||
className : '',
|
||||
trigger : 'hover',
|
||||
default : '',
|
||||
placeholder : '',
|
||||
autoSuggest : {
|
||||
clearAutoSuggestOnClick : true,
|
||||
suggestMethod : 'includes',
|
||||
filterOn : [] // should allow as array to filter on multiple attributes, or even custom filter
|
||||
},
|
||||
};
|
||||
},
|
||||
getInitialState : function() {
|
||||
return {
|
||||
showDropdown : false,
|
||||
value : '',
|
||||
options : [...this.props.options],
|
||||
inputFocused : false
|
||||
};
|
||||
},
|
||||
componentDidMount : function() {
|
||||
if(this.props.trigger == 'click')
|
||||
document.addEventListener('click', this.handleClickOutside);
|
||||
this.setState({
|
||||
value : this.props.default
|
||||
});
|
||||
},
|
||||
componentWillUnmount : function() {
|
||||
if(this.props.trigger == 'click')
|
||||
document.removeEventListener('click', this.handleClickOutside);
|
||||
},
|
||||
handleClickOutside : function(e){
|
||||
// Close dropdown when clicked outside
|
||||
if(this.refs.dropdown && !this.refs.dropdown.contains(e.target)) {
|
||||
this.handleDropdown(false);
|
||||
}
|
||||
},
|
||||
handleDropdown : function(show){
|
||||
this.setState({
|
||||
showDropdown : show,
|
||||
inputFocused : this.props.autoSuggest.clearAutoSuggestOnClick ? show : false
|
||||
});
|
||||
},
|
||||
handleInput : function(e){
|
||||
e.persist();
|
||||
this.setState({
|
||||
value : e.target.value,
|
||||
inputFocused : false
|
||||
}, ()=>{
|
||||
this.props.onEntry(e);
|
||||
});
|
||||
},
|
||||
handleSelect : function(e){
|
||||
this.setState({
|
||||
value : e.currentTarget.getAttribute('data-value')
|
||||
}, ()=>{this.props.onSelect(this.state.value);});
|
||||
;
|
||||
},
|
||||
renderTextInput : function(){
|
||||
return (
|
||||
<div className='dropdown-input item'
|
||||
onMouseEnter={this.props.trigger == 'hover' ? ()=>{this.handleDropdown(true);} : undefined}
|
||||
onClick= {this.props.trigger == 'click' ? ()=>{this.handleDropdown(true);} : undefined}>
|
||||
<input
|
||||
type='text'
|
||||
onChange={(e)=>this.handleInput(e)}
|
||||
value={this.state.value || ''}
|
||||
placeholder={this.props.placeholder}
|
||||
onBlur={(e)=>{
|
||||
if(!e.target.checkValidity()){
|
||||
this.setState({
|
||||
value : this.props.default
|
||||
}, ()=>this.props.onEntry(e));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
renderDropdown : function(dropdownChildren){
|
||||
if(!this.state.showDropdown) return null;
|
||||
if(this.props.autoSuggest && !this.state.inputFocused){
|
||||
const suggestMethod = this.props.autoSuggest.suggestMethod;
|
||||
const filterOn = _.isString(this.props.autoSuggest.filterOn) ? [this.props.autoSuggest.filterOn] : this.props.autoSuggest.filterOn;
|
||||
const filteredArrays = filterOn.map((attr)=>{
|
||||
const children = dropdownChildren.filter((item)=>{
|
||||
if(suggestMethod === 'includes'){
|
||||
return item.props[attr]?.toLowerCase().includes(this.state.value.toLowerCase());
|
||||
} else if(suggestMethod === 'startsWith'){
|
||||
return item.props[attr]?.toLowerCase().startsWith(this.state.value.toLowerCase());
|
||||
}
|
||||
});
|
||||
return children;
|
||||
});
|
||||
dropdownChildren = _.uniq(filteredArrays.flat(1));
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='dropdown-options'>
|
||||
{dropdownChildren}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
render : function () {
|
||||
const dropdownChildren = this.state.options.map((child, i)=>{
|
||||
const clone = React.cloneElement(child, { onClick: (e)=>this.handleSelect(e) });
|
||||
return clone;
|
||||
});
|
||||
return (
|
||||
<div className={`dropdown-container ${this.props.className}`}
|
||||
ref='dropdown'
|
||||
onMouseLeave={this.props.trigger == 'hover' ? ()=>{this.handleDropdown(false);} : undefined}>
|
||||
{this.renderTextInput()}
|
||||
{this.renderDropdown(dropdownChildren)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Combobox;
|
||||
50
client/components/combobox.less
Normal file
@@ -0,0 +1,50 @@
|
||||
.dropdown-container {
|
||||
position:relative;
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
.dropdown-options {
|
||||
position:absolute;
|
||||
background-color: white;
|
||||
z-index: 100;
|
||||
width: 100%;
|
||||
border: 1px solid gray;
|
||||
overflow-y: auto;
|
||||
max-height: 200px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 14px;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #ffffff;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: #949494;
|
||||
border-radius: 10px;
|
||||
border: 3px solid #ffffff;
|
||||
}
|
||||
|
||||
.item {
|
||||
position:relative;
|
||||
font-size: 11px;
|
||||
font-family: Open Sans;
|
||||
padding: 5px;
|
||||
cursor: default;
|
||||
margin: 0 3px;
|
||||
//border-bottom: 1px solid darkgray;
|
||||
&:hover {
|
||||
filter: brightness(120%);
|
||||
background-color: rgb(163, 163, 163);
|
||||
}
|
||||
.detail {
|
||||
width:100%;
|
||||
text-align: left;
|
||||
color: rgb(124, 124, 124);
|
||||
font-style:italic;
|
||||
font-size: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,6 +27,7 @@ const BrewRenderer = createClass({
|
||||
style : '',
|
||||
renderer : 'legacy',
|
||||
theme : '5ePHB',
|
||||
lang : '',
|
||||
errors : []
|
||||
};
|
||||
},
|
||||
@@ -190,7 +191,6 @@ const BrewRenderer = createClass({
|
||||
const rendererPath = this.props.renderer == 'V3' ? 'V3' : 'Legacy';
|
||||
const themePath = this.props.theme ?? '5ePHB';
|
||||
const baseThemePath = Themes[rendererPath][themePath].baseTheme;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{!this.state.isMounted
|
||||
@@ -223,7 +223,7 @@ const BrewRenderer = createClass({
|
||||
&&
|
||||
<>
|
||||
{this.renderStyle()}
|
||||
<div className='pages' ref='pages'>
|
||||
<div className='pages' ref='pages' lang={`${this.props.lang || 'en'}`}>
|
||||
{this.renderPages()}
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -6,6 +6,7 @@ const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
const request = require('../../utils/request-middleware.js');
|
||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
||||
const Combobox = require('client/components/combobox.jsx');
|
||||
const StringArrayEditor = require('../stringArrayEditor/stringArrayEditor.jsx');
|
||||
|
||||
const Themes = require('themes/themes.json');
|
||||
@@ -35,7 +36,8 @@ const MetadataEditor = createClass({
|
||||
authors : [],
|
||||
systems : [],
|
||||
renderer : 'legacy',
|
||||
theme : '5ePHB'
|
||||
theme : '5ePHB',
|
||||
lang : 'en'
|
||||
},
|
||||
onChange : ()=>{},
|
||||
reportError : ()=>{}
|
||||
@@ -76,6 +78,7 @@ const MetadataEditor = createClass({
|
||||
const errMessage = validationErr.map((err)=>{
|
||||
return `- ${err}`;
|
||||
}).join('\n');
|
||||
|
||||
callIfExists(e.target, 'setCustomValidity', errMessage);
|
||||
callIfExists(e.target, 'reportValidity');
|
||||
}
|
||||
@@ -111,6 +114,11 @@ const MetadataEditor = createClass({
|
||||
this.props.onChange(this.props.metadata);
|
||||
},
|
||||
|
||||
handleLanguage : function(languageCode){
|
||||
this.props.metadata.lang = languageCode;
|
||||
this.props.onChange(this.props.metadata);
|
||||
},
|
||||
|
||||
handleDelete : function(){
|
||||
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;
|
||||
@@ -224,6 +232,47 @@ const MetadataEditor = createClass({
|
||||
</div>;
|
||||
},
|
||||
|
||||
renderLanguageDropdown : function(){
|
||||
const langCodes = ['en', 'de', 'de-ch', 'fr', 'ja', 'es', 'it', 'sv', 'ru', 'zh-Hans', 'zh-Hant'];
|
||||
const listLanguages = ()=>{
|
||||
return _.map(langCodes.sort(), (code, index)=>{
|
||||
const localName = new Intl.DisplayNames([code], { type: 'language' });
|
||||
const englishName = new Intl.DisplayNames('en', { type: 'language' });
|
||||
return <div className='item' title={`${englishName.of(code)}`} key={`${index}`} data-value={`${code}`} data-detail={`${localName.of(code)}`}>
|
||||
{`${code}`}
|
||||
<div className='detail'>{`${localName.of(code)}`}</div>
|
||||
</div>;
|
||||
});
|
||||
};
|
||||
|
||||
const debouncedHandleFieldChange = _.debounce(this.handleFieldChange, 500);
|
||||
|
||||
return <div className='field language'>
|
||||
<label>language</label>
|
||||
<div className='value'>
|
||||
<Combobox trigger='click'
|
||||
className='language-dropdown'
|
||||
default={this.props.metadata.lang || ''}
|
||||
placeholder='en'
|
||||
onSelect={(value)=>this.handleLanguage(value)}
|
||||
onEntry={(e)=>{
|
||||
e.target.setCustomValidity(''); //Clear the validation popup while typing
|
||||
debouncedHandleFieldChange('lang', e);
|
||||
}}
|
||||
options={listLanguages()}
|
||||
autoSuggest={{
|
||||
suggestMethod : 'startsWith',
|
||||
clearAutoSuggestOnClick : true,
|
||||
filterOn : ['data-value', 'data-detail', 'title']
|
||||
}}
|
||||
>
|
||||
</Combobox>
|
||||
<small>Sets the HTML Lang property for your brew. May affect hyphenation or spellcheck.</small>
|
||||
</div>
|
||||
|
||||
</div>;
|
||||
},
|
||||
|
||||
renderRenderOptions : function(){
|
||||
if(!global.enable_v3) return;
|
||||
|
||||
@@ -301,6 +350,8 @@ const MetadataEditor = createClass({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{this.renderLanguageDropdown()}
|
||||
|
||||
{this.renderThemeDropdown()}
|
||||
|
||||
{this.renderRenderOptions()}
|
||||
@@ -315,7 +366,7 @@ const MetadataEditor = createClass({
|
||||
validators={[(v)=>!this.props.metadata.authors?.includes(v)]}
|
||||
placeholder='invite author' unique={true}
|
||||
values={this.props.metadata.invitedAuthors}
|
||||
notes={['Invited authors are case sensitive.', 'After adding an invited author, send them the edit link. There, they can choose to accept or decline the invitation.']}
|
||||
notes={['Invited author usernames are case sensitive.', 'After adding an invited author, send them the edit link. There, they can choose to accept or decline the invitation.']}
|
||||
onChange={(e)=>this.handleFieldChange('invitedAuthors', e)}/>
|
||||
|
||||
<hr/>
|
||||
|
||||
@@ -36,11 +36,15 @@
|
||||
flex: 5 0 200px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.field{
|
||||
display : flex;
|
||||
flex-wrap : wrap;
|
||||
width : 100%;
|
||||
min-width : 200px;
|
||||
position : relative;
|
||||
&>label{
|
||||
width : 80px;
|
||||
font-size : 11px;
|
||||
@@ -57,6 +61,9 @@
|
||||
}
|
||||
input[type='text'], textarea {
|
||||
border : 1px solid gray;
|
||||
&:focus {
|
||||
outline: 1px solid #444;
|
||||
}
|
||||
}
|
||||
&.thumbnail{
|
||||
height : 1.4em;
|
||||
@@ -88,9 +95,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.language .language-dropdown {
|
||||
max-width : 150px;
|
||||
z-index : 200;
|
||||
}
|
||||
small {
|
||||
font-size : 0.6em;
|
||||
font-style : italic;
|
||||
font-size : 0.6em;
|
||||
font-style : italic;
|
||||
line-height : 1.4em;
|
||||
display : inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +172,7 @@
|
||||
.navDropdownContainer {
|
||||
background-color : white;
|
||||
position : relative;
|
||||
z-index : 500;
|
||||
z-index : 100;
|
||||
&.disabled {
|
||||
font-style :italic;
|
||||
font-style : italic;
|
||||
|
||||
@@ -23,9 +23,9 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
],
|
||||
language : [
|
||||
lang : [
|
||||
(value)=>{
|
||||
return new RegExp(/[a-z]{2,3}(-.*)?/).test(value || '') === false ? 'Invalid language code.' : null;
|
||||
return new RegExp(/^([a-zA-Z]{2,3})(-[a-zA-Z]{4})?(-(?:[0-9]{3}|[a-zA-Z]{2}))?$/).test(value) === false && (value.length > 0) ? 'Invalid language code.' : null;
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
padding : 0px;
|
||||
background-color : #ddd;
|
||||
.snippet{
|
||||
position: relative;
|
||||
.animate(background-color);
|
||||
display : flex;
|
||||
align-items : center;
|
||||
|
||||
|
Before Width: | Height: | Size: 305 KiB |
8
client/homebrew/googleDrive.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg viewBox="0 0 87.3 78" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m6.6 66.85 3.85 6.65c.8 1.4 1.95 2.5 3.3 3.3l13.75-23.8h-27.5c0 1.55.4 3.1 1.2 4.5z" fill="#0066da"/>
|
||||
<path d="m43.65 25-13.75-23.8c-1.35.8-2.5 1.9-3.3 3.3l-25.4 44a9.06 9.06 0 0 0 -1.2 4.5h27.5z" fill="#00ac47"/>
|
||||
<path d="m73.55 76.8c1.35-.8 2.5-1.9 3.3-3.3l1.6-2.75 7.65-13.25c.8-1.4 1.2-2.95 1.2-4.5h-27.502l5.852 11.5z" fill="#ea4335"/>
|
||||
<path d="m43.65 25 13.75-23.8c-1.35-.8-2.9-1.2-4.5-1.2h-18.5c-1.6 0-3.15.45-4.5 1.2z" fill="#00832d"/>
|
||||
<path d="m59.8 53h-32.3l-13.75 23.8c1.35.8 2.9 1.2 4.5 1.2h50.8c1.6 0 3.15-.45 4.5-1.2z" fill="#2684fc"/>
|
||||
<path d="m73.4 26.5-12.7-22c-.8-1.4-1.95-2.5-3.3-3.3l-13.75 23.8 16.15 28h27.45c0-1.55-.4-3.1-1.2-4.5z" fill="#ffba00"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 755 B |
|
Before Width: | Height: | Size: 17 KiB |
@@ -47,6 +47,7 @@ const Homebrew = createClass({
|
||||
editId : null,
|
||||
createdAt : null,
|
||||
updatedAt : null,
|
||||
lang : ''
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
@@ -63,7 +63,7 @@ const Account = createClass({
|
||||
if(global.account){
|
||||
return <Nav.dropdown>
|
||||
<Nav.item
|
||||
className='account'
|
||||
className='account username'
|
||||
color='orange'
|
||||
icon='fas fa-user'
|
||||
>
|
||||
|
||||
@@ -187,4 +187,7 @@
|
||||
.account.navItem{
|
||||
min-width: 100px;
|
||||
}
|
||||
.account.username.navItem{
|
||||
text-transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ const cx = require('classnames');
|
||||
const moment = require('moment');
|
||||
const request = require('../../../../utils/request-middleware.js');
|
||||
|
||||
const googleDriveIcon = require('../../../../googleDrive.png');
|
||||
const googleDriveIcon = require('../../../../googleDrive.svg');
|
||||
const dedent = require('dedent-tabs').default;
|
||||
|
||||
const BrewItem = createClass({
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
}
|
||||
}
|
||||
.googleDriveIcon {
|
||||
height : 20px;
|
||||
height : 18px;
|
||||
padding : 0px;
|
||||
margin : -5px;
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ const ListPage = createClass({
|
||||
|
||||
render : function(){
|
||||
return <div className='listPage sitePage'>
|
||||
//<style>@layer V3_5ePHB, bundle;</style>
|
||||
{/*<style>@layer V3_5ePHB, bundle;</style>*/}
|
||||
<link href='/themes/V3/5ePHB/style.css' rel='stylesheet'/>
|
||||
{this.props.navItems}
|
||||
{this.renderSortOptions()}
|
||||
|
||||
@@ -24,8 +24,7 @@ const Markdown = require('naturalcrit/markdown.js');
|
||||
|
||||
const { DEFAULT_BREW_LOAD } = require('../../../../server/brewDefaults.js');
|
||||
|
||||
const googleDriveActive = require('../../googleDrive.png');
|
||||
const googleDriveInactive = require('../../googleDriveMono.png');
|
||||
const googleDriveIcon = require('../../googleDrive.svg');
|
||||
|
||||
const SAVE_TIMEOUT = 3000;
|
||||
|
||||
@@ -222,10 +221,7 @@ const EditPage = createClass({
|
||||
|
||||
renderGoogleDriveIcon : function(){
|
||||
return <Nav.item className='googleDriveStorage' onClick={this.handleGoogleClick}>
|
||||
{this.state.saveGoogle
|
||||
? <img src={googleDriveActive} alt='googleDriveActive'/>
|
||||
: <img src={googleDriveInactive} alt='googleDriveInactive'/>
|
||||
}
|
||||
<img src={googleDriveIcon} className={this.state.saveGoogle ? '' : 'inactive'} alt='Google Drive icon'/>
|
||||
|
||||
{this.state.confirmGoogleTransfer &&
|
||||
<div className='errorContainer' onClick={this.closeAlerts}>
|
||||
@@ -402,7 +398,14 @@ const EditPage = createClass({
|
||||
reportError={this.errorReported}
|
||||
renderer={this.state.brew.renderer}
|
||||
/>
|
||||
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer} theme={this.state.brew.theme} errors={this.state.htmlErrors} />
|
||||
<BrewRenderer
|
||||
text={this.state.brew.text}
|
||||
style={this.state.brew.style}
|
||||
renderer={this.state.brew.renderer}
|
||||
theme={this.state.brew.theme}
|
||||
errors={this.state.htmlErrors}
|
||||
lang={this.state.brew.lang}
|
||||
/>
|
||||
</SplitPane>
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
@@ -18,8 +18,12 @@
|
||||
position : relative;
|
||||
}
|
||||
.googleDriveStorage img{
|
||||
height : 20px;
|
||||
height : 18px;
|
||||
padding : 0px;
|
||||
margin : -5px;
|
||||
|
||||
&.inactive {
|
||||
filter: grayscale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ const NewPage = createClass({
|
||||
// brew.description = metaStorage?.description || this.state.brew.description;
|
||||
brew.renderer = metaStorage?.renderer ?? brew.renderer;
|
||||
brew.theme = metaStorage?.theme ?? brew.theme;
|
||||
brew.lang = metaStorage?.lang ?? brew.lang;
|
||||
|
||||
this.setState({
|
||||
brew : brew
|
||||
@@ -70,7 +71,7 @@ const NewPage = createClass({
|
||||
localStorage.setItem(BREWKEY, brew.text);
|
||||
if(brew.style)
|
||||
localStorage.setItem(STYLEKEY, brew.style);
|
||||
localStorage.setItem(METAKEY, JSON.stringify({ 'renderer': brew.renderer, 'theme': brew.theme }));
|
||||
localStorage.setItem(METAKEY, JSON.stringify({ 'renderer': brew.renderer, 'theme': brew.theme, 'lang': brew.lang }));
|
||||
},
|
||||
componentWillUnmount : function() {
|
||||
document.removeEventListener('keydown', this.handleControlKeys);
|
||||
@@ -114,13 +115,16 @@ const NewPage = createClass({
|
||||
handleMetaChange : function(metadata){
|
||||
this.setState((prevState)=>({
|
||||
brew : { ...prevState.brew, ...metadata },
|
||||
}));
|
||||
localStorage.setItem(METAKEY, JSON.stringify({
|
||||
// 'title' : this.state.brew.title,
|
||||
// 'description' : this.state.brew.description,
|
||||
'renderer' : this.state.brew.renderer,
|
||||
'theme' : this.state.brew.theme
|
||||
}));
|
||||
}), ()=>{
|
||||
localStorage.setItem(METAKEY, JSON.stringify({
|
||||
// 'title' : this.state.brew.title,
|
||||
// 'description' : this.state.brew.description,
|
||||
'renderer' : this.state.brew.renderer,
|
||||
'theme' : this.state.brew.theme,
|
||||
'lang' : this.state.brew.lang
|
||||
}));
|
||||
});
|
||||
;
|
||||
},
|
||||
|
||||
save : async function(){
|
||||
@@ -211,7 +215,7 @@ const NewPage = createClass({
|
||||
onMetaChange={this.handleMetaChange}
|
||||
renderer={this.state.brew.renderer}
|
||||
/>
|
||||
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer} theme={this.state.brew.theme} errors={this.state.htmlErrors}/>
|
||||
<BrewRenderer text={this.state.brew.text} style={this.state.brew.style} renderer={this.state.brew.renderer} theme={this.state.brew.theme} lang={this.state.brew.lang} errors={this.state.htmlErrors}/>
|
||||
</SplitPane>
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
1
client/icons/Davek.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 791.04 953.29"><title>Davek</title><g id="Layer_2" data-name="Layer 2"><g id="Davek"><path d="M178.41,13.46a19.33,19.33,0,0,0-4.71,5.38q8.07,6.07,13.46,6.07a8.27,8.27,0,0,0,4.71-1.35,130.23,130.23,0,0,0,16.83-7.07,74.55,74.55,0,0,1,18.85-6.39h2.7q8.07,0,14.81,8.74a944.19,944.19,0,0,0,95.6,4.72q19.5,0,38.37-.67,69.33-2,139.68-5.72t139.7-5.06q16.82-.64,34.34-.66,50.49,0,98.29,3.36-17.5,12.12-22.55,31.64t-5,33.66q.64,22.89.66,45.1,0,47.13-3.36,97-6.07,74.05-9.78,148.11t-5,146.09v17.51a766.1,766.1,0,0,0,8.75,118.48,38.57,38.57,0,0,0-4,17.51,30.94,30.94,0,0,0,.67,6.06q2,12.12,3.36,23.22c.9,7.42,1.57,14.92,2,22.55v3.37a57.93,57.93,0,0,1-3.36,19.52c.43,4.5.67,8.77.67,12.8a260.65,260.65,0,0,1-2.7,37,344.26,344.26,0,0,0-4,52.52,133.5,133.5,0,0,0,8.09,45.44q8.07,22.57,33,36.68-6.06,8.78-20.19,8.77H762.1c-4.5-.45-8.53-.69-12.12-.69a78.11,78.11,0,0,0-21.54,2.7,579.1,579.1,0,0,0-63.64,3.71q-33.31,3.71-67.65,6.39t-68.66,3.37h-4a188.05,188.05,0,0,1-59.92-9.43q20.19-4,39.06-23.22t20.19-47.46q11.44-22.21,11.45-49.82a320.44,320.44,0,0,1,3.36-49.15q-9.45-4.69-10.09-8.75v-2.7a73,73,0,0,1,.66-8.74,105.81,105.81,0,0,0,3.37-12.8,7.49,7.49,0,0,0,.68-3.37q0-4.7-4.05-10.09c.45-4.93.69-10.1.69-15.48a311.71,311.71,0,0,0-3.37-46.45,207.31,207.31,0,0,1-1.35-24.25,274.58,274.58,0,0,1,4-45.1l15.5,6.73q-3.37-17.49-3.37-41.07,0-24.89,8.75-44.44a27.73,27.73,0,0,0,2-9.43,15.32,15.32,0,0,0-3.36-10.09,60.75,60.75,0,0,1-10.1-15.48l-7.39,6.73q2.67-47.79,8.74-99,3.35-33.63,3.37-65.29,0-14.81-.69-29a205.09,205.09,0,0,1-4-41.74,190.26,190.26,0,0,1,2-26.92q4-37,14.81-67.33a25.14,25.14,0,0,1-2.68-11.43,31.13,31.13,0,0,1,.66-6.07V140q0-6.72-8.74-10.09-3.37-16.83-5.73-31.3T521.07,77.41q-55.2,2.7-115.78,4.71-19.55.7-39.72.69-38.38,0-74.06-2.7c-5.4,4.5-8.08,9.21-8.08,14.14v1.34a41.5,41.5,0,0,0,4.37,15.49q3.7,7.4,7.4,15.16a35,35,0,0,1,3.71,15.13q32.31,34.35,64,68.68a335.89,335.89,0,0,1,51.83,73.38q13.46,7.4,18.51,17.49t10.11,19.87q5.06,9.78,10.1,18.85t16.5,11.78v12.12a194.5,194.5,0,0,1-37.38-4q-20.52-4-40.73-6.73a114.48,114.48,0,0,0-17.49-1.35,97.2,97.2,0,0,0-20.2,2q-17.52,4.05-31,20.19-16.84-1.35-27.27-9.75a76.13,76.13,0,0,1-17.51-20.2q-7.06-11.76-14.47-24.9a79.77,79.77,0,0,0-18.84-22.57A305.87,305.87,0,0,1,177.73,237q-28.29-33.67-54.54-69T68,99.31A381.16,381.16,0,0,0,0,38.37q12.79,0,22.89-9.75A190.69,190.69,0,0,1,44.76,10.44Q56.54,2,68.66,0H72Q82.8,0,97,10.76Z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
1
client/icons/Iokharic.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 428.05 941.17"><title>Iokharic</title><g id="Layer_2" data-name="Layer 2"><g id="Iokharic"><path d="M334.76,909.61V259.3l2.74-89.18c3.43,0,6.18-8.23,7.55-24.69,3.43,0,7.55-8.92,13.72-27.44,13-11,19.89-21.27,19.89-31.56,0-13-5.48-20.58-17.15-23.32l-30.87,2.74H320.36c-21.27,13-39.79,22.64-56.94,27.44h-37c-11.67,0-26.76,7.55-46,22q-12.34,0-30.86,16.46c-10.29,0-40.48,26.75-91.93,80.95,0,8.23-6.17,21.26-18.52,38.41l-3.43,15.78v41.84L67.23,343c2.74,0,9.6,6.86,19.89,19.9,24,18.52,36.36,30.86,36.36,38.41l-12.35,10.29H105c-24.7-15.78-45.28-32.93-62.43-52.13L15.78,316.92,0,266.85c3.43-17.84,7.55-29.5,13.72-35v-11c0-18.52,7.55-39.79,22-63.8,0-9.6,8.23-21.27,24.7-34.3,0-9.6,15.77-26.07,46.64-50.08,19.9-16.46,46-28.12,76.83-35,5.49-6.86,21.27-14.41,46.65-21.95C238,5.49,251.07,0,270.28,0h137.2c8.91,0,15.77,8.23,20.57,24V40.47l-5.48,8.23V166c0,17.15-7.55,31.55-21.95,43.22v41.15l-2.75,24.7q0,9.26,24.7,30.87v38.41c0,10.29-4.81,19.9-15.09,28.82h-6.86V558.39c0,55.57-4.81,97.41-15.1,124.16-4.8,2.75-7.54,19.21-9.6,48.71l2.74,17.15-2.74,76.14v30.19q0,32.93-32.93,86.43C337.5,937.74,334.76,926.76,334.76,909.61Z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
client/icons/Rellanic.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 527.7 940.25"><title>Rellanic</title><g id="Layer_2" data-name="Layer 2"><g id="Rellanic"><path d="M527.7,5.45q-3.83,19.65-15,30.56a129.61,129.61,0,0,1-26.46,19.64q-9.84,6.56-31.66,15.28-19.63,7.65-31.64,16.38Q380.33,103.69,342.16,108a468.46,468.46,0,0,1-54,3.28q-15.83,0-30.56-1.1a53.19,53.19,0,0,0-20.19-6.55H217.74q-7.12,1.11-21.29,1.1a51.67,51.67,0,0,1-20.18-4.36q8.72,19.65,25.63,29.46,14.19,8.74,28.38,29.47a634.05,634.05,0,0,1,98.78,90.58l91.12,103.69a65.1,65.1,0,0,0-.54,8.19,42.47,42.47,0,0,0,.54,7.09c.73,1.82,1.27,3.29,1.64,4.37q7.08,8.75,10.92,12,1.62,1.1,12.55,14.19a14,14,0,0,1,3.27,6.55,9.75,9.75,0,0,1,1.1,4.37,9.62,9.62,0,0,1-1.1,4.36q35.46,43.66,51.3,89.5,3.25,9.82,5.45,19.64a288.59,288.59,0,0,1,10.37,68.75v8.19a296,296,0,0,1-9.81,76.94q-7.12,27.3-24,77.5L418,831.65Q383,872,344.88,899.31a243.27,243.27,0,0,1-90.59,38.19,179.84,179.84,0,0,1-31.64,2.75q-38.78,0-81.87-15.84A293.78,293.78,0,0,1,78,886.22a312.61,312.61,0,0,1-51.85-48,300.52,300.52,0,0,0-18-46.94,60.18,60.18,0,0,1-4.92-13.64,82.36,82.36,0,0,1-2.19-19.11,104.89,104.89,0,0,1,.56-10.91,176.12,176.12,0,0,1-1.64-24,199.79,199.79,0,0,1,2.72-32.74Q5.45,663,5.45,645a103.71,103.71,0,0,0-.54-10.92,242.44,242.44,0,0,1,50.74-67.66,646.83,646.83,0,0,0,57.86-61.12q11.44-10.89,25.09-13.1A88.3,88.3,0,0,1,163.71,489q14.17-1.11,29.46-1.1a108.11,108.11,0,0,0,28.38-7.63q17.44,8.75,27.29,12a124.47,124.47,0,0,1,28.38,13.1q8.71,4.38,23.46,17.46,9.29,9.86,17.47,28.38,7.07,12,9.27,21.83a35.16,35.16,0,0,1,1.64,9.83V585a80.23,80.23,0,0,1-8.73,27.28q-8.2,14.19-18,22.93a166.18,166.18,0,0,1-19.65,19.64q-13.1,8.74-20.72,13.1l-7.65-4.37v-1.64q0-12,6.55-18-8.17-6.55-10.36-10.92l-2.18-8.73c0-2.18-.74-5.81-2.19-10.91v-3.29a38,38,0,0,0-3.82-7.63,196.53,196.53,0,0,0-33.84-40.39Q185.53,542.43,162.61,537a163.71,163.71,0,0,0-50.75,9.81q-25.08,8.76-32.2,36Q67.12,615.56,67.13,654.3a256,256,0,0,0,3.26,39.83,176.75,176.75,0,0,0,5.47,28.38Q88.37,770,122.78,812a452.22,452.22,0,0,0,103.13,58.94,153.57,153.57,0,0,0,107,5.45q25.63-12,37.66-27.28,13.62-14.21,23.46-34.93,10.36-18.57,20.2-39.29Q426.72,753.05,437.1,740q3.27-44.76,5.47-61.12a228.17,228.17,0,0,0,3.26-38.21,213.15,213.15,0,0,0-1.64-26.19,245.3,245.3,0,0,0-8.17-48q-2.2-8.17-4.93-16.36-9.27-30.55-34.92-61.12a70,70,0,0,0-2.18-18,29.12,29.12,0,0,0-4.37-10.37,175.28,175.28,0,0,0-17.46-29.48l-18.55-27.27q-12-16.38-16.38-28.38a282.35,282.35,0,0,1-27.81-28.37q-20.22-26.2-24-31.66Q269,295.76,260.29,286q-10.92-12-31.1-25.11-36.56-31.65-79.12-70.94-45.31-39.28-88.41-66.58-14.74-8.17-17.46-16.9a16.93,16.93,0,0,0-.54-3.83V99.87q0-8.73,6.54-19.11A102.47,102.47,0,0,1,63.3,61.12q9.27-9.82,12.56-18.56a223.6,223.6,0,0,1,38.73-3.27,271,271,0,0,1,40.93,3.27A367.15,367.15,0,0,0,215,47.48c6.91,0,13.64-.17,20.2-.56a45,45,0,0,0,21.27,5.47q17.44,0,25.65-1.1h22.93a77.75,77.75,0,0,1,24,7.65,114,114,0,0,1,27.82-3.29H364q27.25,2.2,39.29,2.19,16.34,0,36.55-5.45,19.1-6.55,27.83-22.93h2.72A20.48,20.48,0,0,0,484.58,24c2.17-4.71,6.17-7.09,12-7.09a26.6,26.6,0,0,1,4.92.54v-.54c0-1.08.72-3.46,2.19-7.11a36.74,36.74,0,0,1,6-6.54C512.57,1.1,515.12,0,517.32,0,521,0,524.41,1.82,527.7,5.45Z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
@@ -31,9 +31,21 @@
|
||||
.mask-corner {
|
||||
content: url('../icons/mask-corner.svg');
|
||||
}
|
||||
.mask-center {
|
||||
content: url('../icons/mask-center.svg');
|
||||
}
|
||||
.fa-file-c {
|
||||
content: url('../icons/fa-file-c.svg');
|
||||
}
|
||||
.book-front-cover {
|
||||
content: url('../icons/book-front-cover.svg');
|
||||
}
|
||||
.davek {
|
||||
content: url('../icons/Davek.svg');
|
||||
}
|
||||
.rellanic {
|
||||
content: url('../icons/Rellanic.svg');
|
||||
}
|
||||
.iokharic {
|
||||
content: url('../icons/Iokharic.svg');
|
||||
}
|
||||
|
||||
63
client/icons/mask-center.svg
Normal file
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 448 512"
|
||||
version="1.1"
|
||||
id="svg135"
|
||||
sodipodi:docname="mask-center.svg"
|
||||
width="448"
|
||||
height="512"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs139"><pattern
|
||||
inkscape:collect="always"
|
||||
xlink:href="#Strips1_1"
|
||||
id="pattern3077"
|
||||
patternTransform="matrix(23.13193,-23.131931,19.25517,19.25517,18.091544,-20.306833)" /><pattern
|
||||
inkscape:collect="always"
|
||||
patternUnits="userSpaceOnUse"
|
||||
width="2"
|
||||
height="1"
|
||||
patternTransform="translate(0,0) scale(10,10)"
|
||||
id="Strips1_1"
|
||||
inkscape:stockid="Stripes 1:1"><rect
|
||||
style="fill:black;stroke:none"
|
||||
x="0"
|
||||
y="-0.5"
|
||||
width="1"
|
||||
height="2"
|
||||
id="rect2097" /></pattern></defs><sodipodi:namedview
|
||||
id="namedview137"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
showguides="false"
|
||||
inkscape:zoom="0.67711183"
|
||||
inkscape:cx="31.75251"
|
||||
inkscape:cy="260.66595"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="991"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg135" /><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path
|
||||
id="rect12201"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:30;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||
d="M 48,-5.2e-6 C 21.40803,-5.2e-6 1.98e-5,21.408025 1.98e-5,47.999995 V 464 C 1.98e-5,490.59197 21.40803,512 48,512 h 352 c 26.59198,0 48,-21.40803 48,-48 V 47.999995 C 448,21.408025 426.59198,-5.2e-6 400,-5.2e-6 Z M 64,63.999995 H 384 V 448 H 64 Z" /><rect
|
||||
style="fill:url(#pattern3077);fill-opacity:1;stroke:#000000;stroke-width:48;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:fill markers stroke;stop-color:#000000"
|
||||
id="rect12206"
|
||||
width="176"
|
||||
height="240"
|
||||
x="136.00002"
|
||||
y="136"
|
||||
rx="48"
|
||||
ry="48" /></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
43
install/README.WINDOWS.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Windows Installation Instructions
|
||||
|
||||
## Before Installing
|
||||
|
||||
These instructions assume that you are installing to a completely new, fresh Windows 10 installation. As such, some steps may not be necessary if you are installing to an existing Windows 10 instance.
|
||||
|
||||
## Installation instructions
|
||||
|
||||
1. Download the installation script from https://raw.githubusercontent.com/naturalcrit/homebrewery/master/install/windows/install.ps1.
|
||||
|
||||
2. Run Powershell as an Administrator.
|
||||
a. Click the Start menu or press the Windows key.
|
||||
b. Type `powershell` into the Search box.
|
||||
c. Right click on the Powershell app and select "Run As Administrator".
|
||||
d. Click YES in the prompt that appears.
|
||||
|
||||
3. Change the script execution policy.
|
||||
a. Run the Powershell command `Set-ExecutionPolicy Bypass -Scope Process`.
|
||||
b. Allow the change to be made - press Y at the prompt that appears.
|
||||
|
||||
4. Run the installation script.
|
||||
a. Navigate to the location of the script, e.g. `cd C:\Users\ExampleUser\Downloads`.
|
||||
b. Start the script - `.\install.ps1`
|
||||
|
||||
5. Once the script has completed, it will start the Homebrewery server. This will normally cause a Network Access prompt for NodeJS - if this appears, click "Allow".
|
||||
|
||||
**NOTE:** At this time, the script **ONLY** installs HomeBrewery. It does **NOT** install the NaturalCrit login system, as that is currently a completely separate project.
|
||||
|
||||
---
|
||||
|
||||
### Testing
|
||||
|
||||
These installation instructions have been tested on the following Ubuntu releases:
|
||||
|
||||
- *Windows 10 Home - OS Build 19045.2546*
|
||||
|
||||
## Final Notes
|
||||
|
||||
While this installation process works successfully at the time of writing (January 23, 2023), it relies on all of the Node.JS packages used in the HomeBrewery project retaining their cross-platform capabilities to continue to function. This is one of the inherent advantages of Node.JS, but it is by no means guaranteed and as such, functionality or even installation may fail without warning at some point in the future.
|
||||
|
||||
Regards,
|
||||
G
|
||||
January 23, 2023
|
||||
51
install/windows/install.ps1
Normal file
@@ -0,0 +1,51 @@
|
||||
Write-Host Homebrewery Install -BackgroundColor Black -ForegroundColor Yellow
|
||||
Write-Host =================== -BackgroundColor Black -ForegroundColor Yellow
|
||||
Write-Host Install Chocolatey -BackgroundColor Black -ForegroundColor Yellow
|
||||
Write-Host Instructions from https://chocolate.org/install -BackgroundColor Black -ForegroundColor Yellow
|
||||
|
||||
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
|
||||
|
||||
Write-Host Install Node JS v16.11.1 -BackgroundColor Black -ForegroundColor Yellow
|
||||
|
||||
choco install nodejs --version=16.11.1 -y
|
||||
|
||||
Write-Host Install MongoDB v 4.4.4 -BackgroundColor Black -ForegroundColor Yellow
|
||||
|
||||
choco install mongodb --version=4.4.4 -y
|
||||
|
||||
Write-Host Install GIT -BackgroundColor Black -ForegroundColor Yellow
|
||||
|
||||
choco install git -y
|
||||
|
||||
Write-Host Refresh Environment -BackgroundColor Black -ForegroundColor Yellow
|
||||
|
||||
Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
|
||||
Update-SessionEnvironment
|
||||
|
||||
Write-Host Create Homebrewery directory - C:\Homebrewery -BackgroundColor Black -ForegroundColor Yellow
|
||||
|
||||
mkdir C:\Hombrewery
|
||||
cd C:\Hombrewery
|
||||
|
||||
Write-Host Download Homebrewery project files -BackgroundColor Black -ForegroundColor Yellow
|
||||
|
||||
git clone https://github.com/naturalcrit/homebrewery.git
|
||||
|
||||
Write-Host Install Homebrewery files -BackgroundColor Black -ForegroundColor Yellow
|
||||
|
||||
cd homebrewery
|
||||
|
||||
npm install
|
||||
npm audit fix
|
||||
|
||||
Write-Host Set install type to 'local' -BackgroundColor Black -ForegroundColor Yellow
|
||||
|
||||
[System.Environment]::SetEnvironmentVariable('NODE_ENV', 'local')
|
||||
|
||||
Write-Host INSTALL COMPLETE -BackgroundColor Black -ForegroundColor Yellow
|
||||
Write-Host To start Homebrewery in the future, open a terminal in the Homebrewery directory and run npm start -BackgroundColor Black -ForegroundColor Yellow
|
||||
Write-Host ================================================================================================== -BackgroundColor Black -ForegroundColor Yellow
|
||||
|
||||
Write-Host Start Homebrewery -BackgroundColor Black -ForegroundColor Yellow
|
||||
|
||||
npm start
|
||||
17871
package-lock.json
generated
38
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "homebrewery",
|
||||
"description": "Create authentic looking D&D homebrews using only markdown",
|
||||
"version": "3.7.0",
|
||||
"version": "3.7.2",
|
||||
"engines": {
|
||||
"node": "16.11.x"
|
||||
},
|
||||
@@ -12,23 +12,22 @@
|
||||
"scripts": {
|
||||
"dev": "node scripts/dev.js",
|
||||
"quick": "node scripts/quick.js",
|
||||
"build": "node scripts/buildHomebrew.js",
|
||||
"buildall": "node scripts/buildHomebrew.js && node scripts/buildAdmin.js",
|
||||
"build": "node scripts/buildHomebrew.js && node scripts/buildAdmin.js",
|
||||
"builddev": "node scripts/buildHomebrew.js --dev",
|
||||
"lint": "eslint --fix **/*.{js,jsx}",
|
||||
"lint:dry": "eslint **/*.{js,jsx}",
|
||||
"circleci": "npm test && eslint **/*.{js,jsx} --max-warnings=0",
|
||||
"verify": "npm run lint && npm test",
|
||||
"test": "jest",
|
||||
"test": "jest --runInBand",
|
||||
"test:api-unit": "jest server/*.spec.js --verbose",
|
||||
"test:coverage": "jest --coverage --silent",
|
||||
"test:coverage": "jest --coverage --silent --runInBand",
|
||||
"test:dev": "jest --verbose --watch",
|
||||
"test:basic": "jest tests/markdown/basic.test.js --verbose",
|
||||
"test:mustache-span": "jest tests/markdown/mustache-span.test.js --verbose",
|
||||
"test:route": "jest tests/routes/static-pages.test.js --verbose",
|
||||
"phb": "node scripts/phb.js",
|
||||
"prod": "set NODE_ENV=production && npm run build",
|
||||
"postinstall": "npm run buildall",
|
||||
"postinstall": "npm run build",
|
||||
"start": "node server.js"
|
||||
},
|
||||
"author": "stolksdorf",
|
||||
@@ -37,12 +36,15 @@
|
||||
"build/*"
|
||||
],
|
||||
"jest": {
|
||||
"testTimeout": 15000,
|
||||
"testTimeout": 30000,
|
||||
"modulePaths": [
|
||||
"node_modules",
|
||||
"shared",
|
||||
"server"
|
||||
],
|
||||
"coveragePathIgnorePatterns": [
|
||||
"build/*"
|
||||
],
|
||||
"coverageThreshold" : {
|
||||
"global" : {
|
||||
"statements" : 25,
|
||||
@@ -68,45 +70,45 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.21.0",
|
||||
"@babel/core": "^7.21.3",
|
||||
"@babel/plugin-transform-runtime": "^7.21.0",
|
||||
"@babel/preset-env": "^7.19.4",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@googleapis/drive": "^5.0.2",
|
||||
"body-parser": "^1.20.2",
|
||||
"classnames": "^2.3.2",
|
||||
"codemirror": "^5.65.6",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"create-react-class": "^15.7.0",
|
||||
"dedent-tabs": "^0.10.2",
|
||||
"dedent-tabs": "^0.10.3",
|
||||
"express": "^4.18.2",
|
||||
"express-async-handler": "^1.2.0",
|
||||
"express-static-gzip": "2.1.7",
|
||||
"fs-extra": "11.1.0",
|
||||
"googleapis": "111.0.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jwt-simple": "^0.5.6",
|
||||
"less": "^3.13.1",
|
||||
"lodash": "^4.17.21",
|
||||
"marked": "4.2.12",
|
||||
"marked": "4.3.0",
|
||||
"marked-extended-tables": "^1.0.5",
|
||||
"markedLegacy": "npm:marked@^0.3.19",
|
||||
"moment": "^2.29.4",
|
||||
"mongoose": "^6.9.2",
|
||||
"mongoose": "^7.0.3",
|
||||
"nanoid": "3.3.4",
|
||||
"nconf": "^0.12.0",
|
||||
"npm": "^8.10.0",
|
||||
"npm": "^9.6.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-frame-component": "4.1.3",
|
||||
"react-router-dom": "6.8.2",
|
||||
"react-frame-component": "5.2.6",
|
||||
"react-router-dom": "6.9.0",
|
||||
"sanitize-filename": "1.6.3",
|
||||
"superagent": "^6.1.0",
|
||||
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.35.0",
|
||||
"eslint": "^8.36.0",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"jest": "^29.4.3",
|
||||
"jest": "^29.5.0",
|
||||
"supertest": "^6.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ const mw = {
|
||||
.status(401)
|
||||
.send('Authorization Required');
|
||||
}
|
||||
const [username, password] = new Buffer(req.get('authorization').split(' ').pop(), 'base64')
|
||||
const [username, password] = Buffer.from(req.get('authorization').split(' ').pop(), 'base64')
|
||||
.toString('ascii')
|
||||
.split(':');
|
||||
if(process.env.ADMIN_USER === username && process.env.ADMIN_PASS === password){
|
||||
|
||||
@@ -23,7 +23,7 @@ const splitTextStyleAndMetadata = (brew)=>{
|
||||
const index = brew.text.indexOf('```\n\n');
|
||||
const metadataSection = brew.text.slice(12, index - 1);
|
||||
const metadata = yaml.load(metadataSection);
|
||||
Object.assign(brew, _.pick(metadata, ['title', 'description', 'tags', 'systems', 'renderer', 'theme']));
|
||||
Object.assign(brew, _.pick(metadata, ['title', 'description', 'tags', 'systems', 'renderer', 'theme', 'lang']));
|
||||
brew.text = brew.text.slice(index + 5);
|
||||
}
|
||||
if(brew.text.startsWith('```css')) {
|
||||
@@ -225,6 +225,7 @@ app.get('/user/:username', async (req, res, next)=>{
|
||||
'pageCount',
|
||||
'description',
|
||||
'authors',
|
||||
'lang',
|
||||
'published',
|
||||
'views',
|
||||
'shareId',
|
||||
|
||||
@@ -15,6 +15,7 @@ const DEFAULT_BREW = {
|
||||
authors : [],
|
||||
tags : [],
|
||||
systems : [],
|
||||
lang : 'en',
|
||||
thumbnail : '',
|
||||
views : 0,
|
||||
published : false,
|
||||
|
||||
@@ -27,8 +27,8 @@ const disconnect = async ()=>{
|
||||
};
|
||||
|
||||
const connect = async (config)=>{
|
||||
return await Mongoose.connect(getMongoDBURL(config),
|
||||
{ retryWrites: false }, handleConnectionError);
|
||||
return await Mongoose.connect(getMongoDBURL(config), { retryWrites: false })
|
||||
.catch((error)=>handleConnectionError(error));
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable max-lines */
|
||||
const _ = require('lodash');
|
||||
const { google } = require('googleapis');
|
||||
const googleDrive = require('@googleapis/drive');
|
||||
const { nanoid } = require('nanoid');
|
||||
const token = require('./token.js');
|
||||
const config = require('./config.js');
|
||||
@@ -14,7 +14,7 @@ if(!config.get('service_account')){
|
||||
config.get('service_account');
|
||||
|
||||
try {
|
||||
serviceAuth = google.auth.fromJSON(keys);
|
||||
serviceAuth = googleDrive.auth.fromJSON(keys);
|
||||
serviceAuth.scopes = ['https://www.googleapis.com/auth/drive'];
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
@@ -22,7 +22,7 @@ if(!config.get('service_account')){
|
||||
}
|
||||
}
|
||||
|
||||
google.options({ auth: serviceAuth || config.get('google_api_key') });
|
||||
const defaultAuth = serviceAuth || config.get('google_api_key');
|
||||
|
||||
const GoogleActions = {
|
||||
|
||||
@@ -33,7 +33,7 @@ const GoogleActions = {
|
||||
throw (err);
|
||||
}
|
||||
|
||||
const oAuth2Client = new google.auth.OAuth2(
|
||||
const oAuth2Client = new googleDrive.auth.OAuth2(
|
||||
config.get('google_client_id'),
|
||||
config.get('google_client_secret'),
|
||||
'/auth/google/redirect'
|
||||
@@ -60,7 +60,7 @@ const GoogleActions = {
|
||||
},
|
||||
|
||||
getGoogleFolder : async (auth)=>{
|
||||
const drive = google.drive({ version: 'v3', auth });
|
||||
const drive = googleDrive.drive({ version: 'v3', auth });
|
||||
|
||||
fileMetadata = {
|
||||
'name' : 'Homebrewery',
|
||||
@@ -97,7 +97,7 @@ const GoogleActions = {
|
||||
},
|
||||
|
||||
listGoogleBrews : async (auth)=>{
|
||||
const drive = google.drive({ version: 'v3', auth });
|
||||
const drive = googleDrive.drive({ version: 'v3', auth });
|
||||
|
||||
const obj = await drive.files.list({
|
||||
pageSize : 1000,
|
||||
@@ -129,14 +129,16 @@ const GoogleActions = {
|
||||
description : file.description,
|
||||
views : parseInt(file.properties.views),
|
||||
published : file.properties.published ? file.properties.published == 'true' : false,
|
||||
systems : []
|
||||
systems : [],
|
||||
lang : file.properties.lang,
|
||||
thumbnail : file.properties.thumbnail
|
||||
};
|
||||
});
|
||||
return brews;
|
||||
},
|
||||
|
||||
updateGoogleBrew : async (brew)=>{
|
||||
const drive = google.drive({ version: 'v3' });
|
||||
const drive = googleDrive.drive({ version: 'v3', auth: defaultAuth });
|
||||
|
||||
await drive.files.update({
|
||||
fileId : brew.googleId,
|
||||
@@ -149,7 +151,8 @@ const GoogleActions = {
|
||||
editId : brew.editId || nanoid(12),
|
||||
pageCount : brew.pageCount,
|
||||
renderer : brew.renderer || 'legacy',
|
||||
isStubbed : true
|
||||
isStubbed : true,
|
||||
lang : brew.lang || 'en'
|
||||
}
|
||||
},
|
||||
media : {
|
||||
@@ -167,7 +170,7 @@ const GoogleActions = {
|
||||
},
|
||||
|
||||
newGoogleBrew : async (auth, brew)=>{
|
||||
const drive = google.drive({ version: 'v3', auth });
|
||||
const drive = googleDrive.drive({ version: 'v3', auth });
|
||||
|
||||
const media = {
|
||||
mimeType : 'text/plain',
|
||||
@@ -187,7 +190,8 @@ const GoogleActions = {
|
||||
pageCount : brew.pageCount,
|
||||
renderer : brew.renderer || 'legacy',
|
||||
isStubbed : true,
|
||||
version : 1
|
||||
version : 1,
|
||||
lang : brew.lang || 'en'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -218,7 +222,7 @@ const GoogleActions = {
|
||||
},
|
||||
|
||||
getGoogleBrew : async (id, accessId, accessType)=>{
|
||||
const drive = google.drive({ version: 'v3' });
|
||||
const drive = googleDrive.drive({ version: 'v3', auth: defaultAuth });
|
||||
|
||||
const obj = await drive.files.get({
|
||||
fileId : id,
|
||||
@@ -255,6 +259,7 @@ const GoogleActions = {
|
||||
description : obj.data.description,
|
||||
systems : obj.data.properties.systems ? obj.data.properties.systems.split(',') : [],
|
||||
authors : [],
|
||||
lang : obj.data.properties.lang,
|
||||
published : obj.data.properties.published ? obj.data.properties.published == 'true' : false,
|
||||
trashed : obj.data.trashed,
|
||||
|
||||
@@ -274,7 +279,7 @@ const GoogleActions = {
|
||||
},
|
||||
|
||||
deleteGoogleBrew : async (auth, id, accessId)=>{
|
||||
const drive = google.drive({ version: 'v3', auth });
|
||||
const drive = googleDrive.drive({ version: 'v3', auth });
|
||||
|
||||
const obj = await drive.files.get({
|
||||
fileId : id,
|
||||
@@ -300,7 +305,7 @@ const GoogleActions = {
|
||||
},
|
||||
|
||||
increaseView : async (id, accessId, accessType, brew)=>{
|
||||
const drive = google.drive({ version: 'v3' });
|
||||
const drive = googleDrive.drive({ version: 'v3', auth: defaultAuth });
|
||||
|
||||
await drive.files.update({
|
||||
fileId : brew.googleId,
|
||||
|
||||
@@ -62,6 +62,7 @@ describe('Tests for api', ()=>{
|
||||
description : 'this is a description',
|
||||
tags : ['something', 'fun'],
|
||||
systems : ['D&D 5e'],
|
||||
lang : 'en',
|
||||
renderer : 'v3',
|
||||
theme : 'phb',
|
||||
published : true,
|
||||
@@ -255,6 +256,7 @@ If you believe you should have access to this brew, ask the file owner to invite
|
||||
pageCount : 1,
|
||||
published : false,
|
||||
renderer : 'legacy',
|
||||
lang : 'en',
|
||||
shareId : undefined,
|
||||
systems : [],
|
||||
tags : [],
|
||||
@@ -448,6 +450,7 @@ brew`);
|
||||
pageCount : 1,
|
||||
published : false,
|
||||
renderer : 'V3',
|
||||
lang : 'en',
|
||||
shareId : expect.any(String),
|
||||
style : undefined,
|
||||
systems : [],
|
||||
@@ -506,6 +509,7 @@ brew`);
|
||||
pageCount : undefined,
|
||||
published : false,
|
||||
renderer : undefined,
|
||||
lang : 'en',
|
||||
shareId : expect.any(String),
|
||||
googleId : expect.any(String),
|
||||
style : undefined,
|
||||
|
||||
@@ -15,6 +15,7 @@ const HomebrewSchema = mongoose.Schema({
|
||||
description : { type: String, default: '' },
|
||||
tags : [String],
|
||||
systems : [String],
|
||||
lang : { type: String, default: 'en' },
|
||||
renderer : { type: String, default: '' },
|
||||
authors : [String],
|
||||
invitedAuthors : [String],
|
||||
@@ -39,30 +40,24 @@ HomebrewSchema.statics.increaseView = async function(query) {
|
||||
return brew;
|
||||
};
|
||||
|
||||
HomebrewSchema.statics.get = function(query, fields=null){
|
||||
return new Promise((resolve, reject)=>{
|
||||
Homebrew.find(query, fields, null, (err, brews)=>{
|
||||
if(err || !brews.length) return reject('Can not find brew');
|
||||
if(!_.isNil(brews[0].textBin)) { // Uncompress zipped text field
|
||||
unzipped = zlib.inflateRawSync(brews[0].textBin);
|
||||
brews[0].text = unzipped.toString();
|
||||
}
|
||||
return resolve(brews[0]);
|
||||
});
|
||||
});
|
||||
HomebrewSchema.statics.get = async function(query, fields=null){
|
||||
const brew = await Homebrew.findOne(query, fields).orFail()
|
||||
.catch((error)=>{throw 'Can not find brew';});
|
||||
if(!_.isNil(brew.textBin)) { // Uncompress zipped text field
|
||||
unzipped = zlib.inflateRawSync(brew.textBin);
|
||||
brew.text = unzipped.toString();
|
||||
}
|
||||
return brew;
|
||||
};
|
||||
|
||||
HomebrewSchema.statics.getByUser = function(username, allowAccess=false, fields=null){
|
||||
return new Promise((resolve, reject)=>{
|
||||
const query = { authors: username, published: true };
|
||||
if(allowAccess){
|
||||
delete query.published;
|
||||
}
|
||||
Homebrew.find(query, fields).lean().exec((err, brews)=>{ //lean() converts results to JSObjects
|
||||
if(err) return reject('Can not find brew');
|
||||
return resolve(brews);
|
||||
});
|
||||
});
|
||||
HomebrewSchema.statics.getByUser = async function(username, allowAccess=false, fields=null){
|
||||
const query = { authors: username, published: true };
|
||||
if(allowAccess){
|
||||
delete query.published;
|
||||
}
|
||||
const brews = await Homebrew.find(query, fields).lean().exec() //lean() converts results to JSObjects
|
||||
.catch((error)=>{throw 'Can not find brews';});
|
||||
return brews;
|
||||
};
|
||||
|
||||
const Homebrew = mongoose.model('Homebrew', HomebrewSchema);
|
||||
|
||||
@@ -239,7 +239,7 @@ const definitionLists = {
|
||||
Marked.use({ extensions: [mustacheSpans, mustacheDivs, mustacheInjectInline, definitionLists] });
|
||||
Marked.use(MarkedExtendedTables());
|
||||
Marked.use(mustacheInjectBlock);
|
||||
Marked.use({ smartypants: true });
|
||||
Marked.use({ renderer: renderer, smartypants: true });
|
||||
|
||||
//Fix local links in the Preview iFrame to link inside the frame
|
||||
renderer.link = function (href, title, text) {
|
||||
@@ -347,10 +347,7 @@ module.exports = {
|
||||
render : (rawBrewText)=>{
|
||||
rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n<div class='columnSplit'></div>\n`)
|
||||
.replace(/^(:+)$/gm, (match)=>`${`<div class='blank'></div>`.repeat(match.length)}\n`);
|
||||
return Marked.parse(
|
||||
sanatizeScriptTags(rawBrewText),
|
||||
{ renderer: renderer }
|
||||
);
|
||||
return Marked.parse(sanatizeScriptTags(rawBrewText));
|
||||
},
|
||||
|
||||
validate : (rawBrewText)=>{
|
||||
|
||||
@@ -13,3 +13,9 @@ test('Processes the markdown within an HTML block if its just a class wrapper',
|
||||
const rendered = Markdown.render(source);
|
||||
expect(rendered).toBe('<div> <p><em>Bold text</em></p>\n </div>');
|
||||
});
|
||||
|
||||
test('Check markdown is using the custom renderer; specifically that it adds target=_self attribute to internal links in HTML blocks', function() {
|
||||
const source = '<div>[Has _self Attribute?](#p1)</div>';
|
||||
const rendered = Markdown.render(source);
|
||||
expect(rendered).toBe('<div> <p><a href="#p1" target="_self">Has _self Attribute?</a></p>\n </div>');
|
||||
});
|
||||
|
||||
@@ -262,6 +262,7 @@ body {
|
||||
//Full Width
|
||||
hr+hr+blockquote{
|
||||
.useColumns(0.96);
|
||||
column-fill : balance;
|
||||
}
|
||||
//*****************************
|
||||
// * FOOTER
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
const MagicGen = require('./snippets/magic.gen.js');
|
||||
const ClassTableGen = require('./snippets/classtable.gen.js');
|
||||
const MonsterBlockGen = require('./snippets/monsterblock.gen.js');
|
||||
const scriptGen = require('./snippets/script.gen.js');
|
||||
const ClassFeatureGen = require('./snippets/classfeature.gen.js');
|
||||
const CoverPageGen = require('./snippets/coverpage.gen.js');
|
||||
const InsideCoverPageGen = require('./snippets/insidecoverpage.gen.js')
|
||||
@@ -239,7 +240,30 @@ module.exports = [
|
||||
name : '1/3 Class Table (unframed)',
|
||||
icon : 'fas fa-border-none',
|
||||
gen : ClassTableGen.third('classTable'),
|
||||
}
|
||||
},
|
||||
{
|
||||
name : 'Rune Table',
|
||||
icon : 'fas fa-language',
|
||||
gen : scriptGen.dwarvish,
|
||||
experimental : true,
|
||||
subsnippets : [
|
||||
{
|
||||
name : 'Dwarvish',
|
||||
icon : 'fac davek',
|
||||
gen : scriptGen.dwarvish,
|
||||
},
|
||||
{
|
||||
name : 'Elvish',
|
||||
icon : 'fac rellanic',
|
||||
gen : scriptGen.elvish,
|
||||
},
|
||||
{
|
||||
name : 'Draconic',
|
||||
icon : 'fac iokharic',
|
||||
gen : scriptGen.draconic,
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
48
themes/V3/5ePHB/snippets/script.gen.js
Normal file
@@ -0,0 +1,48 @@
|
||||
const _ = require('lodash');
|
||||
const dedent = require('dedent-tabs').default;
|
||||
|
||||
module.exports = {
|
||||
dwarvish : ()=>{
|
||||
return dedent `##### Dwarvish Runes: Sample Alphabet
|
||||
{{runeTable,wide,frame,font-family:Davek
|
||||
| a | b | c | d | e | f | g | h | i | j | k | l | m |
|
||||
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||
| a | b | c | d | e | f | g | h | i | j | k | l | m |
|
||||
:
|
||||
| n | o | p | q | r | s | t | u | v | w | x | y | z |
|
||||
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||
| n | o | p | q | r | s | t | u | v | w | x | y | z |
|
||||
}}\n\n`;
|
||||
},
|
||||
elvish : ()=>{
|
||||
return dedent `##### Elvish Runes: Sample Alphabet
|
||||
{{runeTable,wide,frame,font-family:Rellanic
|
||||
| a | b | c | d | e | f | g | h | i | j | k | l | m |
|
||||
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||
| a | b | c | d | e | f | g | h | i | j | k | l | m |
|
||||
:
|
||||
| n | o | p | q | r | s | t | u | v | w | x | y | z |
|
||||
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||
| n | o | p | q | r | s | t | u | v | w | x | y | z |
|
||||
}}\n\n`;
|
||||
},
|
||||
draconic : ()=>{
|
||||
return dedent `##### Draconic Runes: Sample Alphabet
|
||||
{{runeTable,wide,frame,font-family:Iokharic
|
||||
| a | b | c | d | e | f | g | h | i | j | k | l | m |
|
||||
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||
| a | b | c | d | e | f | g | h | i | j | k | l | m |
|
||||
:
|
||||
| n | o | p | q | r | s | t | u | v | w | x | y | z |
|
||||
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||
| n | o | p | q | r | s | t | u | v | w | x | y | z |
|
||||
}}\n\n`;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
()=>{
|
||||
|
||||
};
|
||||
@@ -253,7 +253,7 @@ body {
|
||||
border-image-outset : 9px 0px;
|
||||
border-image-width : 11px;
|
||||
padding : 0.13cm 0.16cm;
|
||||
filter : drop-shadow(1px 4px 6px #888);
|
||||
box-shadow : 1px 4px 14px #888;
|
||||
.page :where(&) {
|
||||
margin-top : 9px; //Prevent top border getting cut off on colbreak
|
||||
}
|
||||
@@ -282,7 +282,7 @@ body {
|
||||
border-image : @descriptiveBoxImage 12 stretch;
|
||||
border-image-outset : 4px;
|
||||
padding : 0.1em;
|
||||
filter : drop-shadow(0 0 3px #faf7ea);
|
||||
box-shadow : 0 0 6px #faf7ea;
|
||||
.page :where(&) {
|
||||
margin-top : 4px; //Prevent top border getting cut off on colbreak
|
||||
}
|
||||
@@ -397,7 +397,7 @@ body {
|
||||
border-image-outset : 0px 2px;
|
||||
background-blend-mode : overlay;
|
||||
background-attachment : fixed;
|
||||
filter : drop-shadow(1px 4px 6px #888);
|
||||
box-shadow : 1px 4px 14px #888;
|
||||
padding : 4px 2px;
|
||||
margin-left : -0.16cm;
|
||||
margin-right : -0.16cm;
|
||||
@@ -687,11 +687,11 @@ h5 + table{
|
||||
all: unset;
|
||||
}
|
||||
.logo {
|
||||
position : absolute;
|
||||
top : 0.5cm;
|
||||
left : 0;
|
||||
right : 0;
|
||||
filter : drop-shadow(0 0 0.075cm black);
|
||||
position : absolute;
|
||||
top : 0.5cm;
|
||||
left : 0;
|
||||
right : 0;
|
||||
filter :drop-shadow(0 0 0.075cm black);
|
||||
img {
|
||||
height : 2cm;
|
||||
width : 100%;
|
||||
@@ -706,11 +706,11 @@ h5 + table{
|
||||
z-index : -1;
|
||||
}
|
||||
h1 {
|
||||
--shadow-x0 : #000 0px 0px 0.1cm;
|
||||
--shadow-x1 : var(--shadow-x0), var(--shadow-x0), var(--shadow-x0);
|
||||
--shadow-x2 : var(--shadow-x1), var(--shadow-x1), var(--shadow-x1);
|
||||
--shadow-x3 : var(--shadow-x2), var(--shadow-x2), var(--shadow-x2);
|
||||
text-shadow : var(--shadow-x3), var(--shadow-x3), var(--shadow-x3);
|
||||
text-shadow: unset;
|
||||
filter : drop-shadow(0 0 1.5px black) drop-shadow(0 0 0 black)
|
||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
|
||||
text-transform : uppercase;
|
||||
font-weight : normal;
|
||||
display : block;
|
||||
@@ -722,11 +722,10 @@ h5 + table{
|
||||
line-height : 0.85em;
|
||||
}
|
||||
h2 {
|
||||
--shadow-x0 : #000 0px 0px 2.5px;
|
||||
--shadow-x1 : var(--shadow-x0), var(--shadow-x0), var(--shadow-x0);
|
||||
--shadow-x2 : var(--shadow-x1), var(--shadow-x1), var(--shadow-x1);
|
||||
--shadow-x3 : var(--shadow-x2), var(--shadow-x2), var(--shadow-x2);
|
||||
text-shadow : var(--shadow-x3), var(--shadow-x3), var(--shadow-x3);
|
||||
filter : drop-shadow(0 0 1px black) drop-shadow(0 0 0 black)
|
||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
|
||||
font-family : NodestoCapsCondensed;
|
||||
font-weight : normal;
|
||||
font-size : 0.85cm;
|
||||
@@ -746,7 +745,7 @@ h5 + table{
|
||||
filter : drop-shadow(0 0 3px black);
|
||||
}
|
||||
.banner {
|
||||
filter : drop-shadow(2px 2px 2px #000);
|
||||
filter : drop-shadow(2px 2px 2px black);
|
||||
position : absolute;
|
||||
left : 0;
|
||||
bottom : 4.2cm;
|
||||
@@ -766,10 +765,10 @@ h5 + table{
|
||||
padding-top : 0.1cm;
|
||||
}
|
||||
.footnote {
|
||||
--shadow-x0 : #000 0px 0px 0.05cm;
|
||||
--shadow-x1 : var(--shadow-x0), var(--shadow-x0), var(--shadow-x0);
|
||||
--shadow-x2 : var(--shadow-x1), var(--shadow-x1), var(--shadow-x1);
|
||||
text-shadow : var(--shadow-x2), var(--shadow-x2), var(--shadow-x2);
|
||||
filter : drop-shadow(0 0 0.7px black) drop-shadow(0 0 0 black)
|
||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black)
|
||||
drop-shadow(0 0 0 black) drop-shadow(0 0 0 black);
|
||||
position : absolute;
|
||||
text-align : center;
|
||||
color : white;
|
||||
@@ -962,3 +961,43 @@ break-inside : avoid;
|
||||
.page h1 + *{
|
||||
margin-top : 0;
|
||||
}
|
||||
|
||||
//*****************************
|
||||
// * RUNE TABLE
|
||||
// *****************************/
|
||||
.page {
|
||||
.runeTable {
|
||||
margin-block: 0.7cm;
|
||||
table {
|
||||
font-family : inherit;
|
||||
tbody tr {
|
||||
background: unset;
|
||||
}
|
||||
th, td {
|
||||
width: 1.3cm;
|
||||
height: 1.3cm;
|
||||
vertical-align: middle;
|
||||
text-transform: uppercase;
|
||||
outline: 1px solid #000;
|
||||
font-weight: normal;
|
||||
}
|
||||
th{
|
||||
font-family: BookInsanityRemake;
|
||||
font-size: 0.45cm;
|
||||
}
|
||||
td {
|
||||
font-size: 0.7cm;
|
||||
}
|
||||
}
|
||||
|
||||
&.frame {
|
||||
border: initial;
|
||||
border-style: solid;
|
||||
border-image-outset: .45cm .35cm .4cm .4cm;
|
||||
border-image-repeat: stretch;
|
||||
border-image-slice: 170;
|
||||
border-image-source: @scriptBorder;
|
||||
border-image-width: 1.4cm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,12 @@ module.exports = [
|
||||
icon : 'fas fa-fill-drip',
|
||||
gen : WatercolorGen,
|
||||
},
|
||||
{
|
||||
name : 'Watercolor Center',
|
||||
icon : 'fac mask-center',
|
||||
gen : ImageMaskGen.center,
|
||||
experimental : true,
|
||||
},
|
||||
{
|
||||
name : 'Watercolor Edge',
|
||||
icon : 'fac mask-edge',
|
||||
|
||||
@@ -2,6 +2,16 @@ const _ = require('lodash');
|
||||
const dedent = require('dedent-tabs').default;
|
||||
|
||||
module.exports = {
|
||||
center :()=>{
|
||||
return dedent`
|
||||
{{imageMaskCenter${_.random(1, 16)},--offsetX:0%,--offsetY:0%,--rotation:0;
|
||||
{height:100%}
|
||||
}}
|
||||
<!-- Use --offsetX to shift the mask left or right (can use cm instead of %)
|
||||
Use --offsetY to shift the mask up or down
|
||||
Use --rotation to set rotation angle in degrees. -->\n\n`;
|
||||
},
|
||||
|
||||
edge : (side = 'bottom')=>{
|
||||
const rotation = {
|
||||
'bottom' : 0,
|
||||
@@ -10,10 +20,10 @@ module.exports = {
|
||||
'right' : 270
|
||||
}[side];
|
||||
return dedent`
|
||||
{{imageMaskEdge${_.random(1, 8)},--offset:0cm,--rotation:${rotation}
|
||||
{{imageMaskEdge${_.random(1, 8)},--offset:0%,--rotation:${rotation}
|
||||
{height:100%}
|
||||
}}
|
||||
<!-- Use --offset to shift the mask toward or away from the page center.
|
||||
<!-- Use --offset to shift the mask away from page center (can use cm instead of %)
|
||||
Use --rotation to set rotation angle in degrees. -->\n\n`;
|
||||
},
|
||||
|
||||
|
||||
@@ -187,7 +187,6 @@ body {
|
||||
height : 100%;
|
||||
font-size : 120px;
|
||||
text-transform : uppercase;
|
||||
color : black;
|
||||
mix-blend-mode : overlay;
|
||||
opacity : 30%;
|
||||
transform : rotate(-45deg);
|
||||
@@ -228,6 +227,7 @@ body {
|
||||
.watercolor12 { --wc : @watercolor12; }
|
||||
|
||||
/* Image Masks */
|
||||
|
||||
[class*="imageMask"] {
|
||||
position : absolute;
|
||||
height : 200%;
|
||||
@@ -288,14 +288,66 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
.imageMaskEdge1 { --wc : url(/assets/waterColorMasks/edge/0001.webp); }
|
||||
.imageMaskEdge2 { --wc : url(/assets/waterColorMasks/edge/0002.webp); }
|
||||
.imageMaskEdge3 { --wc : url(/assets/waterColorMasks/edge/0003.webp); }
|
||||
.imageMaskEdge4 { --wc : url(/assets/waterColorMasks/edge/0004.webp); }
|
||||
.imageMaskEdge5 { --wc : url(/assets/waterColorMasks/edge/0005.webp); }
|
||||
.imageMaskEdge6 { --wc : url(/assets/waterColorMasks/edge/0006.webp); }
|
||||
.imageMaskEdge7 { --wc : url(/assets/waterColorMasks/edge/0007.webp); }
|
||||
.imageMaskEdge8 { --wc : url(/assets/waterColorMasks/edge/0008.webp); }
|
||||
.imageMaskEdge {
|
||||
&1 { --wc : url(/assets/waterColorMasks/edge/0001.webp); }
|
||||
&2 { --wc : url(/assets/waterColorMasks/edge/0002.webp); }
|
||||
&3 { --wc : url(/assets/waterColorMasks/edge/0003.webp); }
|
||||
&4 { --wc : url(/assets/waterColorMasks/edge/0004.webp); }
|
||||
&5 { --wc : url(/assets/waterColorMasks/edge/0005.webp); }
|
||||
&6 { --wc : url(/assets/waterColorMasks/edge/0006.webp); }
|
||||
&7 { --wc : url(/assets/waterColorMasks/edge/0007.webp); }
|
||||
&8 { --wc : url(/assets/waterColorMasks/edge/0008.webp); }
|
||||
}
|
||||
|
||||
[class*="imageMaskCenter"] {
|
||||
width : 100%;
|
||||
height : 100%;
|
||||
left : calc(var(--offsetX));
|
||||
bottom : calc(var(--offsetY));
|
||||
-webkit-mask-image : var(--wc), var(--revealer);
|
||||
-webkit-mask-repeat : no-repeat;
|
||||
-webkit-mask-size : 100% 100%; //Scale both dimensions to fit page size
|
||||
-webkit-mask-position : 0% 0%;
|
||||
mask-image : var(--wc), var(--revealer);
|
||||
mask-repeat : no-repeat;
|
||||
mask-size : 100% 100%; //Scale both dimensions to fit page size
|
||||
mask-position : 50% 50%;
|
||||
transform : rotate(calc(1deg * var(--rotation))) scaleX(var(--scaleX)) scaleY(var(--scaleY));
|
||||
|
||||
& > p:has(img) {
|
||||
position : absolute;
|
||||
width : 100%;
|
||||
height : 100%;
|
||||
bottom : 0;
|
||||
left : 0;
|
||||
transform : unset;
|
||||
transform : scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY)))
|
||||
rotate(calc(-1deg * var(--rotation)))
|
||||
translateX(calc(-1 * var(--offsetX)))
|
||||
translateY(calc(1 * var(--offsetY)));
|
||||
}
|
||||
}
|
||||
|
||||
.imageMaskCenter {
|
||||
&1 { --wc : url(/assets/waterColorMasks/center/0001.webp); }
|
||||
&2 { --wc : url(/assets/waterColorMasks/center/0002.webp); }
|
||||
&3 { --wc : url(/assets/waterColorMasks/center/0003.webp); }
|
||||
&4 { --wc : url(/assets/waterColorMasks/center/0004.webp); }
|
||||
&5 { --wc : url(/assets/waterColorMasks/center/0005.webp); }
|
||||
&6 { --wc : url(/assets/waterColorMasks/center/0006.webp); }
|
||||
&7 { --wc : url(/assets/waterColorMasks/center/0007.webp); }
|
||||
&8 { --wc : url(/assets/waterColorMasks/center/0008.webp); }
|
||||
&9 { --wc : url(/assets/waterColorMasks/center/0009.webp); }
|
||||
&10 { --wc : url(/assets/waterColorMasks/center/0010.webp); }
|
||||
&11 { --wc : url(/assets/waterColorMasks/center/0011.webp); }
|
||||
&12 { --wc : url(/assets/waterColorMasks/center/0012.webp); }
|
||||
&13 { --wc : url(/assets/waterColorMasks/center/0013.webp); }
|
||||
&14 { --wc : url(/assets/waterColorMasks/center/0014.webp); }
|
||||
&15 { --wc : url(/assets/waterColorMasks/center/0015.webp); }
|
||||
&16 { --wc : url(/assets/waterColorMasks/center/0016.webp); }
|
||||
&special { --wc : url(/assets/waterColorMasks/center/special.webp); }
|
||||
}
|
||||
|
||||
|
||||
[class*="imageMaskCorner"] {
|
||||
height : 200%;
|
||||
@@ -310,7 +362,7 @@ body {
|
||||
mask-repeat : no-repeat;
|
||||
mask-size : 100% 100%; //Scale both dimensions to fit page size
|
||||
mask-position : 50% 50%;
|
||||
transform : rotate(calc(1deg * var(--rotation))) scaleX(var(--scaleX)) scaleY(var(--scaleY));;
|
||||
transform : rotate(calc(1deg * var(--rotation))) scaleX(var(--scaleX)) scaleY(var(--scaleY));
|
||||
& > p:has(img) {
|
||||
width : 50%;
|
||||
height : 50%; //Complex transform below to handle mix of % and cm offsets
|
||||
@@ -322,44 +374,45 @@ body {
|
||||
translateY(calc(1 * var(--offsetY)));
|
||||
}
|
||||
}
|
||||
|
||||
.imageMaskCorner1 { --wc : url(/assets/waterColorMasks/corner/0001.webp); }
|
||||
.imageMaskCorner2 { --wc : url(/assets/waterColorMasks/corner/0002.webp); }
|
||||
.imageMaskCorner3 { --wc : url(/assets/waterColorMasks/corner/0003.webp); }
|
||||
.imageMaskCorner4 { --wc : url(/assets/waterColorMasks/corner/0004.webp); }
|
||||
.imageMaskCorner5 { --wc : url(/assets/waterColorMasks/corner/0005.webp); }
|
||||
.imageMaskCorner6 { --wc : url(/assets/waterColorMasks/corner/0006.webp); }
|
||||
.imageMaskCorner7 { --wc : url(/assets/waterColorMasks/corner/0007.webp); }
|
||||
.imageMaskCorner8 { --wc : url(/assets/waterColorMasks/corner/0008.webp); }
|
||||
.imageMaskCorner9 { --wc : url(/assets/waterColorMasks/corner/0009.webp); }
|
||||
.imageMaskCorner10 { --wc : url(/assets/waterColorMasks/corner/0010.webp); }
|
||||
.imageMaskCorner11 { --wc : url(/assets/waterColorMasks/corner/0011.webp); }
|
||||
.imageMaskCorner12 { --wc : url(/assets/waterColorMasks/corner/0012.webp); }
|
||||
.imageMaskCorner13 { --wc : url(/assets/waterColorMasks/corner/0013.webp); }
|
||||
.imageMaskCorner14 { --wc : url(/assets/waterColorMasks/corner/0014.webp); }
|
||||
.imageMaskCorner15 { --wc : url(/assets/waterColorMasks/corner/0015.webp); }
|
||||
.imageMaskCorner16 { --wc : url(/assets/waterColorMasks/corner/0016.webp); }
|
||||
.imageMaskCorner17 { --wc : url(/assets/waterColorMasks/corner/0017.webp); }
|
||||
.imageMaskCorner18 { --wc : url(/assets/waterColorMasks/corner/0018.webp); }
|
||||
.imageMaskCorner19 { --wc : url(/assets/waterColorMasks/corner/0019.webp); }
|
||||
.imageMaskCorner20 { --wc : url(/assets/waterColorMasks/corner/0020.webp); }
|
||||
.imageMaskCorner21 { --wc : url(/assets/waterColorMasks/corner/0021.webp); }
|
||||
.imageMaskCorner22 { --wc : url(/assets/waterColorMasks/corner/0022.webp); }
|
||||
.imageMaskCorner23 { --wc : url(/assets/waterColorMasks/corner/0023.webp); }
|
||||
.imageMaskCorner24 { --wc : url(/assets/waterColorMasks/corner/0024.webp); }
|
||||
.imageMaskCorner25 { --wc : url(/assets/waterColorMasks/corner/0025.webp); }
|
||||
.imageMaskCorner26 { --wc : url(/assets/waterColorMasks/corner/0026.webp); }
|
||||
.imageMaskCorner27 { --wc : url(/assets/waterColorMasks/corner/0027.webp); }
|
||||
.imageMaskCorner28 { --wc : url(/assets/waterColorMasks/corner/0028.webp); }
|
||||
.imageMaskCorner29 { --wc : url(/assets/waterColorMasks/corner/0029.webp); }
|
||||
.imageMaskCorner30 { --wc : url(/assets/waterColorMasks/corner/0030.webp); }
|
||||
.imageMaskCorner31 { --wc : url(/assets/waterColorMasks/corner/0031.webp); }
|
||||
.imageMaskCorner32 { --wc : url(/assets/waterColorMasks/corner/0032.webp); }
|
||||
.imageMaskCorner33 { --wc : url(/assets/waterColorMasks/corner/0033.webp); }
|
||||
.imageMaskCorner34 { --wc : url(/assets/waterColorMasks/corner/0034.webp); }
|
||||
.imageMaskCorner35 { --wc : url(/assets/waterColorMasks/corner/0035.webp); }
|
||||
.imageMaskCorner36 { --wc : url(/assets/waterColorMasks/corner/0036.webp); }
|
||||
.imageMaskCorner37 { --wc : url(/assets/waterColorMasks/corner/0037.webp); }
|
||||
.imageMaskCorner {
|
||||
&1 { --wc : url(/assets/waterColorMasks/corner/0001.webp); }
|
||||
&2 { --wc : url(/assets/waterColorMasks/corner/0002.webp); }
|
||||
&3 { --wc : url(/assets/waterColorMasks/corner/0003.webp); }
|
||||
&4 { --wc : url(/assets/waterColorMasks/corner/0004.webp); }
|
||||
&5 { --wc : url(/assets/waterColorMasks/corner/0005.webp); }
|
||||
&6 { --wc : url(/assets/waterColorMasks/corner/0006.webp); }
|
||||
&7 { --wc : url(/assets/waterColorMasks/corner/0007.webp); }
|
||||
&8 { --wc : url(/assets/waterColorMasks/corner/0008.webp); }
|
||||
&9 { --wc : url(/assets/waterColorMasks/corner/0009.webp); }
|
||||
&10 { --wc : url(/assets/waterColorMasks/corner/0010.webp); }
|
||||
&11 { --wc : url(/assets/waterColorMasks/corner/0011.webp); }
|
||||
&12 { --wc : url(/assets/waterColorMasks/corner/0012.webp); }
|
||||
&13 { --wc : url(/assets/waterColorMasks/corner/0013.webp); }
|
||||
&14 { --wc : url(/assets/waterColorMasks/corner/0014.webp); }
|
||||
&15 { --wc : url(/assets/waterColorMasks/corner/0015.webp); }
|
||||
&16 { --wc : url(/assets/waterColorMasks/corner/0016.webp); }
|
||||
&17 { --wc : url(/assets/waterColorMasks/corner/0017.webp); }
|
||||
&18 { --wc : url(/assets/waterColorMasks/corner/0018.webp); }
|
||||
&19 { --wc : url(/assets/waterColorMasks/corner/0019.webp); }
|
||||
&20 { --wc : url(/assets/waterColorMasks/corner/0020.webp); }
|
||||
&21 { --wc : url(/assets/waterColorMasks/corner/0021.webp); }
|
||||
&22 { --wc : url(/assets/waterColorMasks/corner/0022.webp); }
|
||||
&23 { --wc : url(/assets/waterColorMasks/corner/0023.webp); }
|
||||
&24 { --wc : url(/assets/waterColorMasks/corner/0024.webp); }
|
||||
&25 { --wc : url(/assets/waterColorMasks/corner/0025.webp); }
|
||||
&26 { --wc : url(/assets/waterColorMasks/corner/0026.webp); }
|
||||
&27 { --wc : url(/assets/waterColorMasks/corner/0027.webp); }
|
||||
&28 { --wc : url(/assets/waterColorMasks/corner/0028.webp); }
|
||||
&29 { --wc : url(/assets/waterColorMasks/corner/0029.webp); }
|
||||
&30 { --wc : url(/assets/waterColorMasks/corner/0030.webp); }
|
||||
&31 { --wc : url(/assets/waterColorMasks/corner/0031.webp); }
|
||||
&32 { --wc : url(/assets/waterColorMasks/corner/0032.webp); }
|
||||
&33 { --wc : url(/assets/waterColorMasks/corner/0033.webp); }
|
||||
&34 { --wc : url(/assets/waterColorMasks/corner/0034.webp); }
|
||||
&35 { --wc : url(/assets/waterColorMasks/corner/0035.webp); }
|
||||
&36 { --wc : url(/assets/waterColorMasks/corner/0036.webp); }
|
||||
&37 { --wc : url(/assets/waterColorMasks/corner/0037.webp); }
|
||||
}
|
||||
}
|
||||
|
||||
//*****************************
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
@coverPageBanner : url('/assets/coverPageBanner.svg');
|
||||
@horizontalRule : url('/assets/horizontalRule.svg');
|
||||
@insideCoverMask : url('/assets/insideCoverMask.png');
|
||||
@scriptBorder : url('/assets/scriptBorder.png');
|
||||
|
||||
// Watercolor Images
|
||||
@watercolor1 : url('/assets/watercolor/watercolor1.png');
|
||||
|
||||
BIN
themes/assets/scriptBorder.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
themes/assets/waterColorMasks/center/0001.webp
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
themes/assets/waterColorMasks/center/0002.webp
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
themes/assets/waterColorMasks/center/0003.webp
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
themes/assets/waterColorMasks/center/0004.webp
Normal file
|
After Width: | Height: | Size: 97 KiB |
BIN
themes/assets/waterColorMasks/center/0005.webp
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
themes/assets/waterColorMasks/center/0006.webp
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
themes/assets/waterColorMasks/center/0007.webp
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
themes/assets/waterColorMasks/center/0008.webp
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
themes/assets/waterColorMasks/center/0009.webp
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
themes/assets/waterColorMasks/center/0010.webp
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
themes/assets/waterColorMasks/center/0011.webp
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
themes/assets/waterColorMasks/center/0012.webp
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
themes/assets/waterColorMasks/center/0013.webp
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
themes/assets/waterColorMasks/center/0014.webp
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
themes/assets/waterColorMasks/center/0015.webp
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
themes/assets/waterColorMasks/center/0016.webp
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
themes/assets/waterColorMasks/center/special.webp
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
themes/fonts/5e/Davek.woff2
Normal file
BIN
themes/fonts/5e/Iokharic.woff2
Normal file
BIN
themes/fonts/5e/Rellanic.woff2
Normal file
@@ -113,3 +113,23 @@
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Davek;
|
||||
src: url('../../../fonts/5e/Davek.woff2');
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Iokharic;
|
||||
src: url('../../../fonts/5e/Iokharic.woff2');
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: Rellanic;
|
||||
src: url('../../../fonts/5e/Rellanic.woff2');
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||