0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2025-12-24 20:42:43 +00:00

Merge branch 'master' into type-fix-for-stylesheet-import-in-html

This commit is contained in:
Trevor Buckner
2024-03-06 12:53:04 -05:00
committed by GitHub
27 changed files with 1416 additions and 843 deletions

View File

@@ -20,7 +20,7 @@ const PAGE_HEIGHT = 1056;
const INITIAL_CONTENT = dedent`
<!DOCTYPE html><html><head>
<link href="//use.fontawesome.com/releases/v5.15.1/css/all.css" type="text/css" rel="stylesheet" />
<link href="//use.fontawesome.com/releases/v6.5.1/css/all.css" rel="stylesheet" type="text/css" />
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
<link href='/homebrew/bundle.css' type="text/css" rel='stylesheet' />
<base target=_blank>

View File

@@ -160,21 +160,21 @@ const Editor = createClass({
}
}
// Superscript
if(line.includes('\^')) {
const regex = /\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^/g;
let match;
while ((match = regex.exec(line)) != null) {
codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[1]) - 1 }, { line: lineNumber, ch: line.indexOf(match[1]) + match[1].length + 1 }, { className: 'superscript' });
}
}
// Subscript
if(line.includes('^^')) {
const regex = /\^\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^\^/g;
let match;
while ((match = regex.exec(line)) != null) {
codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[1]) - 2 }, { line: lineNumber, ch: line.indexOf(match[1]) + match[1].length + 2 }, { className: 'subscript' });
// Subscript & Superscript
if(line.includes('^')) {
let startIndex = line.indexOf('^');
const superRegex = /\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^/gy;
const subRegex = /\^\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^\^/gy;
while (startIndex >= 0) {
superRegex.lastIndex = subRegex.lastIndex = startIndex;
let isSuper = false;
let match = subRegex.exec(line) || superRegex.exec(line);
if (match) {
isSuper = !subRegex.lastIndex;
codeMirror.markText({ line: lineNumber, ch: match.index }, { line: lineNumber, ch: match.index + match[0].length }, { className: isSuper ? 'superscript' : 'subscript' });
}
startIndex = line.indexOf('^', Math.max(startIndex + 1, subRegex.lastIndex, superRegex.lastIndex));
}
}

View File

@@ -1,272 +1,345 @@
@import "naturalcrit/styles/colors.less";
@navbarHeight : 28px;
@keyframes pinkColoring {
0% {color : pink;}
50% {color : pink;}
75% {color : red;}
100% {color : pink;}
}
.homebrew nav {
.homebrewLogo {
.animate(color);
font-family : CodeBold;
font-size : 12px;
color : white;
div {
margin-top : 2px;
margin-bottom : -2px;
}
&:hover {
color : @blue;
}
}
.editTitle.navItem {
padding : 2px 12px;
input {
font-family : "Open Sans", sans-serif;
font-size : 12px;
font-weight : 800;
width : 250px;
margin : 0;
padding : 2px;
text-align : center;
color : white;
border : 1px solid @blue;
outline : none;
background-color : transparent;
}
.charCount {
display : inline-block;
margin-left : 8px;
text-align : right;
vertical-align : bottom;
color : #666;
&.max {
color : @red;
}
}
}
.brewTitle.navItem {
font-size : 12px;
font-weight : 800;
height : 100%;
text-align : center;
text-transform : initial;
color : white;
background-color : transparent;
flex-grow : 1;
}
.save-menu {
.dropdown {
z-index : 1000;
}
.navItem i.fa-power-off {
color : red;
&.active {
color : rgb(0, 182, 52);
filter : drop-shadow(0 0 2px rgba(0, 182, 52, 0.765));
}
}
}
.patreon.navItem {
border-right : 1px solid #666;
border-left : 1px solid #666;
&:hover i {
color : red;
}
i {
.animate(color);
animation-name : pinkColoring;
animation-duration : 2s;
color : pink;
}
}
.recent.navDropdownContainer {
position : relative;
.navDropdown .navItem {
overflow : hidden auto;
max-height : ~"calc(100vh - 28px)";
scrollbar-color : #666 #333;
scrollbar-width : thin;
@import 'naturalcrit/styles/colors.less';
#backgroundColorsHover;
.animate(background-color);
position : relative;
display : block;
overflow : clip;
box-sizing : border-box;
padding : 8px 5px 13px;
text-decoration : none;
color : white;
border-top : 1px solid #888;
background-color : #333;
.clear {
position : absolute;
top : 50%;
right : 0;
display : none;
width : 20px;
height : 100%;
transform : translateY(-50%);
opacity : 70%;
border-radius : 3px;
background-color : #333;
&:hover {
opacity : 100%;
}
i {
font-size : 10px;
width : 100%;
height : 100%;
margin : 0;
text-align : center;
}
}
&:hover {
background-color : @blue;
.clear {
display : grid;
place-content : center;
}
}
.title {
display : inline-block;
overflow : hidden;
width : 100%;
white-space : nowrap;
text-overflow : ellipsis;
}
.time {
font-size : 0.7em;
position : absolute;
right : 2px;
bottom : 2px;
color : #888;
}
&.header {
display : block;
box-sizing : border-box;
padding : 5px 0;
text-align : center;
color : #BBB;
border-top : 1px solid #888;
background-color : #333;
&:nth-of-type(1) {
background-color : darken(@teal, 20%);
}
&:nth-of-type(2) {
background-color : darken(@purple, 30%);
}
}
}
@navbarHeight : 28px;
@keyframes pinkColoring {
0% { color : pink; }
50% { color : pink; }
75% { color : red; }
100% { color : pink; }
}
@keyframes glideDropDown {
0% {
background-color : #333333;
opacity : 0;
transform : translate(0px, -100%);
}
.metadata.navItem {
position : relative;
100% {
background-color : #333333;
opacity : 1;
transform : translate(0px, 0px);
}
}
.homebrew nav {
background-color : #333333;
.navContent {
position : relative;
z-index : 2;
display : flex;
justify-content : space-between;
}
.navSection {
display : flex;
align-items : center;
height : 100%;
padding : 0;
flex-grow : 1;
i {
margin-right : 10px;
&:last-child .navItem { border-left : 1px solid #666666; }
}
// "NaturalCrit" logo
.navLogo {
display : block;
margin-top : 0px;
margin-right : 8px;
margin-left : 8px;
color : white;
text-decoration : none;
&:hover {
.name { color : @orange; }
svg { fill : @orange; }
}
.window {
position : absolute;
z-index : -1;
bottom : 0;
left : 50%;
display : flex;
justify-content : flex-start;
width : 440px;
max-height : ~"calc(100vh - 28px)";
margin : 0 auto;
padding : 0 10px 5px;
transition : transform 0.4s, opacity 0.4s;
border : 3px solid #444;
border-top : unset;
border-radius : 0 0 5px 5px;
background-color : #333;
box-shadow : inset 0 7px 9px -7px #111;
flex-flow : row wrap;
align-content : baseline;
&.active {
transform : translateX(-50%) translateY(100%);
opacity : 1;
svg {
height : 13px;
margin-right : 0.2em;
cursor : pointer;
fill : white;
}
span.name {
font-family : 'CodeLight';
font-size : 15px;
span.crit { font-family : 'CodeBold'; }
small {
font-family : 'Open Sans';
font-size : 0.3em;
font-weight : 800;
text-transform : uppercase;
}
&.inactive {
transform : translateX(-50%) translateY(0%);
opacity : 0;
}
}
.navItem {
#backgroundColorsHover;
.animate(background-color);
padding : 8px 12px;
font-size : 10px;
font-weight : 800;
line-height : 13px;
color : white;
text-decoration : none;
text-transform : uppercase;
cursor : pointer;
background-color : #333333;
i {
float : right;
margin-left : 5px;
font-size : 13px;
}
&.patreon {
border-right : 1px solid #666666;
border-left : 1px solid #666666;
&:hover i { color : red; }
i {
color : pink;
.animate(color);
animation-name : pinkColoring;
animation-duration : 2s;
}
.row {
display : flex;
width : 100%;
flex-flow : row wrap;
h4 {
display : block;
box-sizing : border-box;
min-width : 76px;
padding : 5px 0;
text-align : center;
color : #BBB;
flex-basis : 20%;
flex-grow : 1;
}
&.editTitle { // this is not needed at all currently - you used to be able to edit the title via the navbar.
padding : 2px 12px;
input {
width : 250px;
padding : 2px;
margin : 0;
font-family : 'Open Sans', sans-serif;
font-size : 12px;
font-weight : 800;
color : white;
text-align : center;
background-color : transparent;
border : 1px solid @blue;
outline : none;
}
.charCount {
display : inline-block;
margin-left : 8px;
color : #666666;
text-align : right;
vertical-align : bottom;
&.max { color : @red; }
}
}
&.brewTitle {
flex-grow : 1;
font-size : 12px;
font-weight : 800;
color : white;
text-align : center;
text-transform : initial;
background-color : transparent;
}
// "The Homebrewery" logo
&.homebrewLogo {
.animate(color);
font-family : 'CodeBold';
font-size : 12px;
color : white;
div {
margin-top : 2px;
margin-bottom : -2px;
}
&:hover { color : @blue; }
}
&.metadata {
position : relative;
display : flex;
flex-grow : 1;
align-items : center;
height : 100%;
padding : 0;
i { margin-right : 10px;}
.window {
position : absolute;
bottom : 0;
left : 50%;
z-index : -1;
display : flex;
flex-flow : row wrap;
align-content : baseline;
justify-content : flex-start;
width : 440px;
max-height : ~'calc(100vh - 28px)';
padding : 0 10px 5px;
margin : 0 auto;
background-color : #333333;
border : 3px solid #444444;
border-top : unset;
border-radius : 0 0 5px 5px;
box-shadow : inset 0 7px 9px -7px #111111;
transition : transform 0.4s, opacity 0.4s;
&.active {
opacity : 1;
transform : translateX(-50%) translateY(100%);
}
p {
font-family : "Open Sans", sans-serif;
font-size : 10px;
font-weight : normal;
padding : 5px 0;
text-transform : initial;
flex-basis : 80%;
flex-grow : 1;
.tag {
display : inline-block;
margin : 2px 2px;
padding : 2px;
border : 2px solid grey;
border-radius : 5px;
background-color : #444;
&.inactive {
opacity : 0;
transform : translateX(-50%) translateY(0%);
}
.row {
display : flex;
flex-flow : row wrap;
width : 100%;
h4 {
box-sizing : border-box;
display : block;
flex-basis : 20%;
flex-grow : 1;
min-width : 76px;
padding : 5px 0;
color : #BBBBBB;
text-align : center;
}
a.userPageLink {
text-decoration : none;
color : white;
&:hover {
text-decoration : underline;
p {
flex-basis : 80%;
flex-grow : 1;
padding : 5px 0;
font-family : 'Open Sans', sans-serif;
font-size : 10px;
font-weight : normal;
text-transform : initial;
.tag {
display : inline-block;
padding : 2px;
margin : 2px 2px;
background-color : #444444;
border : 2px solid grey;
border-radius : 5px;
}
a.userPageLink {
color : white;
text-decoration : none;
&:hover { text-decoration : underline; }
}
}
&:nth-of-type(even) { background-color : #555555; }
}
&:nth-of-type(even) {
background-color : #555;
}
}
&.warning {
position : relative;
color : white;
background-color : @orange;
&:hover > .dropdown { visibility : visible; }
.dropdown {
position : absolute;
top : 28px;
left : 0;
z-index : 10000;
box-sizing : border-box;
display : block;
width : 100%;
padding : 13px 5px;
text-align : center;
visibility : hidden;
background-color : #333333;
}
}
&.account {
min-width : 100px;
&.username { text-transform : none;}
}
}
.navDropdownContainer {
position : relative;
.navDropdown {
position : absolute;
top : 28px;
left : 0px;
z-index : 10000;
width : 100%;
max-height : calc(100vh - 28px);
overflow : hidden auto;
.navItem {
position : relative;
display : block;
width : 100%;
padding : 8px 5px;
border : 1px solid #888888;
border-bottom : 0;
animation-name : glideDropDown;
animation-duration : 0.4s;
}
}
&.recent {
position : relative;
.navDropdown .navItem {
#backgroundColorsHover;
.animate(background-color);
position : relative;
box-sizing : border-box;
display : block;
max-height : ~'calc(100vh - 28px)';
padding : 8px 5px 13px;
overflow : hidden auto;
color : white;
text-decoration : none;
background-color : #333333;
border-top : 1px solid #888888;
scrollbar-color : #666666 #333333;
scrollbar-width : thin;
.clear {
position : absolute;
top : 50%;
right : 0;
display : none;
width : 20px;
height : 100%;
background-color : #333333;
border-radius : 3px;
opacity : 70%;
transform : translateY(-50%);
&:hover { opacity : 100%; }
i {
width : 100%;
height : 100%;
margin : 0;
font-size : 10px;
text-align : center;
}
}
&:hover {
background-color : @blue;
.clear {
display : grid;
place-content : center;
}
}
.title {
display : inline-block;
width : 100%;
overflow : hidden auto;
text-overflow : ellipsis;
white-space : nowrap;
}
.time {
position : absolute;
right : 2px;
bottom : 2px;
font-size : 0.7em;
color : #888888;
}
&.header {
box-sizing : border-box;
display : block;
padding : 5px 0;
color : #BBBBBB;
text-align : center;
background-color : #333333;
border-top : 1px solid #888888;
&:nth-of-type(1) { background-color : darken(@teal, 20%); }
&:nth-of-type(2) { background-color : darken(@purple, 30%); }
}
}
}
}
.warning.navItem {
position : relative;
color : white;
background-color : @orange;
&:hover > .dropdown {
visibility : visible;
}
// this should likely be refactored into .navDropdownContainer
.save-menu {
.dropdown { z-index : 1000; }
.navItem i.fa-power-off {
color : red;
&.active {
color : rgb(0, 182, 52);
filter : drop-shadow(0 0 2px rgba(0, 182, 52, 0.765));
}
.dropdown {
position : absolute;
z-index : 10000;
top : 28px;
left : 0;
display : block;
visibility : hidden;
box-sizing : border-box;
width : 100%;
padding : 13px 5px;
text-align : center;
background-color : #333;
}
}
.account.navItem {
min-width : 100px;
}
.account.username.navItem {
text-transform : none;
}
}

View File

@@ -1,12 +1,64 @@
const React = require('react');
const _ = require('lodash');
const Nav = require('naturalcrit/nav/nav.jsx');
const { splitTextStyleAndMetadata } = require('../../../shared/helpers.js'); // Importing the function from helpers.js
module.exports = function(props){
return <Nav.item
href='/new'
newTab={true}
color='purple'
icon='fas fa-plus-square'>
new
</Nav.item>;
const BREWKEY = 'homebrewery-new';
const STYLEKEY = 'homebrewery-new-style';
const METAKEY = 'homebrewery-new-meta';
const NewBrew = () => {
const handleFileChange = (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
const fileContent = e.target.result;
const newBrew = {
text: fileContent,
style: ''
};
if(fileContent.startsWith('```metadata')) {
splitTextStyleAndMetadata(newBrew); // Modify newBrew directly
localStorage.setItem(BREWKEY, newBrew.text);
localStorage.setItem(STYLEKEY, newBrew.style);
localStorage.setItem(METAKEY, JSON.stringify(_.pick(newBrew, ['title', 'description', 'tags', 'systems', 'renderer', 'theme', 'lang'])));
window.location.href = '/new';
} else {
alert('This file is invalid, please, enter a valid file');
}
};
reader.readAsText(file);
}
};
return (
<Nav.dropdown>
<Nav.item
className='new'
color='purple'
icon='fa-solid fa-plus-square'>
new
</Nav.item>
<Nav.item
className='fromBlank'
href='/new'
newTab={true}
color='purple'
icon='fa-solid fa-file'>
from blank
</Nav.item>
<Nav.item
className='fromFile'
color='purple'
icon='fa-solid fa-upload'
onClick={() => { document.getElementById('uploadTxt').click(); }}>
<input id="uploadTxt" className='newFromLocal' type="file" onChange={handleFileChange} style={{ display: 'none' }} />
from file
</Nav.item>
</Nav.dropdown>
);
};
module.exports = NewBrew;

