mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2025-12-24 18:32:41 +00:00
Merge branch 'master' into Issue_1958
This commit is contained in:
36
.github/pull_request_template.md
vendored
Normal file
36
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<!--
|
||||
Before submitting a Pull Request, please consider the following to speed up reviews:
|
||||
- 👷♀️ Create small PRs. Large PRs can usually be broken down into incremental PRs.
|
||||
- 🚩 Do you already have several open PRs? Consider finishing or asking for help with existing PRs first.
|
||||
- 🔧 Does your PR reference a discussed and approved issue, especially for personal or edge-case requests?
|
||||
- 💡 Is the solution agreed upon? Save rework time by discussing strategy before coding.
|
||||
-->
|
||||
|
||||
## Description
|
||||
|
||||
|
||||
## Related Issues or Discussions
|
||||
|
||||
- Closes #
|
||||
|
||||
## QA Instructions, Screenshots, Recordings
|
||||
|
||||
_Please replace this line with instructions on how to test or view your changes, as well as any before/after
|
||||
images for UI changes._
|
||||
|
||||
### Reviewer Checklist
|
||||
|
||||
_Please replace the list below with specific features you want reviewers to look at._
|
||||
|
||||
*Reviewers, refer to this list when testing features, or suggest new items *
|
||||
- [ ] Verify new features are functional
|
||||
- [ ] Feature A does X
|
||||
- [ ] Feature B does Y
|
||||
- [ ] Verify old features have not broken
|
||||
- [ ] Feature Z can still be used
|
||||
- [ ] Test for edge cases / try to break things
|
||||
- [ ] Feature A handles negative numbers
|
||||
- [ ] Identify opportunities for simplification and refactoring
|
||||
- [ ] Check for code legibility and appropriate comments
|
||||
|
||||
<details><summary>Copy this list</summary>
|
||||
@@ -1,7 +1,7 @@
|
||||
/*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/
|
||||
require('./brewRenderer.less');
|
||||
const React = require('react');
|
||||
const { useState, useRef, useEffect } = React;
|
||||
const { useState, useRef, useEffect, useCallback } = React;
|
||||
const _ = require('lodash');
|
||||
|
||||
const MarkdownLegacy = require('naturalcrit/markdownLegacy.js');
|
||||
@@ -49,23 +49,25 @@ let rawPages = [];
|
||||
|
||||
const BrewRenderer = (props)=>{
|
||||
props = {
|
||||
text : '',
|
||||
style : '',
|
||||
renderer : 'legacy',
|
||||
theme : '5ePHB',
|
||||
lang : '',
|
||||
errors : [],
|
||||
currentEditorPage : 0,
|
||||
themeBundle : {},
|
||||
text : '',
|
||||
style : '',
|
||||
renderer : 'legacy',
|
||||
theme : '5ePHB',
|
||||
lang : '',
|
||||
errors : [],
|
||||
currentEditorCursorPageNum : 0,
|
||||
currentEditorViewPageNum : 0,
|
||||
currentBrewRendererPageNum : 0,
|
||||
themeBundle : {},
|
||||
onPageChange : ()=>{},
|
||||
...props
|
||||
};
|
||||
|
||||
const [state, setState] = useState({
|
||||
height : PAGE_HEIGHT,
|
||||
isMounted : false,
|
||||
visibility : 'hidden',
|
||||
zoom : 100,
|
||||
currentPageNumber : 1,
|
||||
height : PAGE_HEIGHT,
|
||||
isMounted : false,
|
||||
visibility : 'hidden',
|
||||
zoom : 100
|
||||
});
|
||||
|
||||
const mainRef = useRef(null);
|
||||
@@ -87,25 +89,22 @@ const BrewRenderer = (props)=>{
|
||||
}));
|
||||
};
|
||||
|
||||
const getCurrentPage = (e)=>{
|
||||
const updateCurrentPage = useCallback(_.throttle((e)=>{
|
||||
const { scrollTop, clientHeight, scrollHeight } = e.target;
|
||||
const totalScrollableHeight = scrollHeight - clientHeight;
|
||||
const currentPageNumber = Math.ceil((scrollTop / totalScrollableHeight) * rawPages.length);
|
||||
const currentPageNumber = Math.ceil(((scrollTop + 1) / totalScrollableHeight) * rawPages.length);
|
||||
|
||||
setState((prevState)=>({
|
||||
...prevState,
|
||||
currentPageNumber : currentPageNumber || 1
|
||||
}));
|
||||
};
|
||||
props.onPageChange(currentPageNumber);
|
||||
}, 200), []);
|
||||
|
||||
const isInView = (index)=>{
|
||||
if(!state.isMounted)
|
||||
return false;
|
||||
|
||||
if(index == props.currentEditorPage) //Already rendered before this step
|
||||
if(index == props.currentEditorCursorPageNum - 1) //Already rendered before this step
|
||||
return false;
|
||||
|
||||
if(Math.abs(index - state.currentPageNumber) <= 3)
|
||||
if(Math.abs(index - props.currentBrewRendererPageNum - 1) <= 3)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -142,7 +141,7 @@ const BrewRenderer = (props)=>{
|
||||
renderedPages.length = 0;
|
||||
|
||||
// Render currently-edited page first so cross-page effects (variables, links) can propagate out first
|
||||
renderedPages[props.currentEditorPage] = renderPage(rawPages[props.currentEditorPage], props.currentEditorPage);
|
||||
renderedPages[props.currentEditorCursorPageNum - 1] = renderPage(rawPages[props.currentEditorCursorPageNum - 1], props.currentEditorCursorPageNum - 1);
|
||||
|
||||
_.forEach(rawPages, (page, index)=>{
|
||||
if((isInView(index) || !renderedPages[index]) && typeof window !== 'undefined'){
|
||||
@@ -192,7 +191,7 @@ const BrewRenderer = (props)=>{
|
||||
<>
|
||||
{/*render dummy page while iFrame is mounting.*/}
|
||||
{!state.isMounted
|
||||
? <div className='brewRenderer' onScroll={getCurrentPage}>
|
||||
? <div className='brewRenderer' onScroll={updateCurrentPage}>
|
||||
<div className='pages'>
|
||||
{renderDummyPage(1)}
|
||||
</div>
|
||||
@@ -205,7 +204,7 @@ const BrewRenderer = (props)=>{
|
||||
<NotificationPopup />
|
||||
</div>
|
||||
|
||||
<ToolBar onZoomChange={handleZoom} currentPage={state.currentPageNumber} totalPages={rawPages.length}/>
|
||||
<ToolBar onZoomChange={handleZoom} currentPage={props.currentBrewRendererPageNum} totalPages={rawPages.length}/>
|
||||
|
||||
{/*render in iFrame so broken code doesn't crash the site.*/}
|
||||
<Frame id='BrewRenderer' initialContent={INITIAL_CONTENT}
|
||||
@@ -214,7 +213,7 @@ const BrewRenderer = (props)=>{
|
||||
onClick={()=>{emitClick();}}
|
||||
>
|
||||
<div className={'brewRenderer'}
|
||||
onScroll={getCurrentPage}
|
||||
onScroll={updateCurrentPage}
|
||||
onKeyDown={handleControlKeys}
|
||||
tabIndex={-1}
|
||||
style={{ height: state.height }}>
|
||||
|
||||
@@ -11,6 +11,7 @@ const ToolBar = ({ onZoomChange, currentPage, onPageChange, totalPages })=>{
|
||||
|
||||
const [zoomLevel, setZoomLevel] = useState(100);
|
||||
const [pageNum, setPageNum] = useState(currentPage);
|
||||
const [toolsVisible, setToolsVisible] = useState(true);
|
||||
|
||||
useEffect(()=>{
|
||||
onZoomChange(zoomLevel);
|
||||
@@ -66,8 +67,9 @@ const ToolBar = ({ onZoomChange, currentPage, onPageChange, totalPages })=>{
|
||||
return deltaZoom;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='toolBar'>
|
||||
return (
|
||||
<div className={`toolBar ${toolsVisible ? 'visible' : 'hidden'}`}>
|
||||
<button className='toggleButton' title={`${toolsVisible ? 'Hide' : 'Show'} Preview Toolbar`} onClick={()=>{setToolsVisible(!toolsVisible)}}><i className='fas fa-glasses' /></button>
|
||||
{/*v=====----------------------< Zoom Controls >---------------------=====v*/}
|
||||
<div className='group'>
|
||||
<button
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
font-family : 'Open Sans', sans-serif;
|
||||
color : #CCCCCC;
|
||||
background-color : #555555;
|
||||
& > *:not(.toggleButton) {
|
||||
opacity: 1;
|
||||
transition: all .2s ease;
|
||||
}
|
||||
|
||||
.group {
|
||||
box-sizing : border-box;
|
||||
@@ -100,4 +104,25 @@
|
||||
font-size:1.2em;
|
||||
}
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
width: 32px;
|
||||
transition: all .3s ease;
|
||||
flex-wrap:nowrap;
|
||||
overflow: hidden;
|
||||
background-color: unset;
|
||||
opacity: .5;
|
||||
& > *:not(.toggleButton) {
|
||||
opacity: 0;
|
||||
transition: all .2s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button.toggleButton {
|
||||
z-index : 5;
|
||||
position:absolute;
|
||||
left: 0;
|
||||
width: 32px;
|
||||
min-width: unset;
|
||||
}
|
||||
@@ -36,9 +36,16 @@ const Editor = createClass({
|
||||
onStyleChange : ()=>{},
|
||||
onMetaChange : ()=>{},
|
||||
reportError : ()=>{},
|
||||
|
||||
onCursorPageChange : ()=>{},
|
||||
onViewPageChange : ()=>{},
|
||||
|
||||
editorTheme : 'default',
|
||||
renderer : 'legacy'
|
||||
renderer : 'legacy',
|
||||
|
||||
currentEditorCursorPageNum : 0,
|
||||
currentEditorViewPageNum : 0,
|
||||
currentBrewRendererPageNum : 0,
|
||||
};
|
||||
},
|
||||
getInitialState : function() {
|
||||
@@ -62,6 +69,9 @@ const Editor = createClass({
|
||||
document.getElementById('BrewRenderer').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('scroll', _.throttle(()=>{this.updateCurrentViewPage(this.codeEditor.current.getTopVisibleLine())}, 200));
|
||||
|
||||
const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY);
|
||||
if(editorTheme) {
|
||||
this.setState({
|
||||
@@ -96,7 +106,6 @@ const Editor = createClass({
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
updateEditorSize : function() {
|
||||
if(this.codeEditor.current) {
|
||||
let paneHeight = this.editor.current.parentNode.clientHeight;
|
||||
@@ -105,6 +114,20 @@ const Editor = createClass({
|
||||
}
|
||||
},
|
||||
|
||||
updateCurrentCursorPage : function(cursor) {
|
||||
const lines = this.props.brew.text.split('\n').slice(0, cursor.line + 1);
|
||||
const pageRegex = this.props.brew.renderer == 'V3' ? /^\\page$/ : /\\page/;
|
||||
const currentPage = lines.reduce((count, line) => count + (pageRegex.test(line) ? 1 : 0), 1);
|
||||
this.props.onCursorPageChange(currentPage);
|
||||
},
|
||||
|
||||
updateCurrentViewPage : function(topScrollLine) {
|
||||
const lines = this.props.brew.text.split('\n').slice(0, topScrollLine + 1);
|
||||
const pageRegex = this.props.brew.renderer == 'V3' ? /^\\page$/ : /\\page/;
|
||||
const currentPage = lines.reduce((count, line) => count + (pageRegex.test(line) ? 1 : 0), 1);
|
||||
this.props.onViewPageChange(currentPage);
|
||||
},
|
||||
|
||||
handleInject : function(injectText){
|
||||
this.codeEditor.current?.injectText(injectText, false);
|
||||
},
|
||||
@@ -119,18 +142,6 @@ const Editor = createClass({
|
||||
}); //TODO: not sure if updateeditorsize needed
|
||||
},
|
||||
|
||||
getCurrentPage : function(){
|
||||
const lines = this.props.brew.text.split('\n').slice(0, this.codeEditor.current.getCursorPosition().line + 1);
|
||||
return _.reduce(lines, (r, line)=>{
|
||||
if(
|
||||
(this.props.renderer == 'legacy' && line.indexOf('\\page') !== -1)
|
||||
||
|
||||
(this.props.renderer == 'V3' && line.match(/^\\page$/))
|
||||
) r++;
|
||||
return r;
|
||||
}, 1);
|
||||
},
|
||||
|
||||
highlightCustomMarkdown : function(){
|
||||
if(!this.codeEditor.current) return;
|
||||
if(this.state.view === 'text') {
|
||||
@@ -139,7 +150,7 @@ const Editor = createClass({
|
||||
codeMirror.operation(()=>{ // Batch CodeMirror styling
|
||||
|
||||
const foldLines = [];
|
||||
|
||||
|
||||
//reset custom text styles
|
||||
const customHighlights = codeMirror.getAllMarks().filter((mark)=>{
|
||||
// Record details of folded sections
|
||||
@@ -291,9 +302,9 @@ const Editor = createClass({
|
||||
}
|
||||
},
|
||||
|
||||
brewJump : function(targetPage=this.getCurrentPage()){
|
||||
brewJump : function(targetPage=this.props.currentEditorCursorPageNum){
|
||||
if(!window) return;
|
||||
// console.log(`Scroll to: p${targetPage}`);
|
||||
// Get current brewRenderer scroll position and calculate target position
|
||||
const brewRenderer = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer')[0];
|
||||
const currentPos = brewRenderer.scrollTop;
|
||||
const targetPos = window.frames['BrewRenderer'].contentDocument.getElementById(`p${targetPage}`).getBoundingClientRect().top;
|
||||
@@ -321,19 +332,8 @@ const Editor = createClass({
|
||||
if(targetLine == null) {
|
||||
targetLine = 0;
|
||||
|
||||
const pageCollection = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('page');
|
||||
const brewRendererHeight = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer').item(0).getBoundingClientRect().height;
|
||||
|
||||
let currentPage = 1;
|
||||
for (const page of pageCollection) {
|
||||
if(page.getBoundingClientRect().bottom > (brewRendererHeight / 2)) {
|
||||
currentPage = parseInt(page.id.slice(1)) || 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const textSplit = this.props.renderer == 'V3' ? /^\\page$/gm : /\\page/;
|
||||
const textString = this.props.brew.text.split(textSplit).slice(0, currentPage-1).join(textSplit);
|
||||
const textString = this.props.brew.text.split(textSplit).slice(0, this.props.currentBrewRendererPageNum-1).join(textSplit);
|
||||
const textPosition = textString.length;
|
||||
const lineCount = textString.match('\n') ? textString.slice(0, textPosition).split('\n').length : 0;
|
||||
|
||||
@@ -389,6 +389,8 @@ const Editor = createClass({
|
||||
view={this.state.view}
|
||||
value={this.props.brew.text}
|
||||
onChange={this.props.onTextChange}
|
||||
onCursorActivity={this.props.onCursorActivity}
|
||||
onScroll={this.props.onPageChange}
|
||||
editorTheme={this.state.editorTheme}
|
||||
rerenderParent={this.rerenderParent} />
|
||||
</>;
|
||||
|
||||
@@ -70,7 +70,7 @@ const Snippetbar = createClass({
|
||||
mergeCustomizer : function(oldValue, newValue, key) {
|
||||
if(key == 'snippets') {
|
||||
const result = _.reverse(_.unionBy(_.reverse(newValue), _.reverse(oldValue), 'name')); // Join snippets together, with preference for the child theme over the parent theme
|
||||
return _.filter(result, 'gen'); //Only keep snippets with a 'gen' property.
|
||||
return result.filter(snip => snip.gen || snip.subsnippets);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/* eslint-disable max-lines */
|
||||
require('./editPage.less');
|
||||
const React = require('react');
|
||||
const createClass = require('create-react-class');
|
||||
const _ = require('lodash');
|
||||
const createClass = require('create-react-class');
|
||||
|
||||
const request = require('../../utils/request-middleware.js');
|
||||
const { Meta } = require('vitreum/headtags');
|
||||
|
||||
@@ -41,22 +42,24 @@ const EditPage = createClass({
|
||||
|
||||
getInitialState : function() {
|
||||
return {
|
||||
brew : this.props.brew,
|
||||
isSaving : false,
|
||||
isPending : false,
|
||||
alertTrashedGoogleBrew : this.props.brew.trashed,
|
||||
alertLoginToTransfer : false,
|
||||
saveGoogle : this.props.brew.googleId ? true : false,
|
||||
confirmGoogleTransfer : false,
|
||||
error : null,
|
||||
htmlErrors : Markdown.validate(this.props.brew.text),
|
||||
url : '',
|
||||
autoSave : true,
|
||||
autoSaveWarning : false,
|
||||
unsavedTime : new Date(),
|
||||
currentEditorPage : 0,
|
||||
displayLockMessage : this.props.brew.lock || false,
|
||||
themeBundle : {}
|
||||
brew : this.props.brew,
|
||||
isSaving : false,
|
||||
isPending : false,
|
||||
alertTrashedGoogleBrew : this.props.brew.trashed,
|
||||
alertLoginToTransfer : false,
|
||||
saveGoogle : this.props.brew.googleId ? true : false,
|
||||
confirmGoogleTransfer : false,
|
||||
error : null,
|
||||
htmlErrors : Markdown.validate(this.props.brew.text),
|
||||
url : '',
|
||||
autoSave : true,
|
||||
autoSaveWarning : false,
|
||||
unsavedTime : new Date(),
|
||||
currentEditorViewPageNum : 0,
|
||||
currentEditorCursorPageNum : 0,
|
||||
currentBrewRendererPageNum : 0,
|
||||
displayLockMessage : this.props.brew.lock || false,
|
||||
themeBundle : {}
|
||||
};
|
||||
},
|
||||
|
||||
@@ -113,16 +116,31 @@ const EditPage = createClass({
|
||||
this.editor.current.update();
|
||||
},
|
||||
|
||||
handleEditorViewPageChange : function(pageNumber){
|
||||
console.log(`editor view : ${pageNumber}`);
|
||||
this.setState({ currentEditorViewPageNum: pageNumber });
|
||||
},
|
||||
|
||||
handleEditorCursorPageChange : function(pageNumber){
|
||||
console.log(`editor cursor : ${pageNumber}`);
|
||||
this.setState({ currentEditorCursorPageNum: pageNumber });
|
||||
},
|
||||
|
||||
handleBrewRendererPageChange : function(pageNumber){
|
||||
console.log(`brewRenderer view : ${pageNumber}`);
|
||||
this.setState({ currentBrewRendererPageNum: pageNumber });
|
||||
},
|
||||
|
||||
handleTextChange : function(text){
|
||||
//If there are errors, run the validator on every change to give quick feedback
|
||||
console.log('text change');
|
||||
let htmlErrors = this.state.htmlErrors;
|
||||
if(htmlErrors.length) htmlErrors = Markdown.validate(text);
|
||||
|
||||
this.setState((prevState)=>({
|
||||
brew : { ...prevState.brew, text: text },
|
||||
isPending : true,
|
||||
htmlErrors : htmlErrors,
|
||||
currentEditorPage : this.editor.current.getCurrentPage() - 1 //Offset index since Marked starts pages at 0
|
||||
brew : { ...prevState.brew, text: text },
|
||||
isPending : true,
|
||||
htmlErrors : htmlErrors,
|
||||
}), ()=>{if(this.state.autoSave) this.trySave();});
|
||||
},
|
||||
|
||||
@@ -413,6 +431,11 @@ const EditPage = createClass({
|
||||
renderer={this.state.brew.renderer}
|
||||
userThemes={this.props.userThemes}
|
||||
snippetBundle={this.state.themeBundle.snippets}
|
||||
onCursorPageChange={this.handleEditorCursorPageChange}
|
||||
onViewPageChange={this.handleEditorViewPageChange}
|
||||
currentEditorViewPageNum={this.state.currentEditorViewPageNum}
|
||||
currentEditorCursorPageNum={this.state.currentEditorCursorPageNum}
|
||||
currentBrewRendererPageNum={this.state.currentBrewRendererPageNum}
|
||||
/>
|
||||
<BrewRenderer
|
||||
text={this.state.brew.text}
|
||||
@@ -422,7 +445,10 @@ const EditPage = createClass({
|
||||
themeBundle={this.state.themeBundle}
|
||||
errors={this.state.htmlErrors}
|
||||
lang={this.state.brew.lang}
|
||||
currentEditorPage={this.state.currentEditorPage}
|
||||
onPageChange={this.handleBrewRendererPageChange}
|
||||
currentEditorViewPageNum={this.state.currentEditorViewPageNum}
|
||||
currentEditorCursorPageNum={this.state.currentEditorCursorPageNum}
|
||||
currentBrewRendererPageNum={this.state.currentBrewRendererPageNum}
|
||||
allowPrint={true}
|
||||
/>
|
||||
</SplitPane>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
require('./homePage.less');
|
||||
const React = require('react');
|
||||
const createClass = require('create-react-class');
|
||||
const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
const request = require('../../utils/request-middleware.js');
|
||||
const { Meta } = require('vitreum/headtags');
|
||||
@@ -32,11 +31,13 @@ const HomePage = createClass({
|
||||
},
|
||||
getInitialState : function() {
|
||||
return {
|
||||
brew : this.props.brew,
|
||||
welcomeText : this.props.brew.text,
|
||||
error : undefined,
|
||||
currentEditorPage : 0,
|
||||
themeBundle : {}
|
||||
brew : this.props.brew,
|
||||
welcomeText : this.props.brew.text,
|
||||
error : undefined,
|
||||
currentEditorViewPageNum : 0,
|
||||
currentEditorCursorPageNum : 0,
|
||||
currentBrewRendererPageNum : 0,
|
||||
themeBundle : {}
|
||||
};
|
||||
},
|
||||
|
||||
@@ -61,10 +62,25 @@ const HomePage = createClass({
|
||||
handleSplitMove : function(){
|
||||
this.editor.current.update();
|
||||
},
|
||||
|
||||
handleEditorViewPageChange : function(pageNumber){
|
||||
console.log(`editor view : ${pageNumber}`);
|
||||
this.setState({ currentEditorViewPageNum: pageNumber });
|
||||
},
|
||||
|
||||
handleEditorCursorPageChange : function(pageNumber){
|
||||
console.log(`editor cursor : ${pageNumber}`);
|
||||
this.setState({ currentEditorCursorPageNum: pageNumber });
|
||||
},
|
||||
|
||||
handleBrewRendererPageChange : function(pageNumber){
|
||||
console.log(`brewRenderer view : ${pageNumber}`);
|
||||
this.setState({ currentBrewRendererPageNum: pageNumber });
|
||||
},
|
||||
|
||||
handleTextChange : function(text){
|
||||
this.setState((prevState)=>({
|
||||
brew : { ...prevState.brew, text: text },
|
||||
currentEditorPage : this.editor.current.getCurrentPage() - 1 //Offset index since Marked starts pages at 0
|
||||
brew : { ...prevState.brew, text: text },
|
||||
}));
|
||||
},
|
||||
renderNavbar : function(){
|
||||
@@ -97,12 +113,20 @@ const HomePage = createClass({
|
||||
renderer={this.state.brew.renderer}
|
||||
showEditButtons={false}
|
||||
snippetBundle={this.state.themeBundle.snippets}
|
||||
onCursorPageChange={this.handleEditorCursorPageChange}
|
||||
onViewPageChange={this.handleEditorViewPageChange}
|
||||
currentEditorViewPageNum={this.state.currentEditorViewPageNum}
|
||||
currentEditorCursorPageNum={this.state.currentEditorCursorPageNum}
|
||||
currentBrewRendererPageNum={this.state.currentBrewRendererPageNum}
|
||||
/>
|
||||
<BrewRenderer
|
||||
text={this.state.brew.text}
|
||||
style={this.state.brew.style}
|
||||
renderer={this.state.brew.renderer}
|
||||
currentEditorPage={this.state.currentEditorPage}
|
||||
onPageChange={this.handleBrewRendererPageChange}
|
||||
currentEditorViewPageNum={this.state.currentEditorViewPageNum}
|
||||
currentEditorCursorPageNum={this.state.currentEditorCursorPageNum}
|
||||
currentBrewRendererPageNum={this.state.currentBrewRendererPageNum}
|
||||
themeBundle={this.state.themeBundle}
|
||||
/>
|
||||
</SplitPane>
|
||||
|
||||
@@ -39,13 +39,15 @@ const NewPage = createClass({
|
||||
const brew = this.props.brew;
|
||||
|
||||
return {
|
||||
brew : brew,
|
||||
isSaving : false,
|
||||
saveGoogle : (global.account && global.account.googleId ? true : false),
|
||||
error : null,
|
||||
htmlErrors : Markdown.validate(brew.text),
|
||||
currentEditorPage : 0,
|
||||
themeBundle : {}
|
||||
brew : brew,
|
||||
isSaving : false,
|
||||
saveGoogle : (global.account && global.account.googleId ? true : false),
|
||||
error : null,
|
||||
htmlErrors : Markdown.validate(brew.text),
|
||||
currentEditorViewPageNum : 0,
|
||||
currentEditorCursorPageNum : 0,
|
||||
currentBrewRendererPageNum : 0,
|
||||
themeBundle : {}
|
||||
};
|
||||
},
|
||||
|
||||
@@ -108,15 +110,29 @@ const NewPage = createClass({
|
||||
this.editor.current.update();
|
||||
},
|
||||
|
||||
handleEditorViewPageChange : function(pageNumber){
|
||||
console.log(`editor view : ${pageNumber}`);
|
||||
this.setState({ currentEditorViewPageNum: pageNumber });
|
||||
},
|
||||
|
||||
handleEditorCursorPageChange : function(pageNumber){
|
||||
console.log(`editor cursor : ${pageNumber}`);
|
||||
this.setState({ currentEditorCursorPageNum: pageNumber });
|
||||
},
|
||||
|
||||
handleBrewRendererPageChange : function(pageNumber){
|
||||
console.log(`brewRenderer view : ${pageNumber}`);
|
||||
this.setState({ currentBrewRendererPageNum: pageNumber });
|
||||
},
|
||||
|
||||
handleTextChange : function(text){
|
||||
//If there are errors, run the validator on every change to give quick feedback
|
||||
let htmlErrors = this.state.htmlErrors;
|
||||
if(htmlErrors.length) htmlErrors = Markdown.validate(text);
|
||||
|
||||
this.setState((prevState)=>({
|
||||
brew : { ...prevState.brew, text: text },
|
||||
htmlErrors : htmlErrors,
|
||||
currentEditorPage : this.editor.current.getCurrentPage() - 1 //Offset index since Marked starts pages at 0
|
||||
brew : { ...prevState.brew, text: text },
|
||||
htmlErrors : htmlErrors,
|
||||
}));
|
||||
localStorage.setItem(BREWKEY, text);
|
||||
},
|
||||
@@ -221,6 +237,11 @@ const NewPage = createClass({
|
||||
renderer={this.state.brew.renderer}
|
||||
userThemes={this.props.userThemes}
|
||||
snippetBundle={this.state.themeBundle.snippets}
|
||||
onCursorPageChange={this.handleEditorCursorPageChange}
|
||||
onViewPageChange={this.handleEditorViewPageChange}
|
||||
currentEditorViewPageNum={this.state.currentEditorViewPageNum}
|
||||
currentEditorCursorPageNum={this.state.currentEditorCursorPageNum}
|
||||
currentBrewRendererPageNum={this.state.currentBrewRendererPageNum}
|
||||
/>
|
||||
<BrewRenderer
|
||||
text={this.state.brew.text}
|
||||
@@ -230,7 +251,10 @@ const NewPage = createClass({
|
||||
themeBundle={this.state.themeBundle}
|
||||
errors={this.state.htmlErrors}
|
||||
lang={this.state.brew.lang}
|
||||
currentEditorPage={this.state.currentEditorPage}
|
||||
onPageChange={this.handleBrewRendererPageChange}
|
||||
currentEditorViewPageNum={this.state.currentEditorViewPageNum}
|
||||
currentEditorCursorPageNum={this.state.currentEditorCursorPageNum}
|
||||
currentBrewRendererPageNum={this.state.currentBrewRendererPageNum}
|
||||
allowPrint={true}
|
||||
/>
|
||||
</SplitPane>
|
||||
|
||||
200
package-lock.json
generated
200
package-lock.json
generated
@@ -23,7 +23,7 @@
|
||||
"dedent-tabs": "^0.10.3",
|
||||
"dompurify": "^3.1.6",
|
||||
"expr-eval": "^2.0.2",
|
||||
"express": "^4.19.2",
|
||||
"express": "^4.21.0",
|
||||
"express-async-handler": "^1.2.0",
|
||||
"express-static-gzip": "2.1.7",
|
||||
"fs-extra": "11.2.0",
|
||||
@@ -38,22 +38,22 @@
|
||||
"marked-smartypants-lite": "^1.0.2",
|
||||
"markedLegacy": "npm:marked@^0.3.19",
|
||||
"moment": "^2.30.1",
|
||||
"mongoose": "^8.6.1",
|
||||
"mongoose": "^8.6.2",
|
||||
"nanoid": "3.3.4",
|
||||
"nconf": "^0.12.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-frame-component": "^4.1.3",
|
||||
"react-router-dom": "6.26.1",
|
||||
"react-router-dom": "6.26.2",
|
||||
"sanitize-filename": "1.6.3",
|
||||
"superagent": "^10.1.0",
|
||||
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@stylistic/stylelint-plugin": "^3.0.1",
|
||||
"eslint": "^9.9.1",
|
||||
"eslint": "^9.10.0",
|
||||
"eslint-plugin-jest": "^28.8.3",
|
||||
"eslint-plugin-react": "^7.35.2",
|
||||
"eslint-plugin-react": "^7.36.1",
|
||||
"globals": "^15.9.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-expect-message": "^1.1.3",
|
||||
@@ -2073,9 +2073,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz",
|
||||
"integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==",
|
||||
"version": "9.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.10.0.tgz",
|
||||
"integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2090,6 +2090,18 @@
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/plugin-kit": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.1.0.tgz",
|
||||
"integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"levn": "^0.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@googleapis/drive": {
|
||||
"version": "8.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-8.14.0.tgz",
|
||||
@@ -3013,9 +3025,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz",
|
||||
"integrity": "sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==",
|
||||
"version": "1.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz",
|
||||
"integrity": "sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
@@ -4128,10 +4140,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
|
||||
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
|
||||
"license": "MIT",
|
||||
"version": "1.20.3",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
||||
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.5",
|
||||
@@ -4141,7 +4152,7 @@
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.11.0",
|
||||
"qs": "6.13.0",
|
||||
"raw-body": "2.5.2",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
@@ -5589,10 +5600,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
||||
"license": "MIT",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
@@ -5819,16 +5829,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.9.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.1.tgz",
|
||||
"integrity": "sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==",
|
||||
"version": "9.10.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.10.0.tgz",
|
||||
"integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.11.0",
|
||||
"@eslint/config-array": "^0.18.0",
|
||||
"@eslint/eslintrc": "^3.1.0",
|
||||
"@eslint/js": "9.9.1",
|
||||
"@eslint/js": "9.10.0",
|
||||
"@eslint/plugin-kit": "^0.1.0",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@humanwhocodes/retry": "^0.3.0",
|
||||
"@nodelib/fs.walk": "^1.2.8",
|
||||
@@ -5851,7 +5862,6 @@
|
||||
"is-glob": "^4.0.0",
|
||||
"is-path-inside": "^3.0.3",
|
||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||
"levn": "^0.4.1",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"minimatch": "^3.1.2",
|
||||
"natural-compare": "^1.4.0",
|
||||
@@ -5903,9 +5913,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-react": {
|
||||
"version": "7.35.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.2.tgz",
|
||||
"integrity": "sha512-Rbj2R9zwP2GYNcIak4xoAMV57hrBh3hTaR0k7hVjwCQgryE/pw5px4b13EYjduOI0hfXyZhwBxaGpOTbWSGzKQ==",
|
||||
"version": "7.36.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.36.1.tgz",
|
||||
"integrity": "sha512-/qwbqNXZoq+VP30s1d4Nc1C5GTxjJQjk4Jzs4Wq2qzxFM7dSmuG2UkIjg2USMLh3A/aVcUNrK7v0J5U1XEGGwA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"array-includes": "^3.1.8",
|
||||
@@ -6305,37 +6315,36 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.19.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
|
||||
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
|
||||
"license": "MIT",
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
|
||||
"integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.2",
|
||||
"body-parser": "1.20.3",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.6.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"encodeurl": "~1.0.2",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "1.2.0",
|
||||
"finalhandler": "1.3.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"merge-descriptors": "1.0.1",
|
||||
"merge-descriptors": "1.0.3",
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.7",
|
||||
"path-to-regexp": "0.1.10",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.11.0",
|
||||
"qs": "6.13.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "0.18.0",
|
||||
"serve-static": "1.15.0",
|
||||
"send": "0.19.0",
|
||||
"serve-static": "1.16.2",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"type-is": "~1.6.18",
|
||||
@@ -6574,13 +6583,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
|
||||
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
|
||||
"license": "MIT",
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
|
||||
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"encodeurl": "~1.0.2",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
@@ -6595,7 +6603,6 @@
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
@@ -6603,8 +6610,7 @@
|
||||
"node_modules/finalhandler/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/find-up": {
|
||||
"version": "5.0.0",
|
||||
@@ -10523,10 +10529,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==",
|
||||
"license": "MIT"
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
||||
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-stream": {
|
||||
"version": "2.0.0",
|
||||
@@ -10794,9 +10802,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mongoose": {
|
||||
"version": "8.6.1",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.6.1.tgz",
|
||||
"integrity": "sha512-dppGcYqvsdg+VcnqXR5b467V4a+iNhmvkfYNpEPi6AjaUxnz6ioEDmrMLOi+sOWjvoHapuwPOigV4f2l7HC6ag==",
|
||||
"version": "8.6.2",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.6.2.tgz",
|
||||
"integrity": "sha512-ErbDVvuUzUfyQpXvJ6sXznmZDICD8r6wIsa0VKjJtB6/LZncqwUn5Um040G1BaNo6L3Jz+xItLSwT0wZmSmUaQ==",
|
||||
"dependencies": {
|
||||
"bson": "^6.7.0",
|
||||
"kareem": "2.6.3",
|
||||
@@ -11637,10 +11645,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
|
||||
"license": "MIT"
|
||||
"version": "0.1.10",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
|
||||
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="
|
||||
},
|
||||
"node_modules/path-type": {
|
||||
"version": "4.0.0",
|
||||
@@ -12074,12 +12081,11 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||
"license": "BSD-3-Clause",
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.4"
|
||||
"side-channel": "^1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
@@ -12204,11 +12210,11 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.26.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.1.tgz",
|
||||
"integrity": "sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==",
|
||||
"version": "6.26.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz",
|
||||
"integrity": "sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.19.1"
|
||||
"@remix-run/router": "1.19.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -12218,12 +12224,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "6.26.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.1.tgz",
|
||||
"integrity": "sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==",
|
||||
"version": "6.26.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz",
|
||||
"integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.19.1",
|
||||
"react-router": "6.26.1"
|
||||
"@remix-run/router": "1.19.2",
|
||||
"react-router": "6.26.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -12721,10 +12727,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
|
||||
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
|
||||
"license": "MIT",
|
||||
"version": "0.19.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
|
||||
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
@@ -12748,7 +12753,6 @@
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
@@ -12756,25 +12760,30 @@
|
||||
"node_modules/send/node_modules/debug/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/send/node_modules/encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
|
||||
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
|
||||
"license": "MIT",
|
||||
"version": "1.16.2",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
||||
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
|
||||
"dependencies": {
|
||||
"encodeurl": "~1.0.2",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"parseurl": "~1.3.3",
|
||||
"send": "0.18.0"
|
||||
"send": "0.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
@@ -14616,21 +14625,6 @@
|
||||
"integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/url/node_modules/qs": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/use": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
|
||||
|
||||
10
package.json
10
package.json
@@ -98,7 +98,7 @@
|
||||
"dedent-tabs": "^0.10.3",
|
||||
"dompurify": "^3.1.6",
|
||||
"expr-eval": "^2.0.2",
|
||||
"express": "^4.19.2",
|
||||
"express": "^4.21.0",
|
||||
"express-async-handler": "^1.2.0",
|
||||
"express-static-gzip": "2.1.7",
|
||||
"fs-extra": "11.2.0",
|
||||
@@ -113,22 +113,22 @@
|
||||
"marked-smartypants-lite": "^1.0.2",
|
||||
"markedLegacy": "npm:marked@^0.3.19",
|
||||
"moment": "^2.30.1",
|
||||
"mongoose": "^8.6.1",
|
||||
"mongoose": "^8.6.2",
|
||||
"nanoid": "3.3.4",
|
||||
"nconf": "^0.12.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-frame-component": "^4.1.3",
|
||||
"react-router-dom": "6.26.1",
|
||||
"react-router-dom": "6.26.2",
|
||||
"sanitize-filename": "1.6.3",
|
||||
"superagent": "^10.1.0",
|
||||
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@stylistic/stylelint-plugin": "^3.0.1",
|
||||
"eslint": "^9.9.1",
|
||||
"eslint": "^9.10.0",
|
||||
"eslint-plugin-jest": "^28.8.3",
|
||||
"eslint-plugin-react": "^7.35.2",
|
||||
"eslint-plugin-react": "^7.36.1",
|
||||
"globals": "^15.9.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-expect-message": "^1.1.3",
|
||||
|
||||
@@ -203,6 +203,23 @@ app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{
|
||||
res.status(200).send(brew.text);
|
||||
});
|
||||
|
||||
//Serve brew metadata
|
||||
app.get('/metadata/:id', asyncHandler(getBrew('share')), (req, res) => {
|
||||
const { brew } = req;
|
||||
sanitizeBrew(brew, 'share');
|
||||
|
||||
const fields = [ 'title', 'pageCount', 'description', 'authors', 'lang',
|
||||
'published', 'views', 'shareId', 'createdAt', 'updatedAt',
|
||||
'lastViewed', 'thumbnail', 'tags'
|
||||
];
|
||||
|
||||
const metadata = fields.reduce((acc, field) => {
|
||||
if (brew[field] !== undefined) acc[field] = brew[field];
|
||||
return acc;
|
||||
}, {});
|
||||
res.status(200).json(metadata);
|
||||
});
|
||||
|
||||
//Serve brew styling
|
||||
app.get('/css/:id', asyncHandler(getBrew('share')), (req, res)=>{getCSS(req, res);});
|
||||
|
||||
@@ -440,6 +457,10 @@ if(isLocalEnvironment){
|
||||
|
||||
//Vault Page
|
||||
app.get('/vault', asyncHandler(async(req, res, next)=>{
|
||||
req.ogMeta = { ...defaultMetaTags,
|
||||
title : 'The Vault',
|
||||
description : 'Search for Brews'
|
||||
};
|
||||
return next();
|
||||
}));
|
||||
|
||||
|
||||
@@ -49,12 +49,12 @@ const CodeEditor = createClass({
|
||||
displayName : 'CodeEditor',
|
||||
getDefaultProps : function() {
|
||||
return {
|
||||
language : '',
|
||||
value : '',
|
||||
wrap : true,
|
||||
onChange : ()=>{},
|
||||
enableFolding : true,
|
||||
editorTheme : 'default'
|
||||
language : '',
|
||||
value : '',
|
||||
wrap : true,
|
||||
onChange : ()=>{},
|
||||
enableFolding : true,
|
||||
editorTheme : 'default'
|
||||
};
|
||||
},
|
||||
|
||||
@@ -189,7 +189,7 @@ const CodeEditor = createClass({
|
||||
autoCompleteEmoji.showAutocompleteEmoji(CodeMirror, this.codeMirror);
|
||||
|
||||
// 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();
|
||||
},
|
||||
|
||||
@@ -397,6 +397,11 @@ const CodeEditor = createClass({
|
||||
getCursorPosition : function(){
|
||||
return this.codeMirror.getCursor();
|
||||
},
|
||||
getTopVisibleLine : function(){
|
||||
const rect = this.codeMirror.getWrapperElement().getBoundingClientRect();
|
||||
const topVisibleLine = this.codeMirror.lineAtHeight(rect.top, "window");
|
||||
return topVisibleLine;
|
||||
},
|
||||
updateSize : function(){
|
||||
this.codeMirror.refresh();
|
||||
},
|
||||
|
||||
@@ -126,7 +126,7 @@ const SplitPane = createClass({
|
||||
|
||||
renderDivider : function(){
|
||||
return <>
|
||||
{this.renderMoveArrows()}
|
||||
{this.props.showDividerButtons && this.renderMoveArrows()}
|
||||
<div className='divider' onPointerDown={this.handleDown} >
|
||||
<div className='dots'>
|
||||
<i className='fas fa-circle' />
|
||||
|
||||
@@ -1,77 +1,78 @@
|
||||
const _ = require('lodash');
|
||||
const dedent = require('dedent-tabs').default;
|
||||
|
||||
const getTOC = (pages)=>{
|
||||
// Map each actual page to its footer label, accounting for skips or numbering resets
|
||||
const mapPages = (pages)=>{
|
||||
let actualPage = 0;
|
||||
let mappedPage = 0; // Number displayed in footer
|
||||
let pageMap = [];
|
||||
|
||||
const recursiveAdd = (title, page, targetDepth, child, curDepth=0)=>{
|
||||
if(curDepth > 5) return; // Something went wrong.
|
||||
if(curDepth == targetDepth) {
|
||||
child.push({
|
||||
title : title,
|
||||
page : page,
|
||||
children : []
|
||||
});
|
||||
} else {
|
||||
if(child.length == 0) {
|
||||
child.push({
|
||||
title : null,
|
||||
page : page,
|
||||
children : []
|
||||
});
|
||||
pages.forEach(page => {
|
||||
actualPage++;
|
||||
const doSkip = page.querySelector('.skipCounting');
|
||||
const doReset = page.querySelector('.resetCounting');
|
||||
|
||||
if(doReset)
|
||||
mappedPage = 1;
|
||||
if(!doSkip && !doReset)
|
||||
mappedPage++;
|
||||
|
||||
pageMap[actualPage] = {
|
||||
mappedPage : mappedPage,
|
||||
showPage : !doSkip
|
||||
};
|
||||
});
|
||||
return pageMap;
|
||||
};
|
||||
|
||||
const getMarkdown = (headings, pageMap) => {
|
||||
const levelPad = ['- ###', ' - ####', ' -', ' -', ' -', ' -'];
|
||||
|
||||
let allMarkdown = [];
|
||||
let depthChain = [0];
|
||||
|
||||
headings.forEach(heading => {
|
||||
const page = parseInt(heading.closest('.page').id?.replace(/^p/, ''));
|
||||
const mappedPage = pageMap[page].mappedPage;
|
||||
const showPage = pageMap[page].showPage;
|
||||
const title = heading.textContent.trim();
|
||||
const ToCExclude = getComputedStyle(heading).getPropertyValue('--TOC');
|
||||
const depth = parseInt(heading.tagName.substring(1));
|
||||
|
||||
if(!title || !showPage || ToCExclude == 'exclude')
|
||||
return;
|
||||
|
||||
//If different header depth than last, remove indents until nearest higher-level header, then indent once
|
||||
if (depth !== depthChain[depthChain.length -1]) {
|
||||
while (depth <= depthChain[depthChain.length - 1]) {
|
||||
depthChain.pop();
|
||||
}
|
||||
recursiveAdd(title, page, targetDepth, _.last(child).children, curDepth+1,);
|
||||
depthChain.push(depth);
|
||||
}
|
||||
};
|
||||
|
||||
const res = [];
|
||||
let markdown = `${levelPad[depthChain.length - 2]} [{{ ${title}}}{{ ${mappedPage}}}](#p${page})`;
|
||||
allMarkdown.push(markdown);
|
||||
});
|
||||
return allMarkdown.join('\n');
|
||||
};
|
||||
|
||||
const getTOC = ()=>{
|
||||
const iframe = document.getElementById('BrewRenderer');
|
||||
const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
|
||||
const headings = iframeDocument.querySelectorAll('h1, h2, h3, h4, h5, h6');
|
||||
const headerDepth = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'];
|
||||
const pages = iframeDocument.querySelectorAll('.page');
|
||||
|
||||
_.each(headings, (heading)=>{
|
||||
const onPage = parseInt(heading.closest('.page').id?.replace(/^p/, ''));
|
||||
const ToCExclude = getComputedStyle(heading).getPropertyValue('--TOC');
|
||||
|
||||
if(ToCExclude != 'exclude') {
|
||||
recursiveAdd(heading.textContent.trim(), onPage, headerDepth.indexOf(heading.tagName), res);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
};
|
||||
|
||||
|
||||
const ToCIterate = (entries, curDepth=0)=>{
|
||||
const levelPad = ['- ###', ' - ####', ' - ', ' - ', ' - ', ' - '];
|
||||
const toc = [];
|
||||
if(entries.title !== null){
|
||||
toc.push(`${levelPad[curDepth]} [{{ ${entries.title}}}{{ ${entries.page}}}](#p${entries.page})`);
|
||||
}
|
||||
if(entries.children.length) {
|
||||
_.each(entries.children, (entry, idx)=>{
|
||||
const children = ToCIterate(entry, entry.title == null ? curDepth : curDepth+1);
|
||||
if(children.length) {
|
||||
toc.push(...children);
|
||||
}
|
||||
});
|
||||
}
|
||||
return toc;
|
||||
const pageMap = mapPages(pages);
|
||||
return getMarkdown(headings, pageMap);
|
||||
};
|
||||
|
||||
module.exports = function(props){
|
||||
const pages = props.brew.text.split('\\page');
|
||||
const TOC = getTOC(pages);
|
||||
const markdown = _.reduce(TOC, (r, g1, idx1)=>{
|
||||
r.push(ToCIterate(g1).join('\n'));
|
||||
return r;
|
||||
}, []).join('\n');
|
||||
const TOC = getTOC();
|
||||
|
||||
return dedent`
|
||||
{{toc,wide
|
||||
# Contents
|
||||
|
||||
${markdown}
|
||||
${TOC}
|
||||
}}
|
||||
\n`;
|
||||
};
|
||||
};
|
||||
@@ -23,14 +23,30 @@ module.exports = [
|
||||
gen : '\n\\page\n'
|
||||
},
|
||||
{
|
||||
name : 'Page Number',
|
||||
icon : 'fas fa-bookmark',
|
||||
gen : '{{pageNumber 1}}\n'
|
||||
},
|
||||
{
|
||||
name : 'Auto-incrementing Page Number',
|
||||
icon : 'fas fa-sort-numeric-down',
|
||||
gen : '{{pageNumber,auto}}\n'
|
||||
name : 'Page Numbering',
|
||||
icon : 'fas fa-bookmark',
|
||||
subsnippets : [
|
||||
{
|
||||
name : 'Page Number',
|
||||
icon : 'fas fa-bookmark',
|
||||
gen : '{{pageNumber 1}}\n'
|
||||
},
|
||||
{
|
||||
name : 'Auto-incrementing Page Number',
|
||||
icon : 'fas fa-sort-numeric-down',
|
||||
gen : '{{pageNumber,auto}}\n'
|
||||
},
|
||||
{
|
||||
name : 'Skip Page Number Increment this Page',
|
||||
icon : 'fas fa-xmark',
|
||||
gen : '{{skipCounting}}\n'
|
||||
},
|
||||
{
|
||||
name : 'Restart Numbering',
|
||||
icon : 'fas fa-arrow-rotate-left',
|
||||
gen : '{{resetCounting}}\n'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
name : 'Footer',
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
}
|
||||
|
||||
@page { margin : 0; }
|
||||
body { counter-reset : page-numbers; }
|
||||
body { counter-reset : page-numbers 0; }
|
||||
* { -webkit-print-color-adjust : exact; }
|
||||
|
||||
//*****************************
|
||||
@@ -51,7 +51,6 @@ body { counter-reset : page-numbers; }
|
||||
height : 279.4mm;
|
||||
padding : 1.4cm 1.9cm 1.7cm;
|
||||
overflow : hidden;
|
||||
counter-increment : page-numbers;
|
||||
background-color : var(--HB_Color_Background);
|
||||
text-rendering : optimizeLegibility;
|
||||
contain : size;
|
||||
@@ -494,4 +493,13 @@ body { counter-reset : page-numbers; }
|
||||
&:nth-child(even) {
|
||||
.pageNumber { left : 30px; }
|
||||
}
|
||||
}
|
||||
|
||||
.resetCounting {
|
||||
counter-set : page-numbers 1;
|
||||
}
|
||||
|
||||
&:not(:has(.skipCounting)) {
|
||||
counter-increment : page-numbers;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user