mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-05 05:52:46 +00:00
Merge branch 'master' into experimentalBrewLocking-#3326
This commit is contained in:
@@ -20,9 +20,9 @@ const PAGE_HEIGHT = 1056;
|
|||||||
|
|
||||||
const INITIAL_CONTENT = dedent`
|
const INITIAL_CONTENT = dedent`
|
||||||
<!DOCTYPE html><html><head>
|
<!DOCTYPE html><html><head>
|
||||||
<link href="//use.fontawesome.com/releases/v5.15.1/css/all.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="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
|
||||||
<link href='/homebrew/bundle.css' rel='stylesheet' />
|
<link href='/homebrew/bundle.css' type="text/css" rel='stylesheet' />
|
||||||
<base target=_blank>
|
<base target=_blank>
|
||||||
</head><body style='overflow: hidden'><div></div></body></html>`;
|
</head><body style='overflow: hidden'><div></div></body></html>`;
|
||||||
|
|
||||||
@@ -211,11 +211,11 @@ const BrewRenderer = (props)=>{
|
|||||||
<RenderWarnings />
|
<RenderWarnings />
|
||||||
<NotificationPopup />
|
<NotificationPopup />
|
||||||
</div>
|
</div>
|
||||||
<link href={`/themes/${rendererPath}/Blank/style.css`} rel='stylesheet'/>
|
<link href={`/themes/${rendererPath}/Blank/style.css`} type="text/css" rel='stylesheet'/>
|
||||||
{baseThemePath &&
|
{baseThemePath &&
|
||||||
<link href={`/themes/${rendererPath}/${baseThemePath}/style.css`} rel='stylesheet'/>
|
<link href={`/themes/${rendererPath}/${baseThemePath}/style.css`} type="text/css" rel='stylesheet'/>
|
||||||
}
|
}
|
||||||
<link href={`/themes/${rendererPath}/${themePath}/style.css`} rel='stylesheet'/>
|
<link href={`/themes/${rendererPath}/${themePath}/style.css`} type="text/css" rel='stylesheet'/>
|
||||||
|
|
||||||
{/* Apply CSS from Style tab and render pages from Markdown tab */}
|
{/* Apply CSS from Style tab and render pages from Markdown tab */}
|
||||||
{state.isMounted
|
{state.isMounted
|
||||||
|
|||||||
@@ -151,30 +151,37 @@ const Editor = createClass({
|
|||||||
|
|
||||||
// definition lists
|
// definition lists
|
||||||
if(line.includes('::')){
|
if(line.includes('::')){
|
||||||
const regex = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym;
|
if(/^:*$/.test(line) == true){ return };
|
||||||
|
const regex = /^([^\n]*?:?\s?)(::[^\n]*)(?:\n|$)/ymd; // the `d` flag, for match indices, throws an ESLint error.
|
||||||
let match;
|
let match;
|
||||||
while ((match = regex.exec(line)) != null){
|
while ((match = regex.exec(line)) != null){
|
||||||
codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[0]) }, { line: lineNumber, ch: line.indexOf(match[0]) + match[0].length }, { className: 'define' });
|
codeMirror.markText({ line: lineNumber, ch: match.indices[0][0] }, { line: lineNumber, ch: match.indices[0][1] }, { className: 'dl-highlight' });
|
||||||
codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[1]) }, { line: lineNumber, ch: line.indexOf(match[1]) + match[1].length }, { className: 'term' });
|
codeMirror.markText({ line: lineNumber, ch: match.indices[1][0] }, { line: lineNumber, ch: match.indices[1][1] }, { className: 'dt-highlight' });
|
||||||
codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[2]) }, { line: lineNumber, ch: line.indexOf(match[2]) + match[2].length }, { className: 'definition' });
|
codeMirror.markText({ line: lineNumber, ch: match.indices[2][0] }, { line: lineNumber, ch: match.indices[2][1] }, { className: 'dd-highlight' });
|
||||||
|
const ddIndex = match.indices[2][0];
|
||||||
|
let colons = /::/g;
|
||||||
|
let colonMatches = colons.exec(match[2]);
|
||||||
|
if(colonMatches !== null){
|
||||||
|
codeMirror.markText({ line: lineNumber, ch: colonMatches.index + ddIndex }, { line: lineNumber, ch: colonMatches.index + colonMatches[0].length + ddIndex }, { className: 'dl-colon-highlight'} )
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Superscript
|
// Subscript & Superscript
|
||||||
if(line.includes('\^')) {
|
if(line.includes('^')) {
|
||||||
const regex = /\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^/g;
|
let startIndex = line.indexOf('^');
|
||||||
let match;
|
const superRegex = /\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^/gy;
|
||||||
while ((match = regex.exec(line)) != null) {
|
const subRegex = /\^\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^\^/gy;
|
||||||
codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[1]) - 1 }, { line: lineNumber, ch: line.indexOf(match[1]) + match[1].length + 1 }, { className: 'superscript' });
|
|
||||||
}
|
while (startIndex >= 0) {
|
||||||
}
|
superRegex.lastIndex = subRegex.lastIndex = startIndex;
|
||||||
|
let isSuper = false;
|
||||||
// Subscript
|
let match = subRegex.exec(line) || superRegex.exec(line);
|
||||||
if(line.includes('^^')) {
|
if (match) {
|
||||||
const regex = /\^\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^\^/g;
|
isSuper = !subRegex.lastIndex;
|
||||||
let match;
|
codeMirror.markText({ line: lineNumber, ch: match.index }, { line: lineNumber, ch: match.index + match[0].length }, { className: isSuper ? 'superscript' : 'subscript' });
|
||||||
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' });
|
startIndex = line.indexOf('^', Math.max(startIndex + 1, subRegex.lastIndex, superRegex.lastIndex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,16 @@
|
|||||||
vertical-align : sub;
|
vertical-align : sub;
|
||||||
font-size : 0.9em;
|
font-size : 0.9em;
|
||||||
}
|
}
|
||||||
|
.dl-highlight {
|
||||||
|
&.dl-colon-highlight {
|
||||||
|
font-weight : bold;
|
||||||
|
color : #949494;
|
||||||
|
background : #E5E5E5;
|
||||||
|
border-radius : 3px;
|
||||||
|
}
|
||||||
|
&.dt-highlight { color : rgb(96, 117, 143); }
|
||||||
|
&.dd-highlight { color : rgb(97, 57, 178); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.brewJump {
|
.brewJump {
|
||||||
|
|||||||
@@ -1,272 +1,351 @@
|
|||||||
@import "naturalcrit/styles/colors.less";
|
@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;
|
|
||||||
|
|
||||||
|
|
||||||
#backgroundColorsHover;
|
@navbarHeight : 28px;
|
||||||
.animate(background-color);
|
|
||||||
position : relative;
|
@keyframes pinkColoring {
|
||||||
display : block;
|
0% { color : pink; }
|
||||||
overflow : clip;
|
50% { color : pink; }
|
||||||
box-sizing : border-box;
|
75% { color : red; }
|
||||||
padding : 8px 5px 13px;
|
100% { color : pink; }
|
||||||
text-decoration : none;
|
}
|
||||||
color : white;
|
|
||||||
border-top : 1px solid #888;
|
@keyframes glideDropDown {
|
||||||
background-color : #333;
|
0% {
|
||||||
.clear {
|
background-color : #333333;
|
||||||
position : absolute;
|
opacity : 0;
|
||||||
top : 50%;
|
transform : translate(0px, -100%);
|
||||||
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%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.metadata.navItem {
|
100% {
|
||||||
position : relative;
|
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;
|
display : flex;
|
||||||
align-items : center;
|
align-items : center;
|
||||||
height : 100%;
|
&:last-child .navItem { border-left : 1px solid #666666; }
|
||||||
padding : 0;
|
}
|
||||||
flex-grow : 1;
|
// "NaturalCrit" logo
|
||||||
i {
|
.navLogo {
|
||||||
margin-right : 10px;
|
display : block;
|
||||||
|
margin-top : 0px;
|
||||||
|
margin-right : 8px;
|
||||||
|
margin-left : 8px;
|
||||||
|
color : white;
|
||||||
|
text-decoration : none;
|
||||||
|
&:hover {
|
||||||
|
.name { color : @orange; }
|
||||||
|
svg { fill : @orange; }
|
||||||
}
|
}
|
||||||
.window {
|
svg {
|
||||||
position : absolute;
|
height : 13px;
|
||||||
z-index : -1;
|
margin-right : 0.2em;
|
||||||
bottom : 0;
|
cursor : pointer;
|
||||||
left : 50%;
|
fill : white;
|
||||||
display : flex;
|
}
|
||||||
justify-content : flex-start;
|
span.name {
|
||||||
width : 440px;
|
font-family : 'CodeLight';
|
||||||
max-height : ~"calc(100vh - 28px)";
|
font-size : 15px;
|
||||||
margin : 0 auto;
|
span.crit { font-family : 'CodeBold'; }
|
||||||
padding : 0 10px 5px;
|
small {
|
||||||
transition : transform 0.4s, opacity 0.4s;
|
font-family : 'Open Sans';
|
||||||
border : 3px solid #444;
|
font-size : 0.3em;
|
||||||
border-top : unset;
|
font-weight : 800;
|
||||||
border-radius : 0 0 5px 5px;
|
text-transform : uppercase;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
&.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;
|
&.editTitle { // this is not needed at all currently - you used to be able to edit the title via the navbar.
|
||||||
width : 100%;
|
padding : 2px 12px;
|
||||||
flex-flow : row wrap;
|
input {
|
||||||
h4 {
|
width : 250px;
|
||||||
display : block;
|
padding : 2px;
|
||||||
box-sizing : border-box;
|
margin : 0;
|
||||||
min-width : 76px;
|
font-family : 'Open Sans', sans-serif;
|
||||||
padding : 5px 0;
|
font-size : 12px;
|
||||||
text-align : center;
|
font-weight : 800;
|
||||||
color : #BBB;
|
color : white;
|
||||||
flex-basis : 20%;
|
text-align : center;
|
||||||
flex-grow : 1;
|
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 {
|
&.inactive {
|
||||||
font-family : "Open Sans", sans-serif;
|
opacity : 0;
|
||||||
font-size : 10px;
|
transform : translateX(-50%) translateY(0%);
|
||||||
font-weight : normal;
|
}
|
||||||
padding : 5px 0;
|
.row {
|
||||||
text-transform : initial;
|
display : flex;
|
||||||
flex-basis : 80%;
|
flex-flow : row wrap;
|
||||||
flex-grow : 1;
|
width : 100%;
|
||||||
.tag {
|
h4 {
|
||||||
display : inline-block;
|
box-sizing : border-box;
|
||||||
margin : 2px 2px;
|
display : block;
|
||||||
padding : 2px;
|
flex-basis : 20%;
|
||||||
border : 2px solid grey;
|
flex-grow : 1;
|
||||||
border-radius : 5px;
|
min-width : 76px;
|
||||||
background-color : #444;
|
padding : 5px 0;
|
||||||
|
color : #BBBBBB;
|
||||||
|
text-align : center;
|
||||||
}
|
}
|
||||||
a.userPageLink {
|
p {
|
||||||
text-decoration : none;
|
flex-basis : 80%;
|
||||||
color : white;
|
flex-grow : 1;
|
||||||
&:hover {
|
padding : 5px 0;
|
||||||
text-decoration : underline;
|
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;
|
||||||
|
right: 0px;
|
||||||
|
z-index: 10000;
|
||||||
|
width: max-content;
|
||||||
|
min-width:100%;
|
||||||
|
max-height: calc(100vh - 28px);
|
||||||
|
overflow: hidden auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
.navItem {
|
||||||
|
position : relative;
|
||||||
|
display : flex;
|
||||||
|
justify-content : space-between;
|
||||||
|
align-items : center;
|
||||||
|
width : 100%;
|
||||||
|
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-width : 15em;
|
||||||
|
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;
|
// this should likely be refactored into .navDropdownContainer
|
||||||
background-color : @orange;
|
.save-menu {
|
||||||
&:hover > .dropdown {
|
.dropdown { z-index : 1000; }
|
||||||
visibility : visible;
|
.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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,64 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
const _ = require('lodash');
|
||||||
const Nav = require('naturalcrit/nav/nav.jsx');
|
const Nav = require('naturalcrit/nav/nav.jsx');
|
||||||
|
const { splitTextStyleAndMetadata } = require('../../../shared/helpers.js'); // Importing the function from helpers.js
|
||||||
|
|
||||||
module.exports = function(props){
|
const BREWKEY = 'homebrewery-new';
|
||||||
return <Nav.item
|
const STYLEKEY = 'homebrewery-new-style';
|
||||||
href='/new'
|
const METAKEY = 'homebrewery-new-meta';
|
||||||
newTab={true}
|
|
||||||
color='purple'
|
const NewBrew = () => {
|
||||||
icon='fas fa-plus-square'>
|
const handleFileChange = (e) => {
|
||||||
new
|
const file = e.target.files[0];
|
||||||
</Nav.item>;
|
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;
|
||||||
|
|||||||
@@ -262,8 +262,8 @@ const ListPage = createClass({
|
|||||||
render : function(){
|
render : function(){
|
||||||
return <div className='listPage sitePage'>
|
return <div className='listPage sitePage'>
|
||||||
{/*<style>@layer V3_5ePHB, bundle;</style>*/}
|
{/*<style>@layer V3_5ePHB, bundle;</style>*/}
|
||||||
<link href='/themes/V3/Blank/style.css' rel='stylesheet'/>
|
<link href='/themes/V3/Blank/style.css' type="text/css" rel='stylesheet'/>
|
||||||
<link href='/themes/V3/5ePHB/style.css' rel='stylesheet'/>
|
<link href='/themes/V3/5ePHB/style.css' type="text/css" rel='stylesheet'/>
|
||||||
{this.props.navItems}
|
{this.props.navItems}
|
||||||
{this.renderSortOptions()}
|
{this.renderSortOptions()}
|
||||||
{this.renderTagsOptions()}
|
{this.renderTagsOptions()}
|
||||||
|
|||||||
@@ -96,11 +96,11 @@ const PrintPage = createClass({
|
|||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<Meta name='robots' content='noindex, nofollow' />
|
<Meta name='robots' content='noindex, nofollow' />
|
||||||
<link href={`/themes/${rendererPath}/Blank/style.css`} rel='stylesheet'/>
|
<link href={`/themes/${rendererPath}/Blank/style.css`} type="text/css" rel='stylesheet'/>
|
||||||
{baseThemePath &&
|
{baseThemePath &&
|
||||||
<link href={`/themes/${rendererPath}/${baseThemePath}/style.css`} rel='stylesheet'/>
|
<link href={`/themes/${rendererPath}/${baseThemePath}/style.css`} type="text/css" rel='stylesheet'/>
|
||||||
}
|
}
|
||||||
<link href={`/themes/${rendererPath}/${themePath}/style.css`} rel='stylesheet'/>
|
<link href={`/themes/${rendererPath}/${themePath}/style.css`} type="text/css" rel='stylesheet'/>
|
||||||
{/* Apply CSS from Style tab */}
|
{/* Apply CSS from Style tab */}
|
||||||
{this.renderStyle()}
|
{this.renderStyle()}
|
||||||
<div className='pages' ref='pages' lang={this.state.brew.lang}>
|
<div className='pages' ref='pages' lang={this.state.brew.lang}>
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ const template = async function(name, title='', props = {}){
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, height=device-height, interactive-widget=resizes-visual" />
|
<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" 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="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
|
||||||
<link href=${`/${name}/bundle.css`} rel='stylesheet' />
|
<link href=${`/${name}/bundle.css`} type="text/css" rel='stylesheet' />
|
||||||
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon" />
|
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon" />
|
||||||
${ogMetaTags}
|
${ogMetaTags}
|
||||||
<meta name="twitter:card" content="summary">
|
<meta name="twitter:card" content="summary">
|
||||||
|
|||||||
922
package-lock.json
generated
922
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@@ -31,6 +31,7 @@
|
|||||||
"test:mustache-syntax:inline": "jest '.*(mustache-syntax).*' -t '^Inline:.*' --verbose --noStackTrace",
|
"test:mustache-syntax:inline": "jest '.*(mustache-syntax).*' -t '^Inline:.*' --verbose --noStackTrace",
|
||||||
"test:mustache-syntax:block": "jest '.*(mustache-syntax).*' -t '^Block:.*' --verbose --noStackTrace",
|
"test:mustache-syntax:block": "jest '.*(mustache-syntax).*' -t '^Block:.*' --verbose --noStackTrace",
|
||||||
"test:mustache-syntax:injection": "jest '.*(mustache-syntax).*' -t '^Injection:.*' --verbose --noStackTrace",
|
"test:mustache-syntax:injection": "jest '.*(mustache-syntax).*' -t '^Injection:.*' --verbose --noStackTrace",
|
||||||
|
"test:marked-extensions": "jest tests/markdown/marked-extensions.test.js --verbose --noStackTrace",
|
||||||
"test:route": "jest tests/routes/static-pages.test.js --verbose",
|
"test:route": "jest tests/routes/static-pages.test.js --verbose",
|
||||||
"phb": "node scripts/phb.js",
|
"phb": "node scripts/phb.js",
|
||||||
"prod": "set NODE_ENV=production && npm run build",
|
"prod": "set NODE_ENV=production && npm run build",
|
||||||
@@ -80,9 +81,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.23.9",
|
"@babel/core": "^7.24.0",
|
||||||
"@babel/plugin-transform-runtime": "^7.23.9",
|
"@babel/plugin-transform-runtime": "^7.24.0",
|
||||||
"@babel/preset-env": "^7.23.9",
|
"@babel/preset-env": "^7.24.0",
|
||||||
"@babel/preset-react": "^7.23.3",
|
"@babel/preset-react": "^7.23.3",
|
||||||
"@googleapis/drive": "^8.7.0",
|
"@googleapis/drive": "^8.7.0",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
@@ -92,7 +93,7 @@
|
|||||||
"create-react-class": "^15.7.0",
|
"create-react-class": "^15.7.0",
|
||||||
"dedent-tabs": "^0.10.3",
|
"dedent-tabs": "^0.10.3",
|
||||||
"expr-eval": "^2.0.2",
|
"expr-eval": "^2.0.2",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.3",
|
||||||
"express-async-handler": "^1.2.0",
|
"express-async-handler": "^1.2.0",
|
||||||
"express-static-gzip": "2.1.7",
|
"express-static-gzip": "2.1.7",
|
||||||
"fs-extra": "11.2.0",
|
"fs-extra": "11.2.0",
|
||||||
@@ -106,13 +107,13 @@
|
|||||||
"marked-smartypants-lite": "^1.0.2",
|
"marked-smartypants-lite": "^1.0.2",
|
||||||
"markedLegacy": "npm:marked@^0.3.19",
|
"markedLegacy": "npm:marked@^0.3.19",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"mongoose": "^8.2.0",
|
"mongoose": "^8.2.1",
|
||||||
"nanoid": "3.3.4",
|
"nanoid": "3.3.4",
|
||||||
"nconf": "^0.12.1",
|
"nconf": "^0.12.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-frame-component": "^4.1.3",
|
"react-frame-component": "^4.1.3",
|
||||||
"react-router-dom": "6.22.1",
|
"react-router-dom": "6.22.3",
|
||||||
"sanitize-filename": "1.6.3",
|
"sanitize-filename": "1.6.3",
|
||||||
"superagent": "^8.1.2",
|
"superagent": "^8.1.2",
|
||||||
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
||||||
@@ -120,7 +121,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-plugin-jest": "^27.9.0",
|
"eslint-plugin-jest": "^27.9.0",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.34.0",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"jest-expect-message": "^1.1.3",
|
"jest-expect-message": "^1.1.3",
|
||||||
"postcss-less": "^6.0.0",
|
"postcss-less": "^6.0.0",
|
||||||
|
|||||||
@@ -17,21 +17,8 @@ const asyncHandler = require('express-async-handler');
|
|||||||
|
|
||||||
const { DEFAULT_BREW } = require('./brewDefaults.js');
|
const { DEFAULT_BREW } = require('./brewDefaults.js');
|
||||||
|
|
||||||
const splitTextStyleAndMetadata = (brew)=>{
|
const { splitTextStyleAndMetadata } = require('../shared/helpers.js');
|
||||||
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 sanitizeBrew = (brew, accessType)=>{
|
const sanitizeBrew = (brew, accessType)=>{
|
||||||
brew._id = undefined;
|
brew._id = undefined;
|
||||||
|
|||||||
22
shared/helpers.js
Normal file
22
shared/helpers.js
Normal 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
|
||||||
|
};
|
||||||
@@ -436,7 +436,7 @@ const CodeEditor = createClass({
|
|||||||
|
|
||||||
render : function(){
|
render : function(){
|
||||||
return <>
|
return <>
|
||||||
<link href={`../homebrew/cm-themes/${this.props.editorTheme}.css`} rel='stylesheet' />
|
<link href={`../homebrew/cm-themes/${this.props.editorTheme}.css`} type="text/css" rel='stylesheet' />
|
||||||
<div className='codeEditor' ref='editor' style={this.props.style}/>
|
<div className='codeEditor' ref='editor' style={this.props.style}/>
|
||||||
</>;
|
</>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -294,10 +294,10 @@ const superSubScripts = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const definitionLists = {
|
const definitionListsInline = {
|
||||||
name : 'definitionLists',
|
name : 'definitionListsInline',
|
||||||
level : 'block',
|
level : 'block',
|
||||||
start(src) { return src.match(/^.*?::.*/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
start(src) { return src.match(/^[^\n]*?::[^\n]*/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
tokenizer(src, tokens) {
|
tokenizer(src, tokens) {
|
||||||
const regex = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym;
|
const regex = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym;
|
||||||
let match;
|
let match;
|
||||||
@@ -312,7 +312,7 @@ const definitionLists = {
|
|||||||
}
|
}
|
||||||
if(definitions.length) {
|
if(definitions.length) {
|
||||||
return {
|
return {
|
||||||
type : 'definitionLists',
|
type : 'definitionListsInline',
|
||||||
raw : src.slice(0, endIndex),
|
raw : src.slice(0, endIndex),
|
||||||
definitions
|
definitions
|
||||||
};
|
};
|
||||||
@@ -321,11 +321,54 @@ const definitionLists = {
|
|||||||
renderer(token) {
|
renderer(token) {
|
||||||
return `<dl>${token.definitions.reduce((html, def)=>{
|
return `<dl>${token.definitions.reduce((html, def)=>{
|
||||||
return `${html}<dt>${this.parser.parseInline(def.dt)}</dt>`
|
return `${html}<dt>${this.parser.parseInline(def.dt)}</dt>`
|
||||||
+ `<dd>${this.parser.parseInline(def.dd)}</dd>\n`;
|
+ `<dd>${this.parser.parseInline(def.dd)}</dd>`;
|
||||||
}, '')}</dl>`;
|
}, '')}</dl>`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const definitionListsMultiline = {
|
||||||
|
name : 'definitionListsMultiline',
|
||||||
|
level : 'block',
|
||||||
|
start(src) { return src.match(/^[^\n]*\n::/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
|
tokenizer(src, tokens) {
|
||||||
|
const regex = /(\n?\n?(?!::)[^\n]+?(?=\n::))|\n::(.(?:.|\n)*?(?=(?:\n::)|(?:\n\n)|$))/y;
|
||||||
|
let match;
|
||||||
|
let endIndex = 0;
|
||||||
|
const definitions = [];
|
||||||
|
while (match = regex.exec(src)) {
|
||||||
|
if(match[1]) {
|
||||||
|
definitions.push({
|
||||||
|
dt : this.lexer.inlineTokens(match[1].trim()),
|
||||||
|
dds : []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(match[2]) {
|
||||||
|
definitions[definitions.length - 1].dds.push(
|
||||||
|
this.lexer.inlineTokens(match[2].trim().replace(/\s/g, ' '))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
endIndex = regex.lastIndex;
|
||||||
|
}
|
||||||
|
if(definitions.length) {
|
||||||
|
return {
|
||||||
|
type : 'definitionListsMultiline',
|
||||||
|
raw : src.slice(0, endIndex),
|
||||||
|
definitions
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
renderer(token) {
|
||||||
|
let returnVal = `<dl>`;
|
||||||
|
token.definitions.forEach((def)=>{
|
||||||
|
const dds = def.dds.map((s)=>{
|
||||||
|
return `\n<dd>${this.parser.parseInline(s).trim()}</dd>`;
|
||||||
|
}).join('');
|
||||||
|
returnVal += `<dt>${this.parser.parseInline(def.dt)}</dt>${dds}\n`;
|
||||||
|
});
|
||||||
|
returnVal = returnVal.trim();
|
||||||
|
return `${returnVal}</dl>`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//v=====--------------------< Variable Handling >-------------------=====v// 242 lines
|
//v=====--------------------< Variable Handling >-------------------=====v// 242 lines
|
||||||
const replaceVar = function(input, hoist=false, allowUnresolved=false) {
|
const replaceVar = function(input, hoist=false, allowUnresolved=false) {
|
||||||
@@ -572,7 +615,7 @@ function MarkedVariables() {
|
|||||||
//^=====--------------------< Variable Handling >-------------------=====^//
|
//^=====--------------------< Variable Handling >-------------------=====^//
|
||||||
|
|
||||||
Marked.use(MarkedVariables());
|
Marked.use(MarkedVariables());
|
||||||
Marked.use({ extensions: [mustacheSpans, mustacheDivs, mustacheInjectInline, definitionLists, superSubScripts] });
|
Marked.use({ extensions: [mustacheSpans, mustacheDivs, mustacheInjectInline, definitionListsInline, definitionListsMultiline, superSubScripts] });
|
||||||
Marked.use(mustacheInjectBlock);
|
Marked.use(mustacheInjectBlock);
|
||||||
Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false });
|
Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false });
|
||||||
Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite());
|
Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite());
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
require('./nav.less');
|
require('client/homebrew/navbar/navbar.less');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { useState, useRef, useEffect } = React;
|
const { useState, useRef, useEffect } = React;
|
||||||
const createClass = require('create-react-class');
|
const createClass = require('create-react-class');
|
||||||
@@ -104,7 +104,7 @@ const Nav = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`navDropdownContainer ${props.className}`}
|
<div className={`navDropdownContainer ${props.className ?? ''}`}
|
||||||
ref={myRef}
|
ref={myRef}
|
||||||
onMouseEnter = { props.trigger.includes('hover') ? ()=>handleDropdown(true) : undefined }
|
onMouseEnter = { props.trigger.includes('hover') ? ()=>handleDropdown(true) : undefined }
|
||||||
onMouseLeave = { props.trigger.includes('hover') ? ()=>handleDropdown(false) : undefined }
|
onMouseLeave = { props.trigger.includes('hover') ? ()=>handleDropdown(false) : undefined }
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
79
tests/markdown/marked-extensions.test.js
Normal file
79
tests/markdown/marked-extensions.test.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/* eslint-disable max-lines */
|
||||||
|
|
||||||
|
const Markdown = require('naturalcrit/markdown.js');
|
||||||
|
|
||||||
|
describe('Inline Definition Lists', ()=>{
|
||||||
|
test('No Term 1 Definition', function() {
|
||||||
|
const source = ':: My First Definition\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt></dt><dd>My First Definition</dd></dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Single Definition Term', function() {
|
||||||
|
const source = 'My term :: My First Definition\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>My term</dt><dd>My First Definition</dd></dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Multiple Definition Terms', function() {
|
||||||
|
const source = 'Term 1::Definition of Term 1\nTerm 2::Definition of Term 2\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt><dd>Definition of Term 1</dd><dt>Term 2</dt><dd>Definition of Term 2</dd></dl>');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Multiline Definition Lists', ()=>{
|
||||||
|
test('Single Term, Single Definition', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1</dd></dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Single Term, Plural Definitions', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\n::Definition 2\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1</dd>\n<dd>Definition 2</dd></dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Multiple Term, Single Definitions', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\n\nTerm 2\n::Definition 1\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1</dd>\n<dt>Term 2</dt>\n<dd>Definition 1</dd></dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Multiple Term, Plural Definitions', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\n::Definition 2\n\nTerm 2\n::Definition 1\n::Definition 2\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1</dd>\n<dd>Definition 2</dd>\n<dt>Term 2</dt>\n<dd>Definition 1</dd>\n<dd>Definition 2</dd></dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Single Term, Single multi-line definition', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\nand more and\nmore and more\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1 and more and more and more</dd></dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Single Term, Plural multi-line definitions', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\nand more and more\n::Definition 2\nand more\nand more\n::Definition 3\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1 and more and more</dd>\n<dd>Definition 2 and more and more</dd>\n<dd>Definition 3</dd></dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Multiple Term, Single multi-line definition', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\nand more and more\n\nTerm 2\n::Definition 1\n::Definition 2\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1 and more and more</dd>\n<dt>Term 2</dt>\n<dd>Definition 1</dd>\n<dd>Definition 2</dd></dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Multiple Term, Single multi-line definition, followed by an inline dl', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\nand more and more\n\nTerm 2\n::Definition 1\n::Definition 2\n\n::Inline Definition (no term)';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1 and more and more</dd>\n<dt>Term 2</dt>\n<dd>Definition 1</dd>\n<dd>Definition 2</dd></dl><dl><dt></dt><dd>Inline Definition (no term)</dd></dl>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Multiple Term, Single multi-line definition, followed by paragraph', function() {
|
||||||
|
const source = 'Term 1\n::Definition 1\nand more and more\n\nTerm 2\n::Definition 1\n::Definition 2\n\nParagraph';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt>\n<dd>Definition 1 and more and more</dd>\n<dt>Term 2</dt>\n<dd>Definition 1</dd>\n<dd>Definition 2</dd></dl><p>Paragraph</p>');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -78,7 +78,7 @@ module.exports = function(props){
|
|||||||
|
|
||||||
return dedent`
|
return dedent`
|
||||||
{{toc,wide
|
{{toc,wide
|
||||||
# Table Of Contents
|
# Contents
|
||||||
|
|
||||||
${markdown}
|
${markdown}
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
@import (less) './themes/fonts/5e/fonts.less';
|
|
||||||
@import (less) './themes/assets/assets.less';
|
@import (less) './themes/assets/assets.less';
|
||||||
@import (less) './themes/fonts/icon fonts/font-icons.less';
|
@import (less) './themes/fonts/icon fonts/font-icons.less';
|
||||||
|
|
||||||
@@ -402,15 +401,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.pageNumber {
|
.pageNumber {
|
||||||
position : absolute;
|
|
||||||
right : 2px;
|
right : 2px;
|
||||||
bottom : 22px;
|
bottom : 22px;
|
||||||
width : 50px;
|
|
||||||
font-size : 0.9em;
|
|
||||||
color : var(--HB_Color_Footnotes);
|
color : var(--HB_Color_Footnotes);
|
||||||
text-align : center;
|
|
||||||
text-indent : 0;
|
|
||||||
&.auto::after { content : counter(phb-page-numbers); }
|
|
||||||
}
|
}
|
||||||
.footnote {
|
.footnote {
|
||||||
position : absolute;
|
position : absolute;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
@import (less) './themes/fonts/5e/fonts.less';
|
@import (less) './themes/fonts/5e/fonts.less';
|
||||||
@import (less) './themes/assets/assets.less';
|
@import (less) './themes/assets/assets.less';
|
||||||
|
@import (less) './themes/fonts/icon fonts/dicefont.less';
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
//Colors
|
//Colors
|
||||||
@@ -8,7 +9,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@page { margin : 0; }
|
@page { margin : 0; }
|
||||||
body { counter-reset : phb-page-numbers; }
|
body { counter-reset : page-numbers; }
|
||||||
* { -webkit-print-color-adjust : exact; }
|
* { -webkit-print-color-adjust : exact; }
|
||||||
|
|
||||||
//*****************************
|
//*****************************
|
||||||
@@ -47,7 +48,7 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
height : 279.4mm;
|
height : 279.4mm;
|
||||||
padding : 1.4cm 1.9cm 1.7cm;
|
padding : 1.4cm 1.9cm 1.7cm;
|
||||||
overflow : hidden;
|
overflow : hidden;
|
||||||
counter-increment : phb-page-numbers;
|
counter-increment : page-numbers;
|
||||||
background-color : var(--HB_Color_Background);
|
background-color : var(--HB_Color_Background);
|
||||||
text-rendering : optimizeLegibility;
|
text-rendering : optimizeLegibility;
|
||||||
contain : size;
|
contain : size;
|
||||||
@@ -166,7 +167,6 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
margin : 0;
|
margin : 0;
|
||||||
font-size : 120px;
|
font-size : 120px;
|
||||||
text-transform : uppercase;
|
text-transform : uppercase;
|
||||||
mix-blend-mode : overlay;
|
|
||||||
opacity : 30%;
|
opacity : 30%;
|
||||||
transform : rotate(-45deg);
|
transform : rotate(-45deg);
|
||||||
p { margin-bottom : none; }
|
p { margin-bottom : none; }
|
||||||
@@ -460,3 +460,22 @@ body { counter-reset : phb-page-numbers; }
|
|||||||
.homebreweryIcon.red { background-color : red; }
|
.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%); }
|
.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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -374,17 +374,9 @@
|
|||||||
}
|
}
|
||||||
.pageNumber{
|
.pageNumber{
|
||||||
font-family : FrederickaTheGreat;
|
font-family : FrederickaTheGreat;
|
||||||
position : absolute;
|
|
||||||
right : 3cm;
|
right : 3cm;
|
||||||
bottom : 1.25cm;
|
bottom : 1.25cm;
|
||||||
width : 50px;
|
|
||||||
font-size : 0.9em;
|
|
||||||
color : var(--HB_Color_HeaderText);
|
color : var(--HB_Color_HeaderText);
|
||||||
text-align : center;
|
|
||||||
text-indent : 0;
|
|
||||||
&.auto::after {
|
|
||||||
content : counter(phb-page-numbers);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.footnote{
|
.footnote{
|
||||||
position : absolute;
|
position : absolute;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
@import url('./dicefont.less');
|
|
||||||
|
|
||||||
/* Main Font, serif */
|
/* Main Font, serif */
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: BookInsanityRemake;
|
font-family: BookInsanityRemake;
|
||||||
|
|||||||
Reference in New Issue
Block a user