View File

@@ -20,7 +20,8 @@ const BrewItem = createClass({
authors : [],
stubbed : true
},
reportError : ()=>{}
updateListFilter : ()=>{},
reportError : ()=>{}
};
},
@@ -44,6 +45,10 @@ const BrewItem = createClass({
});
},
updateFilter : function(type, term){
this.props.updateListFilter(type, term);
},
renderDeleteBrewLink : function(){
if(!this.props.brew.editId) return;
@@ -109,6 +114,9 @@ const BrewItem = createClass({
const brew = this.props.brew;
if(Array.isArray(brew.tags)) { // temporary fix until dud tags are cleaned
brew.tags = brew.tags?.filter((tag)=>tag); //remove tags that are empty strings
brew.tags.sort((a, b)=>{
return a.indexOf(':') - b.indexOf(':') != 0 ? a.indexOf(':') - b.indexOf(':') : a.toLowerCase().localeCompare(b.toLowerCase());
});
}
const dateFormatString = 'YYYY-MM-DD HH:mm:ss';
@@ -129,7 +137,7 @@ const BrewItem = createClass({
<i className='fas fa-tags'/>
{brew.tags.map((tag, idx)=>{
const matches = tag.match(/^(?:([^:]+):)?([^:]+)$/);
return <span key={idx} className={matches[1]}>{matches[2]}</span>;
return <span key={idx} className={matches[1]} onClick={()=>{this.updateFilter(tag);}}>{matches[2]}</span>;
})}
</div>
</> : <></>

View File

@@ -63,6 +63,41 @@
white-space: nowrap;
display: inline-block;
font-weight: bold;
border-color: currentColor;
cursor : pointer;
&:before {
font-family: 'Font Awesome 5 Free';
font-size: 12px;
margin-right: 3px;
}
&.type {
background-color: #0080003b;
color: #008000;
&:before{
content: '\f0ad';
}
}
&.group {
background-color: #5050503b;
color: #000000;
&:before{
content: '\f500';
}
}
&.meta {
background-color: #0000803b;
color: #000080;
&:before{
content: '\f05a';
}
}
&.system {
background-color: #8000003b;
color: #800000;
&:before{
content: '\f518';
}
}
}
&:hover{
.links{

View File

@@ -36,6 +36,7 @@ const ListPage = createClass({
return {
filterString : this.props.query?.filter || '',
filterTags : [],
sortType : this.props.query?.sort || null,
sortDir : this.props.query?.dir || null,
query : this.props.query,
@@ -82,7 +83,7 @@ const ListPage = createClass({
if(!brews || !brews.length) return <div className='noBrews'>No Brews.</div>;
return _.map(brews, (brew, idx)=>{
return <BrewItem brew={brew} key={idx} reportError={this.props.reportError}/>;
return <BrewItem brew={brew} key={idx} reportError={this.props.reportError} updateListFilter={ (tag)=>{ this.updateUrl(this.state.filterString, this.state.sortType, this.state.sortDir, tag); }}/>;
});
},
@@ -136,13 +137,33 @@ const ListPage = createClass({
return;
},
updateUrl : function(filterTerm, sortType, sortDir){
updateUrl : function(filterTerm, sortType, sortDir, filterTag=''){
const url = new URL(window.location.href);
const urlParams = new URLSearchParams(url.search);
urlParams.set('sort', sortType);
urlParams.set('dir', sortDir);
let filterTags = urlParams.getAll('tag');
if(filterTag != '') {
if(filterTags.findIndex((tag)=>{return tag.toLowerCase()==filterTag.toLowerCase();}) == -1){
filterTags.push(filterTag);
} else {
filterTags = filterTags.filter((tag)=>{ return tag.toLowerCase() != filterTag.toLowerCase(); });
}
}
urlParams.delete('tag');
// Add tags to URL in the order they were clicked
filterTags.forEach((tag)=>{ urlParams.append('tag', tag); });
// Sort tags before updating state
filterTags.sort((a, b)=>{
return a.indexOf(':') - b.indexOf(':') != 0 ? a.indexOf(':') - b.indexOf(':') : a.toLowerCase().localeCompare(b.toLowerCase());
});
this.setState({
filterTags
});
if(!filterTerm)
urlParams.delete('filter');
else
@@ -166,6 +187,16 @@ const ListPage = createClass({
</div>;
},
renderTagsOptions : function(){
if(this.state.filterTags?.length == 0) return;
return <div className='tags-container'>
{_.map(this.state.filterTags, (tag, idx)=>{
const matches = tag.match(/^(?:([^:]+):)?([^:]+)$/);
return <span key={idx} className={matches[1]} onClick={()=>{ this.updateUrl(this.state.filterString, this.state.sortType, this.state.sortDir, tag); }}>{matches[2]}</span>;
})}
</div>;
},
renderSortOptions : function(){
return <div className='sort-container'>
<h6>Sort by :</h6>
@@ -176,9 +207,6 @@ const ListPage = createClass({
{/* {this.renderSortOption('Latest', 'latest')} */}
{this.renderFilterOption()}
</div>;
},
@@ -186,14 +214,28 @@ const ListPage = createClass({
const testString = _.deburr(this.state.filterString).toLowerCase();
brews = _.filter(brews, (brew)=>{
// Filter by user entered text
const brewStrings = _.deburr([
brew.title,
brew.description,
brew.tags].join('\n')
.toLowerCase());
return brewStrings.includes(testString);
const filterTextTest = brewStrings.includes(testString);
// Filter by user selected tags
let filterTagTest = true;
if(this.state.filterTags.length > 0){
filterTagTest = Array.isArray(brew.tags) && this.state.filterTags?.every((tag)=>{
return brew.tags.findIndex((brewTag)=>{
return brewTag.toLowerCase() == tag.toLowerCase();
}) >= 0;
});
}
return filterTextTest && filterTagTest;
});
return _.orderBy(brews, (brew)=>{ return this.sortBrewOrder(brew); }, this.state.sortDir);
},
@@ -224,6 +266,7 @@ const ListPage = createClass({
<link href='/themes/V3/5ePHB/style.css' type="text/css" rel='stylesheet'/>
{this.props.navItems}
{this.renderSortOptions()}
{this.renderTagsOptions()}
<div className='content V3'>
<div className='page'>

View File

@@ -53,7 +53,7 @@
}
}
}
.sort-container{
.sort-container {
font-family : 'Open Sans', sans-serif;
position : sticky;
top : 0;
@@ -125,4 +125,66 @@
}
.tags-container {
height : 30px;
background-color : #555;
border-top : 1px solid #666;
border-bottom : 1px solid #666;
color : white;
display : flex;
justify-content : center;
align-items : center;
column-gap : 15px;
row-gap : 5px;
flex-wrap : wrap;
span {
font-family : 'Open Sans', sans-serif;
font-size : 11px;
font-weight : bold;
border : 1px solid;
border-radius : 3px;
padding : 3px;
cursor : pointer;
color: #dfdfdf;
&:before {
font-family: 'Font Awesome 5 Free';
font-size: 12px;
margin-right: 3px;
}
&:after {
content: '\f00d';
font-family: 'Font Awesome 5 Free';
font-size: 12px;
margin-left: 3px;
}
&.type {
background-color: #008000;
border-color: #00a000;
&:before{
content: '\f0ad';
}
}
&.group {
background-color: #505050;
border-color: #000000;
&:before{
content: '\f500';
}
}
&.meta {
background-color: #000080;
border-color: #0000a0;
&:before{
content: '\f05a';
}
}
&.system {
background-color: #800000;
border-color: #a00000;
&:before{
content: '\f518';
}
}
}
}
}

View File

@@ -143,6 +143,7 @@ Much nicer than `<br><br><br><br><br>`
### Column Breaks
Column and page breaks with `\column` and `\page`.
\column
### Tables
Tables now allow column & row spanning between cells. This is included in some updated snippets, but a simplified example is given below.

View File

@@ -12,7 +12,7 @@ const template = async function(name, title='', props = {}){
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, height=device-height, interactive-widget=resizes-visual" />
<link href="//use.fontawesome.com/releases/v5.15.1/css/all.css type="text/css" rel="stylesheet" />
<link href="//use.fontawesome.com/releases/v6.5.1/css/all.css" rel="stylesheet" type="text/css" />
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
<link href=${`/${name}/bundle.css`} type="text/css" rel='stylesheet' />
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon" />

956
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -80,9 +80,9 @@
]
},
"dependencies": {
"@babel/core": "^7.23.9",
"@babel/plugin-transform-runtime": "^7.23.9",
"@babel/preset-env": "^7.23.9",
"@babel/core": "^7.24.0",
"@babel/plugin-transform-runtime": "^7.24.0",
"@babel/preset-env": "^7.24.0",
"@babel/preset-react": "^7.23.3",
"@googleapis/drive": "^8.7.0",
"body-parser": "^1.20.2",
@@ -92,7 +92,7 @@
"create-react-class": "^15.7.0",
"dedent-tabs": "^0.10.3",
"expr-eval": "^2.0.2",
"express": "^4.18.2",
"express": "^4.18.3",
"express-async-handler": "^1.2.0",
"express-static-gzip": "2.1.7",
"fs-extra": "11.2.0",
@@ -106,21 +106,21 @@
"marked-smartypants-lite": "^1.0.2",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.30.1",
"mongoose": "^8.2.0",
"mongoose": "^8.2.1",
"nanoid": "3.3.4",
"nconf": "^0.12.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-frame-component": "^4.1.3",
"react-router-dom": "6.22.1",
"react-router-dom": "6.22.2",
"sanitize-filename": "1.6.3",
"superagent": "^8.1.2",
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
},
"devDependencies": {
"eslint": "^8.56.0",
"eslint": "^8.57.0",
"eslint-plugin-jest": "^27.9.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react": "^7.34.0",
"jest": "^29.7.0",
"jest-expect-message": "^1.1.3",
"postcss-less": "^6.0.0",

View File

@@ -17,21 +17,8 @@ const asyncHandler = require('express-async-handler');
const { DEFAULT_BREW } = require('./brewDefaults.js');
const splitTextStyleAndMetadata = (brew)=>{
brew.text = brew.text.replaceAll('\r\n', '\n');
if(brew.text.startsWith('```metadata')) {
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', 'lang']));
brew.text = brew.text.slice(index + 5);
}
if(brew.text.startsWith('```css')) {
const index = brew.text.indexOf('```\n\n');
brew.style = brew.text.slice(7, index - 1);
brew.text = brew.text.slice(index + 5);
}
};
const { splitTextStyleAndMetadata } = require('../shared/helpers.js');
const sanitizeBrew = (brew, accessType)=>{
brew._id = undefined;

22
shared/helpers.js Normal file
View File

@@ -0,0 +1,22 @@
const _ = require('lodash');
const yaml = require('js-yaml');
const splitTextStyleAndMetadata = (brew) => {
brew.text = brew.text.replaceAll('\r\n', '\n');
if (brew.text.startsWith('```metadata')) {
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', 'lang']));
brew.text = brew.text.slice(index + 5);
}
if (brew.text.startsWith('```css')) {
const index = brew.text.indexOf('```\n\n');
brew.style = brew.text.slice(7, index - 1);
brew.text = brew.text.slice(index + 5);
}
};
module.exports = {
splitTextStyleAndMetadata
};

View File

@@ -1,4 +1,4 @@
require('./nav.less');
require('client/homebrew/navbar/navbar.less');
const React = require('react');
const { useState, useRef, useEffect } = React;
const createClass = require('create-react-class');

View File

@@ -1,97 +0,0 @@
@import '../styles/colors';
@keyframes glideDropDown {
0% {transform : translate(0px, -100%);
opacity : 0;
background-color: #333;}
100% {transform : translate(0px, 0px);
opacity : 1;
background-color: #333;}
}
nav{
background-color : #333;
.navContent{
position : relative;
display : flex;
justify-content : space-between;
z-index : 2;
}
.navSection{
display : flex;
align-items : center;
}
.navLogo{
display : block;
margin-top : 0px;
margin-right : 8px;
margin-left : 8px;
color : white;
text-decoration : none;
&:hover{
.name{ color : @orange; }
svg{ fill : @orange }
}
svg{
height : 13px;
margin-right : 0.2em;
cursor : pointer;
fill : white;
}
span.name{
font-family : 'CodeLight';
font-size : 15px;
span.crit{
font-family : 'CodeBold';
}
small{
font-family : 'Open Sans';
font-size : 0.3em;
font-weight : 800;
text-transform : uppercase;
}
}
}
.navItem{
#backgroundColorsHover;
.animate(background-color);
padding : 8px 12px;
cursor : pointer;
background-color : #333;
font-size : 10px;
font-weight : 800;
color : white;
text-decoration : none;
text-transform : uppercase;
line-height : 13px;
i{
margin-left : 5px;
font-size : 13px;
float : right;
}
}
.navSection:last-child .navItem{
border-left : 1px solid #666;
}
.navDropdownContainer{
position: relative;
.navDropdown {
position : absolute;
top : 28px;
left : 0px;
z-index : 10000;
width : 100%;
overflow : hidden auto;
max-height : calc(100vh - 28px);
.navItem{
animation-name: glideDropDown;
animation-duration: 0.4s;
position : relative;
display : block;
width : 100%;
vertical-align : middle;
padding : 8px 5px;
border : 1px solid #888;
border-bottom : 0;
}
}
}
}

View File

@@ -78,7 +78,7 @@ module.exports = function(props){
return dedent`
{{toc,wide
# Table Of Contents
# Contents
${markdown}
}}

View File

@@ -307,7 +307,6 @@
margin-left : -0.16cm;
background-color : var(--HB_Color_MonsterStatBackground);
background-image : @monsterBlockBackground;
background-attachment : unset;
background-blend-mode : overlay;
border-style : solid;
border-width : 7px 6px;
@@ -403,15 +402,9 @@
}
}
.pageNumber {
position : absolute;
right : 2px;
bottom : 22px;
width : 50px;
font-size : 0.9em;
color : var(--HB_Color_Footnotes);
text-align : center;
text-indent : 0;
&.auto::after { content : counter(phb-page-numbers); }
}
.footnote {
position : absolute;

View File

@@ -8,7 +8,7 @@
}
@page { margin : 0; }
body { counter-reset : phb-page-numbers; }
body { counter-reset : page-numbers; }
* { -webkit-print-color-adjust : exact; }
//*****************************
@@ -47,7 +47,7 @@ body { counter-reset : phb-page-numbers; }
height : 279.4mm;
padding : 1.4cm 1.9cm 1.7cm;
overflow : hidden;
counter-increment : phb-page-numbers;
counter-increment : page-numbers;
background-color : var(--HB_Color_Background);
text-rendering : optimizeLegibility;
contain : size;
@@ -166,7 +166,6 @@ body { counter-reset : phb-page-numbers; }
margin : 0;
font-size : 120px;
text-transform : uppercase;
mix-blend-mode : overlay;
opacity : 30%;
transform : rotate(-45deg);
p { margin-bottom : none; }
@@ -460,3 +459,22 @@ body { counter-reset : phb-page-numbers; }
.homebreweryIcon.red { background-color : red; }
.homebreweryIcon.gold { background-image : linear-gradient(to top left, brown 22.5%, gold 40%, white 60%, gold 67.5%, brown 82.5%); }
}
//*****************************
//* Page Number
//*****************************/
.page {
.pageNumber {
position : absolute;
right : 30px;
bottom : 30px;
width : 50px;
font-size : 0.9em;
text-align : center;
&.auto::after { content : counter(page-numbers); }
}
&:nth-child(even) {
.pageNumber { left : 30px; }
}
}

View File

@@ -374,17 +374,9 @@
}
.pageNumber{
font-family : FrederickaTheGreat;
position : absolute;
right : 3cm;
bottom : 1.25cm;
width : 50px;
font-size : 0.9em;
color : var(--HB_Color_HeaderText);
text-align : center;
text-indent : 0;
&.auto::after {
content : counter(phb-page-numbers);
}
}
.footnote{
position : absolute;

View File

@@ -0,0 +1,121 @@
.CodeMirror {
background: #0C0C0C;
color: #B9BDB6;
}
/* Brew BG */
.brewRenderer {
background-color: #0C0C0C;
}
.cm-s-darkvision {
/* Blinking cursor and selection */
.CodeMirror-cursor {
border-left: 1px solid #B9BDB6;
}
.CodeMirror-selected {
background: #E0E8FF40;
}
/* Line number stuff */
.CodeMirror-gutter-elt {
color: #81969A;
}
.CodeMirror-linenumber {
background-color: #0C0C0C;
}
.CodeMirror-gutter {
background-color: #0C0C0C;
}
/* column splits */
.editor .codeEditor .columnSplit {
font-style: italic;
color: inherit;
background-color:#1F5763;
border-bottom: #299 solid 1px;
}
/* # headings */
.cm-header {
color: #C51B1B;
-webkit-text-stroke-width: 0.1px;
}
/* bold points */
.cm-strong {
font-weight: bold;
color: #309DD2;
}
/* Link headings */
.cm-link {
color: #DD6300;
}
/* links */
.cm-string {
color: #5CE638;
}
/*@import*/
.cm-def {
color: #2986CC;
}
/* Bullets and such */
.cm-variable-2 {
color: #3CBF30;
}
/* Tags (divs) */
.cm-tag {
color: #E3FF00;
}
.cm-attribute {
color: #E3FF00;
}
.cm-atom {
color: #CF7EA9;
}
.cm-qualifier {
color: #EE1919;
}
.cm-comment {
color: #BBC700;
}
.cm-keyword {
color: #CC66FF;
}
.cm-property {
color: aqua;
}
.cm-error {
color: #C50202;
}
.CodeMirror-foldmarker {
color: #F0FF00;
}
/* New page */
.cm-builtin {
color: #FFF;
}
}
.editor .codeEditor {
/* blocks */
.block:not(.cm-comment) {
color: magenta;
}
/* definition lists */
.define.definition {
color: #FFAA3E;
}
.define.term {
color: #7290d9;
}
.define:not(.term):not(.definition) {
background: #333;
}
/* New page */
.pageLine {
background: #000;
color: #000;
border-bottom: 1px solid #FFF;
}
}

View File

@@ -16,6 +16,7 @@
"colorforth",
"darcula",
"darkbrewery-v301",
"darkvision",
"dracula",
"duotone-dark",
"duotone-light",

View File

@@ -0,0 +1,118 @@
/*
Icon Font: dicefont
*/
@font-face {
font-family: 'DiceFont';
src: url('../../../fonts/5e/dicefont.woff2') format('woff2'),
url('../../../fonts/5e/dicefont.woff') format('woff');
font-weight: normal;
font-style: normal;
}
.df {
display: inline-block;
font-family: 'DiceFont';
font-style: normal;
font-weight: normal;
font-variant: normal;
line-height: 1;
text-decoration: inherit;
text-rendering: optimizeLegibility;
text-transform: none;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
font-smooth: antialiased;
&.F:before { content: '\f190'; }
&.F-minus:before { content: '\f191'; }
&.F-plus:before { content: '\f192'; }
&.F-zero:before { content: '\f193'; }
&.d10:before { content: '\f194'; }
&.d10-0:before { content: '\f100'; }
&.d10-1:before { content: '\f101'; }
&.d10-10:before { content: '\f102'; }
&.d10-2:before { content: '\f103'; }
&.d10-3:before { content: '\f104'; }
&.d10-4:before { content: '\f105'; }
&.d10-5:before { content: '\f106'; }
&.d10-6:before { content: '\f107'; }
&.d10-7:before { content: '\f108'; }
&.d10-8:before { content: '\f109'; }
&.d10-9:before { content: '\f10a'; }
&.d12:before { content: '\f195'; }
&.d12-1:before { content: '\f10b'; }
&.d12-10:before { content: '\f10c'; }
&.d12-11:before { content: '\f10d'; }
&.d12-12:before { content: '\f10e'; }
&.d12-2:before { content: '\f10f'; }
&.d12-3:before { content: '\f110'; }
&.d12-4:before { content: '\f111'; }
&.d12-5:before { content: '\f112'; }
&.d12-6:before { content: '\f113'; }
&.d12-7:before { content: '\f114'; }
&.d12-8:before { content: '\f115'; }
&.d12-9:before { content: '\f116'; }
&.d2:before { content: '\f196'; }
&.d2-1:before { content: '\f117'; }
&.d2-2:before { content: '\f118'; }
&.d20:before { content: '\f197'; }
&.d20-1:before { content: '\f119'; }
&.d20-10:before { content: '\f11a'; }
&.d20-11:before { content: '\f11b'; }
&.d20-12:before { content: '\f11c'; }
&.d20-13:before { content: '\f11d'; }
&.d20-14:before { content: '\f11e'; }
&.d20-15:before { content: '\f11f'; }
&.d20-16:before { content: '\f120'; }
&.d20-17:before { content: '\f121'; }
&.d20-18:before { content: '\f122'; }
&.d20-19:before { content: '\f123'; }
&.d20-2:before { content: '\f124'; }
&.d20-20:before { content: '\f125'; }
&.d20-3:before { content: '\f126'; }
&.d20-4:before { content: '\f127'; }
&.d20-5:before { content: '\f128'; }
&.d20-6:before { content: '\f129'; }
&.d20-7:before { content: '\f12a'; }
&.d20-8:before { content: '\f12b'; }
&.d20-9:before { content: '\f12c'; }
&.d4:before { content: '\f198'; }
&.d4-1:before { content: '\f12d'; }
&.d4-2:before { content: '\f12e'; }
&.d4-3:before { content: '\f12f'; }
&.d4-4:before { content: '\f130'; }
&.d6:before { content: '\f199'; }
&.d6-1:before { content: '\f131'; }
&.d6-2:before { content: '\f132'; }
&.d6-3:before { content: '\f133'; }
&.d6-4:before { content: '\f134'; }
&.d6-5:before { content: '\f135'; }
&.d6-6:before { content: '\f136'; }
&.d8:before { content: '\f19a'; }
&.d8-1:before { content: '\f137'; }
&.d8-2:before { content: '\f138'; }
&.d8-3:before { content: '\f139'; }
&.d8-4:before { content: '\f13a'; }
&.d8-5:before { content: '\f13b'; }
&.d8-6:before { content: '\f13c'; }
&.d8-7:before { content: '\f13d'; }
&.d8-8:before { content: '\f13e'; }
&.dot-d6:before { content: '\f19b'; }
&.dot-d6-1:before { content: '\f13f'; }
&.dot-d6-2:before { content: '\f140'; }
&.dot-d6-3:before { content: '\f141'; }
&.dot-d6-4:before { content: '\f142'; }
&.dot-d6-5:before { content: '\f143'; }
&.dot-d6-6:before { content: '\f18f'; }
&.small-dot-d6-1:before { content: '\f183'; }
&.small-dot-d6-2:before { content: '\f184'; }
&.small-dot-d6-3:before { content: '\f185'; }
&.small-dot-d6-4:before { content: '\f186'; }
&.small-dot-d6-5:before { content: '\f187'; }
&.small-dot-d6-6:before { content: '\f188'; }
&.solid-small-dot-d6-1:before { content: '\f189'; }
&.solid-small-dot-d6-2:before { content: '\f18a'; }
&.solid-small-dot-d6-3:before { content: '\f18b'; }
&.solid-small-dot-d6-4:before { content: '\f18c'; }
&.solid-small-dot-d6-5:before { content: '\f18d'; }
&.solid-small-dot-d6-6:before { content: '\f18e'; }
}

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,18 @@
# License
DiceFont is open source. You can use it for commercial projects, personal
projects or open source projects.
## Font License
Applies to all desktop and webfont files: [License: SIL OFL 1.1](http://scripts.sil.org/OFL)
## Code License
Applies to all CSS and LESS files: [License: MIT License](http://opensource.org/licenses/mit-license.html)
## Documentation License
Applies to all other files [CC BY 3.0](http://creativecommons.org/licenses/by/3.0/)
Copyright [Franco Ponticelli](https://github.com/fponticelli).

View File

@@ -1,3 +1,5 @@
@import url('./dicefont.less');
/* Main Font, serif */
@font-face {
font-family: BookInsanityRemake;