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

Merge branch 'master' into experimentalLocalStorageHistory

This commit is contained in:
Trevor Buckner
2024-09-16 01:50:54 -04:00
committed by GitHub
12 changed files with 64 additions and 65 deletions

View File

@@ -17,7 +17,7 @@ const NotificationPopup = ()=>{
<ul> <ul>
<li key='Vault'> <li key='Vault'>
<em>Search brews with our new page!</em><br /> <em>Search brews with our new page!</em><br />
We have been working very hard in making this possible, now you can share your work and look at it in the new <a href="/vault">Vault</a> page! We have been working very hard in making this possible, now you can share your work and look at it in the new <a href='/vault'>Vault</a> page!
All PUBLISHED brews will be available to anyone searching there, by title or author, and filtering by renderer. All PUBLISHED brews will be available to anyone searching there, by title or author, and filtering by renderer.
More features will be coming. More features will be coming.

View File

@@ -56,7 +56,7 @@ const ToolBar = ({ onZoomChange, currentPage, onPageChange, totalPages })=>{
} else if(mode == 'fit'){ } else if(mode == 'fit'){
// find the page with the largest single dim (height or width) so that zoom can be adapted to fit it. // find the page with the largest single dim (height or width) so that zoom can be adapted to fit it.
const minDimRatio = [...pages].reduce((minRatio, page) => Math.min(minRatio, iframeWidth / page.offsetWidth, iframeHeight / page.offsetHeight), Infinity); const minDimRatio = [...pages].reduce((minRatio, page)=>Math.min(minRatio, iframeWidth / page.offsetWidth, iframeHeight / page.offsetHeight), Infinity);
desiredZoom = minDimRatio * 100; desiredZoom = minDimRatio * 100;
} }
@@ -67,9 +67,9 @@ const ToolBar = ({ onZoomChange, currentPage, onPageChange, totalPages })=>{
return deltaZoom; return deltaZoom;
}; };
return ( return (
<div className={`toolBar ${toolsVisible ? 'visible' : 'hidden'}`}> <div className={`toolBar ${toolsVisible ? 'visible' : 'hidden'}`}>
<button className='toggleButton' title={`${toolsVisible ? 'Hide' : 'Show'} Preview Toolbar`} onClick={()=>{setToolsVisible(!toolsVisible)}}><i className='fas fa-glasses' /></button> <button className='toggleButton' title={`${toolsVisible ? 'Hide' : 'Show'} Preview Toolbar`} onClick={()=>{setToolsVisible(!toolsVisible);}}><i className='fas fa-glasses' /></button>
{/*v=====----------------------< Zoom Controls >---------------------=====v*/} {/*v=====----------------------< Zoom Controls >---------------------=====v*/}
<div className='group'> <div className='group'>
<button <button

View File

@@ -36,7 +36,7 @@ const Editor = createClass({
onStyleChange : ()=>{}, onStyleChange : ()=>{},
onMetaChange : ()=>{}, onMetaChange : ()=>{},
reportError : ()=>{}, reportError : ()=>{},
onCursorPageChange : ()=>{}, onCursorPageChange : ()=>{},
onViewPageChange : ()=>{}, onViewPageChange : ()=>{},
@@ -45,7 +45,7 @@ const Editor = createClass({
currentEditorCursorPageNum : 1, currentEditorCursorPageNum : 1,
currentEditorViewPageNum : 1, currentEditorViewPageNum : 1,
currentBrewRendererPageNum : 1, currentBrewRendererPageNum : 1,
}; };
}, },
getInitialState : function() { getInitialState : function() {
@@ -70,8 +70,8 @@ const Editor = createClass({
document.getElementById('BrewRenderer').addEventListener('keydown', this.handleControlKeys); document.getElementById('BrewRenderer').addEventListener('keydown', this.handleControlKeys);
document.addEventListener('keydown', this.handleControlKeys); document.addEventListener('keydown', this.handleControlKeys);
this.codeEditor.current.codeMirror.on('cursorActivity', (cm)=>{this.updateCurrentCursorPage(cm.getCursor())}); this.codeEditor.current.codeMirror.on('cursorActivity', (cm)=>{this.updateCurrentCursorPage(cm.getCursor());});
this.codeEditor.current.codeMirror.on('scroll', _.throttle(()=>{this.updateCurrentViewPage(this.codeEditor.current.getTopVisibleLine())}, 200)); this.codeEditor.current.codeMirror.on('scroll', _.throttle(()=>{this.updateCurrentViewPage(this.codeEditor.current.getTopVisibleLine());}, 200));
const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY); const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY);
if(editorTheme) { if(editorTheme) {
@@ -109,9 +109,9 @@ const Editor = createClass({
if(!(e.ctrlKey && e.metaKey && e.shiftKey)) return; if(!(e.ctrlKey && e.metaKey && e.shiftKey)) return;
const LEFTARROW_KEY = 37; const LEFTARROW_KEY = 37;
const RIGHTARROW_KEY = 39; const RIGHTARROW_KEY = 39;
if (e.keyCode == RIGHTARROW_KEY) this.brewJump(); if(e.keyCode == RIGHTARROW_KEY) this.brewJump();
if (e.keyCode == LEFTARROW_KEY) this.sourceJump(); if(e.keyCode == LEFTARROW_KEY) this.sourceJump();
if (e.keyCode == LEFTARROW_KEY || e.keyCode == RIGHTARROW_KEY) { if(e.keyCode == LEFTARROW_KEY || e.keyCode == RIGHTARROW_KEY) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
} }
@@ -128,14 +128,14 @@ const Editor = createClass({
updateCurrentCursorPage : function(cursor) { updateCurrentCursorPage : function(cursor) {
const lines = this.props.brew.text.split('\n').slice(0, cursor.line + 1); const lines = this.props.brew.text.split('\n').slice(0, cursor.line + 1);
const pageRegex = this.props.brew.renderer == 'V3' ? /^\\page$/ : /\\page/; const pageRegex = this.props.brew.renderer == 'V3' ? /^\\page$/ : /\\page/;
const currentPage = lines.reduce((count, line) => count + (pageRegex.test(line) ? 1 : 0), 1); const currentPage = lines.reduce((count, line)=>count + (pageRegex.test(line) ? 1 : 0), 1);
this.props.onCursorPageChange(currentPage); this.props.onCursorPageChange(currentPage);
}, },
updateCurrentViewPage : function(topScrollLine) { updateCurrentViewPage : function(topScrollLine) {
const lines = this.props.brew.text.split('\n').slice(0, topScrollLine + 1); const lines = this.props.brew.text.split('\n').slice(0, topScrollLine + 1);
const pageRegex = this.props.brew.renderer == 'V3' ? /^\\page$/ : /\\page/; const pageRegex = this.props.brew.renderer == 'V3' ? /^\\page$/ : /\\page/;
const currentPage = lines.reduce((count, line) => count + (pageRegex.test(line) ? 1 : 0), 1); const currentPage = lines.reduce((count, line)=>count + (pageRegex.test(line) ? 1 : 0), 1);
this.props.onViewPageChange(currentPage); this.props.onViewPageChange(currentPage);
}, },
@@ -322,10 +322,10 @@ const Editor = createClass({
const currentPos = brewRenderer.scrollTop; const currentPos = brewRenderer.scrollTop;
const targetPos = window.frames['BrewRenderer'].contentDocument.getElementById(`p${targetPage}`).getBoundingClientRect().top; const targetPos = window.frames['BrewRenderer'].contentDocument.getElementById(`p${targetPage}`).getBoundingClientRect().top;
const checkIfScrollComplete = () => { const checkIfScrollComplete = ()=>{
let scrollingTimeout; let scrollingTimeout;
clearTimeout(scrollingTimeout); // Reset the timer every time a scroll event occurs clearTimeout(scrollingTimeout); // Reset the timer every time a scroll event occurs
scrollingTimeout = setTimeout(() => { scrollingTimeout = setTimeout(()=>{
isJumping = false; isJumping = false;
brewRenderer.removeEventListener('scroll', checkIfScrollComplete); brewRenderer.removeEventListener('scroll', checkIfScrollComplete);
}, 150); // If 150 ms pass without a brewRenderer scroll event, assume scrolling is done }, 150); // If 150 ms pass without a brewRenderer scroll event, assume scrolling is done
@@ -364,16 +364,16 @@ const Editor = createClass({
let currentY = this.codeEditor.current.codeMirror.getScrollInfo().top; let currentY = this.codeEditor.current.codeMirror.getScrollInfo().top;
let targetY = this.codeEditor.current.codeMirror.heightAtLine(targetLine, 'local', true); let targetY = this.codeEditor.current.codeMirror.heightAtLine(targetLine, 'local', true);
const checkIfScrollComplete = () => { const checkIfScrollComplete = ()=>{
let scrollingTimeout; let scrollingTimeout;
clearTimeout(scrollingTimeout); // Reset the timer every time a scroll event occurs clearTimeout(scrollingTimeout); // Reset the timer every time a scroll event occurs
scrollingTimeout = setTimeout(() => { scrollingTimeout = setTimeout(()=>{
isJumping = false; isJumping = false;
this.codeEditor.current.codeMirror.off('scroll', checkIfScrollComplete); this.codeEditor.current.codeMirror.off('scroll', checkIfScrollComplete);
}, 150); // If 150 ms pass without a scroll event, assume scrolling is done }, 150); // If 150 ms pass without a scroll event, assume scrolling is done
}; };
isJumping = true; isJumping = true;
checkIfScrollComplete(); checkIfScrollComplete();
this.codeEditor.current.codeMirror.on('scroll', checkIfScrollComplete); this.codeEditor.current.codeMirror.on('scroll', checkIfScrollComplete);

View File

@@ -111,7 +111,7 @@ const ErrorNavItem = createClass({
Looks like there was a problem retreiving Looks like there was a problem retreiving
the theme, or a theme that it inherits, the theme, or a theme that it inherits,
for this brew. Verify that brew <a className='lowercase' target='_blank' rel='noopener noreferrer' href={`/share/${response.body.brewId}`}> for this brew. Verify that brew <a className='lowercase' target='_blank' rel='noopener noreferrer' href={`/share/${response.body.brewId}`}>
{response.body.brewId}</a> still exists! {response.body.brewId}</a> still exists!
</div> </div>
</Nav.item>; </Nav.item>;
} }

View File

@@ -330,7 +330,7 @@ const VaultPage = (props)=>{
if(error) { if(error) {
const errorText = ErrorIndex()[error.HBErrorCode.toString()] || ''; const errorText = ErrorIndex()[error.HBErrorCode.toString()] || '';
return ( return (
<div className='foundBrews noBrews'> <div className='foundBrews noBrews'>
<h3>Error: {errorText}</h3> <h3>Error: {errorText}</h3>

11
package-lock.json generated
View File

@@ -25,7 +25,7 @@
"expr-eval": "^2.0.2", "expr-eval": "^2.0.2",
"express": "^4.21.0", "express": "^4.21.0",
"express-async-handler": "^1.2.0", "express-async-handler": "^1.2.0",
"express-static-gzip": "2.1.7", "express-static-gzip": "2.1.8",
"fs-extra": "11.2.0", "fs-extra": "11.2.0",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"jwt-simple": "^0.5.6", "jwt-simple": "^0.5.6",
@@ -6362,12 +6362,11 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/express-static-gzip": { "node_modules/express-static-gzip": {
"version": "2.1.7", "version": "2.1.8",
"resolved": "https://registry.npmjs.org/express-static-gzip/-/express-static-gzip-2.1.7.tgz", "resolved": "https://registry.npmjs.org/express-static-gzip/-/express-static-gzip-2.1.8.tgz",
"integrity": "sha512-QOCZUC+lhPPCjIJKpQGu1Oa61Axg9Mq09Qvit8Of7kzpMuwDeMSqjjQteQS3OVw/GkENBoSBheuQDWPlngImvw==", "integrity": "sha512-g8tiJuI9Y9Ffy59ehVXvqb0hhP83JwZiLxzanobPaMbkB5qBWA8nuVgd+rcd5qzH3GkgogTALlc0BaADYwnMbQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"serve-static": "^1.14.1" "serve-static": "^1.16.2"
} }
}, },
"node_modules/express/node_modules/cookie": { "node_modules/express/node_modules/cookie": {

View File

@@ -100,7 +100,7 @@
"expr-eval": "^2.0.2", "expr-eval": "^2.0.2",
"express": "^4.21.0", "express": "^4.21.0",
"express-async-handler": "^1.2.0", "express-async-handler": "^1.2.0",
"express-static-gzip": "2.1.7", "express-static-gzip": "2.1.8",
"fs-extra": "11.2.0", "fs-extra": "11.2.0",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"jwt-simple": "^0.5.6", "jwt-simple": "^0.5.6",

View File

@@ -934,7 +934,7 @@ brew`);
expect(req.brew).toEqual(testBrew); expect(req.brew).toEqual(testBrew);
expect(req.brew).toHaveProperty('style', '\nI Have a style!\n'); expect(req.brew).toHaveProperty('style', '\nI Have a style!\n');
expect(res.status).toHaveBeenCalledWith(200); expect(res.status).toHaveBeenCalledWith(200);
expect(res.send).toHaveBeenCalledWith("\nI Have a style!\n"); expect(res.send).toHaveBeenCalledWith('\nI Have a style!\n');
expect(res.set).toHaveBeenCalledWith({ expect(res.set).toHaveBeenCalledWith({
'Cache-Control' : 'no-cache', 'Cache-Control' : 'no-cache',
'Content-Type' : 'text/css' 'Content-Type' : 'text/css'

View File

@@ -49,12 +49,12 @@ const CodeEditor = createClass({
displayName : 'CodeEditor', displayName : 'CodeEditor',
getDefaultProps : function() { getDefaultProps : function() {
return { return {
language : '', language : '',
value : '', value : '',
wrap : true, wrap : true,
onChange : ()=>{}, onChange : ()=>{},
enableFolding : true, enableFolding : true,
editorTheme : 'default' editorTheme : 'default'
}; };
}, },
@@ -189,7 +189,7 @@ const CodeEditor = createClass({
autoCompleteEmoji.showAutocompleteEmoji(CodeMirror, this.codeMirror); autoCompleteEmoji.showAutocompleteEmoji(CodeMirror, this.codeMirror);
// Note: codeMirror passes a copy of itself in this callback. cm === this.codeMirror. Either one works. // Note: codeMirror passes a copy of itself in this callback. cm === this.codeMirror. Either one works.
this.codeMirror.on('change', (cm)=>{this.props.onChange(cm.getValue())}); this.codeMirror.on('change', (cm)=>{this.props.onChange(cm.getValue());});
this.updateSize(); this.updateSize();
}, },
@@ -399,7 +399,7 @@ const CodeEditor = createClass({
}, },
getTopVisibleLine : function(){ getTopVisibleLine : function(){
const rect = this.codeMirror.getWrapperElement().getBoundingClientRect(); const rect = this.codeMirror.getWrapperElement().getBoundingClientRect();
const topVisibleLine = this.codeMirror.lineAtHeight(rect.top, "window"); const topVisibleLine = this.codeMirror.lineAtHeight(rect.top, 'window');
return topVisibleLine; return topVisibleLine;
}, },
updateSize : function(){ updateSize : function(){

View File

@@ -105,16 +105,16 @@ renderer.link = function (href, title, text) {
// Expose `src` attribute as `--HB_src` to make the URL accessible via CSS // Expose `src` attribute as `--HB_src` to make the URL accessible via CSS
renderer.image = function (href, title, text) { renderer.image = function (href, title, text) {
href = cleanUrl(href); href = cleanUrl(href);
if (href === null) if(href === null)
return text; return text;
let out = `<img src="${href}" alt="${text}" style="--HB_src:url(${href});"`; let out = `<img src="${href}" alt="${text}" style="--HB_src:url(${href});"`;
if (title) if(title)
out += ` title="${title}"`; out += ` title="${title}"`;
out += '>'; out += '>';
return out; return out;
} };
// Disable default reflink behavior, as it steps on our variables extension // Disable default reflink behavior, as it steps on our variables extension
tokenizer.def = function () { tokenizer.def = function () {
@@ -745,7 +745,7 @@ const tableTerminators = [
`:+\\n`, // hardBreak `:+\\n`, // hardBreak
` *{[^\n]+}`, // blockInjector ` *{[^\n]+}`, // blockInjector
` *{{[^{\n]*\n.*?\n}}` // mustacheDiv ` *{{[^{\n]*\n.*?\n}}` // mustacheDiv
] ];
Marked.use(MarkedVariables()); Marked.use(MarkedVariables());
Marked.use({ extensions : [definitionListsMultiLine, definitionListsSingleLine, forcedParagraphBreaks, superSubScripts, Marked.use({ extensions : [definitionListsMultiLine, definitionListsSingleLine, forcedParagraphBreaks, superSubScripts,
@@ -755,12 +755,12 @@ Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false });
Marked.use(MarkedExtendedTables(tableTerminators), MarkedGFMHeadingId({ globalSlugs: true }), MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions)); Marked.use(MarkedExtendedTables(tableTerminators), MarkedGFMHeadingId({ globalSlugs: true }), MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions));
function cleanUrl(href) { function cleanUrl(href) {
try { try {
href = encodeURI(href).replace(/%25/g, '%'); href = encodeURI(href).replace(/%25/g, '%');
} catch { } catch {
return null; return null;
} }
return href; return href;
} }
const escapeTest = /[&<>"']/; const escapeTest = /[&<>"']/;

View File

@@ -15,12 +15,12 @@ const SplitPane = createClass({
getInitialState : function() { getInitialState : function() {
return { return {
currentDividerPos : null, currentDividerPos : null,
windowWidth : 0, windowWidth : 0,
isDragging : false, isDragging : false,
moveSource : false, moveSource : false,
moveBrew : false, moveBrew : false,
showMoveArrows : true showMoveArrows : true
}; };
}, },
@@ -45,7 +45,7 @@ const SplitPane = createClass({
// This lives here instead of in the initial render because you cannot touch localStorage until the componant mounts. // This lives here instead of in the initial render because you cannot touch localStorage until the componant mounts.
const loadLiveScroll = window.localStorage.getItem('liveScroll') === 'true'; const loadLiveScroll = window.localStorage.getItem('liveScroll') === 'true';
this.setState({ liveScroll : loadLiveScroll }); this.setState({ liveScroll: loadLiveScroll });
}, },
componentWillUnmount : function() { componentWillUnmount : function() {
@@ -130,7 +130,7 @@ const SplitPane = createClass({
<i className='fas fa-arrow-right' /> <i className='fas fa-arrow-right' />
</div> </div>
<div id='scrollToggleDiv' className={this.state.liveScroll ? 'arrow lock' : 'arrow unlock'} <div id='scrollToggleDiv' className={this.state.liveScroll ? 'arrow lock' : 'arrow unlock'}
style={{ left: this.state.currentDividerPos-4 }} style={{ left: this.state.currentDividerPos-4 }}
onClick={this.liveScrollToggle} > onClick={this.liveScrollToggle} >
<i id='scrollToggle' className={this.state.liveScroll ? 'fas fa-lock' : 'fas fa-unlock'} /> <i id='scrollToggle' className={this.state.liveScroll ? 'fas fa-lock' : 'fas fa-unlock'} />
</div> </div>
@@ -160,7 +160,7 @@ const SplitPane = createClass({
...(this.props.showDividerButtons && { ...(this.props.showDividerButtons && {
moveBrew : this.state.moveBrew, moveBrew : this.state.moveBrew,
moveSource : this.state.moveSource, moveSource : this.state.moveSource,
liveScroll : this.state.liveScroll, liveScroll : this.state.liveScroll,
setMoveArrows : this.setMoveArrows, setMoveArrows : this.setMoveArrows,
}), }),
})} })}

View File

@@ -4,9 +4,9 @@ const dedent = require('dedent-tabs').default;
const mapPages = (pages)=>{ const mapPages = (pages)=>{
let actualPage = 0; let actualPage = 0;
let mappedPage = 0; // Number displayed in footer let mappedPage = 0; // Number displayed in footer
let pageMap = []; const pageMap = [];
pages.forEach(page => { pages.forEach((page)=>{
actualPage++; actualPage++;
const doSkip = page.querySelector('.skipCounting'); const doSkip = page.querySelector('.skipCounting');
const doReset = page.querySelector('.resetCounting'); const doReset = page.querySelector('.resetCounting');
@@ -24,13 +24,13 @@ const mapPages = (pages)=>{
return pageMap; return pageMap;
}; };
const getMarkdown = (headings, pageMap) => { const getMarkdown = (headings, pageMap)=>{
const levelPad = ['- ###', ' - ####', ' -', ' -', ' -', ' -']; const levelPad = ['- ###', ' - ####', ' -', ' -', ' -', ' -'];
let allMarkdown = [];
let depthChain = [0];
headings.forEach(heading => { const allMarkdown = [];
const depthChain = [0];
headings.forEach((heading)=>{
const page = parseInt(heading.closest('.page').id?.replace(/^p/, '')); const page = parseInt(heading.closest('.page').id?.replace(/^p/, ''));
const mappedPage = pageMap[page].mappedPage; const mappedPage = pageMap[page].mappedPage;
const showPage = pageMap[page].showPage; const showPage = pageMap[page].showPage;
@@ -42,14 +42,14 @@ const getMarkdown = (headings, pageMap) => {
return; return;
//If different header depth than last, remove indents until nearest higher-level header, then indent once //If different header depth than last, remove indents until nearest higher-level header, then indent once
if (depth !== depthChain[depthChain.length -1]) { if(depth !== depthChain[depthChain.length -1]) {
while (depth <= depthChain[depthChain.length - 1]) { while (depth <= depthChain[depthChain.length - 1]) {
depthChain.pop(); depthChain.pop();
} }
depthChain.push(depth); depthChain.push(depth);
} }
let markdown = `${levelPad[depthChain.length - 2]} [{{ ${title}}}{{ ${mappedPage}}}](#p${page})`; const markdown = `${levelPad[depthChain.length - 2]} [{{ ${title}}}{{ ${mappedPage}}}](#p${page})`;
allMarkdown.push(markdown); allMarkdown.push(markdown);
}); });
return allMarkdown.join('\n'); return allMarkdown.join('\n');