0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-15 21:22:43 +00:00

Merge branch 'master' into pr/1819

This commit is contained in:
Trevor Buckner
2021-12-09 22:30:23 -05:00
14 changed files with 1939 additions and 1609 deletions

View File

@@ -3,11 +3,12 @@ h5 {
font-size: .35cm !important; font-size: .35cm !important;
} }
.taskList li { .page ul ul {
list-style-type : none; margin-left: 0px;
} }
.taskList li input { .taskList li input {
list-style-type : none;
margin-left : -0.52cm; margin-left : -0.52cm;
transform: translateY(.05cm); transform: translateY(.05cm);
filter: brightness(1.1) drop-shadow(1px 2px 1px #222); filter: brightness(1.1) drop-shadow(1px 2px 1px #222);
@@ -33,6 +34,49 @@ pre {
## changelog ## changelog
For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery). For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery).
### Tuesday 07/12/2021 - v3.0.5
{{taskList
* [x] Fixed paragraph spacing for **note** and **descriptive** boxes in V3.
Fixes issues: [#1836](https://github.com/naturalcrit/homebrewery/issues/1836)
* [x] Added a whole bunch of hotkeys:
* Page Break `CTRL + ENTER`
* Column Break `CTRL + SHIFT + ENTER`
* Bulleted Lists `CTRL + L`
* Numbered Lists `CTRL + SHIFT + L`
* Headers `CTRL + SHIFT + (1-6)`
* Underline `CTRL + U`
* Link `CTRL + K`
* Non-breaking space (\ ) `CTRL + .`
* Add Horizontal Space `CTRL + SHIFT + .`
* Remove Horizontal Space `CTRL + SHIFT + ,`
* Curly Span `CTRL + M`
* Curly Div `CTRL + SHIFT + M`
* [x] Fixed page numbers in the editor panel getting scrambled when scrolling up and down.
* [x] Faster swapping between tabs on long brews.
* [x] Better error messages for common issue with Google Drive credentials expiring.
}}
### Wednesday 17/11/2021 - v3.0.4
{{taskList
* [x] Fixed incorrect sorting of Google brews by page count and views on the user page.
Fixes issues: [#1793](https://github.com/naturalcrit/homebrewery/issues/1793)
* [x] Added code folding! Only on a page-level for now. Hotkeys `CTRL + [` and `CTRL + ]` to fold/unfold all pages. (Thanks jeddai, new contributor!)
Fixes issues: [#629](https://github.com/naturalcrit/homebrewery/issues/629)
* [x] Fixed rendering issues due to the latest Chrome update to version 96. (Also thanks to jeddai!)
Fixes issues: [#1828](https://github.com/naturalcrit/homebrewery/issues/1828)
}}
### Wednesday 27/10/2021 - v3.0.3 ### Wednesday 27/10/2021 - v3.0.3
{{taskList {{taskList

View File

@@ -107,67 +107,69 @@ const Editor = createClass({
if(this.state.view === 'text') { if(this.state.view === 'text') {
const codeMirror = this.refs.codeEditor.codeMirror; const codeMirror = this.refs.codeEditor.codeMirror;
//reset custom text styles codeMirror.operation(()=>{ // Batch CodeMirror styling
const customHighlights = codeMirror.getAllMarks().filter((mark)=>!mark.__isFold); //Don't undo code folding //reset custom text styles
for (let i=0;i<customHighlights.length;i++) customHighlights[i].clear(); const customHighlights = codeMirror.getAllMarks().filter((mark)=>!mark.__isFold); //Don't undo code folding
for (let i=customHighlights.length - 1;i>=0;i--) customHighlights[i].clear();
const lineNumbers = _.reduce(this.props.brew.text.split('\n'), (r, line, lineNumber)=>{ let editorPageCount = 2; // start page count from page 2
//reset custom line styles _.forEach(this.props.brew.text.split('\n'), (line, lineNumber)=>{
codeMirror.removeLineClass(lineNumber, 'background', 'pageLine');
codeMirror.removeLineClass(lineNumber, 'text');
// Legacy Codemirror styling //reset custom line styles
if(this.props.renderer == 'legacy') { codeMirror.removeLineClass(lineNumber, 'background');
if(line.includes('\\page')){ codeMirror.removeLineClass(lineNumber, 'text');
// Styling for \page breaks
if((this.props.renderer == 'legacy' && line.includes('\\page')) ||
(this.props.renderer == 'V3' && line.match(/^\\page$/))) {
// add back the original class 'background' but also add the new class '.pageline'
codeMirror.addLineClass(lineNumber, 'background', 'pageLine'); codeMirror.addLineClass(lineNumber, 'background', 'pageLine');
r.push(lineNumber); const pageCountElement = Object.assign(document.createElement('span'), {
} className : 'editor-page-count',
} textContent : editorPageCount
});
codeMirror.setBookmark({ line: lineNumber, ch: line.length }, pageCountElement);
// New Codemirror styling for V3 renderer editorPageCount += 1;
if(this.props.renderer == 'V3') { };
if(line.match(/^\\page$/)){
codeMirror.addLineClass(lineNumber, 'background', 'pageLine');
r.push(lineNumber);
}
if(line.match(/^\\column$/)){ // New Codemirror styling for V3 renderer
codeMirror.addLineClass(lineNumber, 'text', 'columnSplit'); if(this.props.renderer == 'V3') {
r.push(lineNumber); if(line.match(/^\\column$/)){
} codeMirror.addLineClass(lineNumber, 'text', 'columnSplit');
// Highlight inline spans {{content}}
if(line.includes('{{') && line.includes('}}')){
const regex = /{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*\s*|}}/g;
let match;
let blockCount = 0;
while ((match = regex.exec(line)) != null) {
if(match[0].startsWith('{')) {
blockCount += 1;
} else {
blockCount -= 1;
}
if(blockCount < 0) {
blockCount = 0;
continue;
}
codeMirror.markText({ line: lineNumber, ch: match.index }, { line: lineNumber, ch: match.index + match[0].length }, { className: 'inline-block' });
} }
} else if(line.trimLeft().startsWith('{{') || line.trimLeft().startsWith('}}')){
// Highlight block divs {{\n Content \n}}
let endCh = line.length+1;
const match = line.match(/^ *{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])* *$|^ *}}$/); // Highlight inline spans {{content}}
if(match) if(line.includes('{{') && line.includes('}}')){
endCh = match.index+match[0].length; const regex = /{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])*\s*|}}/g;
codeMirror.markText({ line: lineNumber, ch: 0 }, { line: lineNumber, ch: endCh }, { className: 'block' }); let match;
let blockCount = 0;
while ((match = regex.exec(line)) != null) {
if(match[0].startsWith('{')) {
blockCount += 1;
} else {
blockCount -= 1;
}
if(blockCount < 0) {
blockCount = 0;
continue;
}
codeMirror.markText({ line: lineNumber, ch: match.index }, { line: lineNumber, ch: match.index + match[0].length }, { className: 'inline-block' });
}
} else if(line.trimLeft().startsWith('{{') || line.trimLeft().startsWith('}}')){
// Highlight block divs {{\n Content \n}}
let endCh = line.length+1;
const match = line.match(/^ *{{(?::(?:"[\w,\-()#%. ]*"|[\w\,\-()#%.]*)|[^"'{}\s])* *$|^ *}}$/);
if(match)
endCh = match.index+match[0].length;
codeMirror.markText({ line: lineNumber, ch: 0 }, { line: lineNumber, ch: endCh }, { className: 'block' });
}
} }
} });
});
return r;
}, []);
return lineNumbers;
} }
}, },

View File

@@ -5,17 +5,13 @@
.codeEditor{ .codeEditor{
height : 100%; height : 100%;
counter-reset : page;
counter-increment : page;
.pageLine{ .pageLine{
background : #33333328; background : #33333328;
border-top : #339 solid 1px; border-top : #339 solid 1px;
&:after{ }
content : counter(page); .editor-page-count{
counter-increment : page; color : grey;
float : right; float : right;
color : gray;
}
} }
.columnSplit{ .columnSplit{
font-style : italic; font-style : italic;

View File

@@ -349,14 +349,14 @@ const EditPage = createClass({
</Nav.item>; </Nav.item>;
} }
if(this.state.errors.status == '403' && this.state.errors.response.body.errors[0].reason == 'insufficientPermissions'){ if(this.state.errors.response.req.url.match(/^\/api\/.*Google.*$/m)){
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'> return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
Oops! Oops!
<div className='errorContainer' onClick={this.clearErrors}> <div className='errorContainer' onClick={this.clearErrors}>
Looks like your Google credentials have Looks like your Google credentials have
expired! Visit the log in page to sign out expired! Visit our log in page to sign out
and sign back in with Google and sign back in with Google,
to save this to Google Drive! then try saving again!
<a target='_blank' rel='noopener noreferrer' <a target='_blank' rel='noopener noreferrer'
href={`https://www.naturalcrit.com/login?redirect=${this.state.url}`}> href={`https://www.naturalcrit.com/login?redirect=${this.state.url}`}>
<div className='confirm'> <div className='confirm'>

View File

@@ -226,14 +226,14 @@ const NewPage = createClass({
</Nav.item>; </Nav.item>;
} }
if(this.state.errors.status == '403' && this.state.errors.response.body.errors[0].reason == 'insufficientPermissions'){ if(this.state.errors.response.req.url.match(/^\/api\/.*Google.*$/m)){
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'> return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
Oops! Oops!
<div className='errorContainer' onClick={this.clearErrors}> <div className='errorContainer' onClick={this.clearErrors}>
Looks like your Google credentials have Looks like your Google credentials have
expired! Visit the log in page to sign out expired! Visit our log in page to sign out
and sign back in with Google and sign back in with Google,
to save this to Google Drive! then try saving again!
<a target='_blank' rel='noopener noreferrer' <a target='_blank' rel='noopener noreferrer'
href={`https://www.naturalcrit.com/login?redirect=${this.state.url}`}> href={`https://www.naturalcrit.com/login?redirect=${this.state.url}`}>
<div className='confirm'> <div className='confirm'>

View File

@@ -2,5 +2,6 @@
"host" : "homebrewery.local.naturalcrit.com:8000", "host" : "homebrewery.local.naturalcrit.com:8000",
"naturalcrit_url" : "local.naturalcrit.com:8010", "naturalcrit_url" : "local.naturalcrit.com:8010",
"secret" : "secret", "secret" : "secret",
"web_port" : 8000 "web_port" : 8000,
"enable_v3" : true
} }

3058
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{ {
"name": "homebrewery", "name": "homebrewery",
"description": "Create authentic looking D&D homebrews using only markdown", "description": "Create authentic looking D&D homebrews using only markdown",
"version": "3.0.3", "version": "3.0.5",
"engines": { "engines": {
"node": "16.11.x" "node": "16.11.x"
}, },
@@ -40,28 +40,29 @@
] ]
}, },
"dependencies": { "dependencies": {
"@babel/core": "^7.15.8", "@babel/core": "^7.16.0",
"@babel/plugin-transform-runtime": "^7.15.8", "@babel/plugin-transform-runtime": "^7.16.4",
"@babel/preset-env": "^7.15.8", "@babel/preset-env": "^7.16.4",
"@babel/preset-react": "^7.14.5", "@babel/preset-react": "^7.16.0",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"classnames": "^2.3.1", "classnames": "^2.3.1",
"codemirror": "^5.63.3", "codemirror": "^5.64.0",
"cookie-parser": "^1.4.5", "cookie-parser": "^1.4.6",
"create-react-class": "^15.7.0", "create-react-class": "^15.7.0",
"dedent-tabs": "^0.10.1", "dedent-tabs": "^0.10.1",
"express": "^4.17.1", "express": "^4.17.1",
"express-async-handler": "^1.1.4", "express-async-handler": "^1.2.0",
"express-static-gzip": "2.1.1", "express-static-gzip": "2.1.1",
"fs-extra": "10.0.0", "fs-extra": "10.0.0",
"googleapis": "89.0.0", "googleapis": "92.0.0",
"js-yaml": "^4.1.0",
"jwt-simple": "^0.5.6", "jwt-simple": "^0.5.6",
"less": "^3.13.1", "less": "^3.13.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"marked": "3.0.8", "marked": "3.0.8",
"markedLegacy": "npm:marked@^0.3.19", "markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.29.1", "moment": "^2.29.1",
"mongoose": "^6.0.12", "mongoose": "^6.0.15",
"nanoid": "3.1.30", "nanoid": "3.1.30",
"nconf": "^0.11.3", "nconf": "^0.11.3",
"prop-types": "15.7.2", "prop-types": "15.7.2",
@@ -75,8 +76,8 @@
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git" "vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^8.1.0", "eslint": "^8.4.1",
"eslint-plugin-react": "^7.26.1", "eslint-plugin-react": "^7.27.1",
"pico-check": "^2.1.3" "pico-check": "^2.1.3"
} }
} }

View File

@@ -2,6 +2,7 @@
const _ = require('lodash'); const _ = require('lodash');
const jwt = require('jwt-simple'); const jwt = require('jwt-simple');
const express = require('express'); const express = require('express');
const yaml = require('js-yaml');
const app = express(); const app = express();
const homebrewApi = require('./server/homebrew.api.js'); const homebrewApi = require('./server/homebrew.api.js');
@@ -32,7 +33,7 @@ const getBrewFromId = asyncHandler(async (id, accessType)=>{
if(accessType == 'raw') { if(accessType == 'raw') {
return brew; return brew;
} }
splitTextAndStyle(brew); splitTextStyleAndMetadata(brew);
return brew; return brew;
}); });
@@ -45,8 +46,15 @@ const sanitizeBrew = (brew, full=false)=>{
return brew; return brew;
}; };
const splitTextAndStyle = (brew)=>{ const splitTextStyleAndMetadata = (brew)=>{
brew.text = brew.text.replaceAll('\r\n', '\n'); 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']));
brew.text = brew.text.slice(index + 5);
}
if(brew.text.startsWith('```css')) { if(brew.text.startsWith('```css')) {
const index = brew.text.indexOf('```\n\n'); const index = brew.text.indexOf('```\n\n');
brew.style = brew.text.slice(7, index - 1); brew.style = brew.text.slice(7, index - 1);
@@ -128,7 +136,7 @@ app.get('/v3_preview', async (req, res, next)=>{
text : welcomeTextV3, text : welcomeTextV3,
renderer : 'V3' renderer : 'V3'
}; };
splitTextAndStyle(brew); splitTextStyleAndMetadata(brew);
req.brew = brew; req.brew = brew;
return next(); return next();
}); });
@@ -140,7 +148,7 @@ app.get('/changelog', async (req, res, next)=>{
text : changelogText, text : changelogText,
renderer : 'V3' renderer : 'V3'
}; };
splitTextAndStyle(brew); splitTextStyleAndMetadata(brew);
req.brew = brew; req.brew = brew;
return next(); return next();
}); });
@@ -152,7 +160,7 @@ app.get('/faq', async (req, res, next)=>{
text : faqText, text : faqText,
renderer : 'V3' renderer : 'V3'
}; };
splitTextAndStyle(brew); splitTextStyleAndMetadata(brew);
req.brew = brew; req.brew = brew;
return next(); return next();
}); });

View File

@@ -120,10 +120,10 @@ GoogleActions = {
updatedAt : file.modifiedTime, updatedAt : file.modifiedTime,
gDrive : true, gDrive : true,
googleId : file.id, googleId : file.id,
pageCount : file.properties.pageCount, pageCount : parseInt(file.properties.pageCount),
title : file.properties.title, title : file.properties.title,
description : file.description, description : file.description,
views : file.properties.views, views : parseInt(file.properties.views),
tags : '', tags : '',
published : file.properties.published ? file.properties.published == 'true' : false, published : file.properties.published ? file.properties.published == 'true' : false,
authors : [req.account.username], //TODO: properly save and load authors to google drive authors : [req.account.username], //TODO: properly save and load authors to google drive

View File

@@ -4,6 +4,7 @@ const router = require('express').Router();
const zlib = require('zlib'); const zlib = require('zlib');
const GoogleActions = require('./googleActions.js'); const GoogleActions = require('./googleActions.js');
const Markdown = require('../shared/naturalcrit/markdown.js'); const Markdown = require('../shared/naturalcrit/markdown.js');
const yaml = require('js-yaml');
// const getTopBrews = (cb) => { // const getTopBrews = (cb) => {
// HomebrewModel.find().sort({ views: -1 }).limit(5).exec(function(err, brews) { // HomebrewModel.find().sort({ views: -1 }).limit(5).exec(function(err, brews) {
@@ -11,6 +12,22 @@ const Markdown = require('../shared/naturalcrit/markdown.js');
// }); // });
// }; // };
const mergeBrewText = (brew)=>{
let text = brew.text;
if(brew.style !== undefined) {
text = `\`\`\`css\n` +
`${brew.style || ''}\n` +
`\`\`\`\n\n` +
`${text}`;
}
const metadata = _.pick(brew, ['title', 'description', 'tags', 'systems', 'renderer']);
text = `\`\`\`metadata\n` +
`${yaml.dump(metadata)}\n` +
`\`\`\`\n\n` +
`${text}`;
return text;
};
const MAX_TITLE_LENGTH = 100; const MAX_TITLE_LENGTH = 100;
const getGoodBrewTitle = (text)=>{ const getGoodBrewTitle = (text)=>{
@@ -28,16 +45,6 @@ const excludePropsFromUpdate = (brew)=>{
return brew; return brew;
}; };
const mergeBrewText = (text, style)=>{
if(typeof style !== 'undefined') {
text = `\`\`\`css\n` +
`${style}\n` +
`\`\`\`\n\n` +
`${text}`;
}
return text;
};
const newBrew = (req, res)=>{ const newBrew = (req, res)=>{
const brew = req.body; const brew = req.body;
@@ -46,7 +53,7 @@ const newBrew = (req, res)=>{
} }
brew.authors = (req.account) ? [req.account.username] : []; brew.authors = (req.account) ? [req.account.username] : [];
brew.text = mergeBrewText(brew.text, brew.style); brew.text = mergeBrewText(brew);
delete brew.editId; delete brew.editId;
delete brew.shareId; delete brew.shareId;
@@ -75,7 +82,7 @@ const updateBrew = (req, res)=>{
.then((brew)=>{ .then((brew)=>{
const updateBrew = excludePropsFromUpdate(req.body); const updateBrew = excludePropsFromUpdate(req.body);
brew = _.merge(brew, updateBrew); brew = _.merge(brew, updateBrew);
brew.text = mergeBrewText(brew.text, brew.style); brew.text = mergeBrewText(brew);
// Compress brew text to binary before saving // Compress brew text to binary before saving
brew.textBin = zlib.deflateRawSync(brew.text); brew.textBin = zlib.deflateRawSync(brew.text);
@@ -143,7 +150,7 @@ const newGoogleBrew = async (req, res, next)=>{
} }
brew.authors = (req.account) ? [req.account.username] : []; brew.authors = (req.account) ? [req.account.username] : [];
brew.text = mergeBrewText(brew.text, brew.style); brew.text = mergeBrewText(brew);
delete brew.editId; delete brew.editId;
delete brew.shareId; delete brew.shareId;
@@ -165,13 +172,13 @@ const updateGoogleBrew = async (req, res, next)=>{
try { oAuth2Client = GoogleActions.authCheck(req.account, res); } catch (err) { return res.status(err.status).send(err.message); } try { oAuth2Client = GoogleActions.authCheck(req.account, res); } catch (err) { return res.status(err.status).send(err.message); }
const brew = excludePropsFromUpdate(req.body); const brew = excludePropsFromUpdate(req.body);
brew.text = mergeBrewText(brew.text, brew.style); brew.text = mergeBrewText(brew);
try { try {
const updatedBrew = await GoogleActions.updateGoogleBrew(oAuth2Client, brew); const updatedBrew = await GoogleActions.updateGoogleBrew(oAuth2Client, brew);
return res.status(200).send(updatedBrew); return res.status(200).send(updatedBrew);
} catch (err) { } catch (err) {
return res.status(err.response.status).send(err); return res.status(err.response?.status || 500).send(err);
} }
}; };

View File

@@ -1,3 +1,4 @@
/* eslint-disable max-lines */
require('./codeEditor.less'); require('./codeEditor.less');
const React = require('react'); const React = require('react');
const createClass = require('create-react-class'); const createClass = require('create-react-class');
@@ -84,20 +85,52 @@ const CodeEditor = createClass({
tabSize : 2, tabSize : 2,
historyEventDelay : 250, historyEventDelay : 250,
extraKeys : { extraKeys : {
'Ctrl-B' : this.makeBold, 'Ctrl-B' : this.makeBold,
'Cmd-B' : this.makeBold, 'Cmd-B' : this.makeBold,
'Ctrl-I' : this.makeItalic, 'Ctrl-I' : this.makeItalic,
'Cmd-I' : this.makeItalic, 'Cmd-I' : this.makeItalic,
'Ctrl-M' : this.makeSpan, 'Ctrl-U' : this.makeUnderline,
'Cmd-M' : this.makeSpan, 'Cmd-U' : this.makeUnderline,
'Ctrl-/' : this.makeComment, 'Ctrl-.' : this.makeNbsp,
'Cmd-/' : this.makeComment, 'Cmd-.' : this.makeNbsp,
'Ctrl-[' : this.foldAllCode, 'Shift-Ctrl-.' : this.makeSpace,
'Cmd-[' : this.foldAllCode, 'Shift-Cmd-.' : this.makeSpace,
'Ctrl-]' : this.unfoldAllCode, 'Shift-Ctrl-,' : this.removeSpace,
'Cmd-]' : this.unfoldAllCode, 'Shift-Cmd-,' : this.removeSpace,
'Ctrl-Alt-F' : this.findPersistent, 'Ctrl-M' : this.makeSpan,
'Cmd-Opt-F' : this.findPersistent 'Cmd-M' : this.makeSpan,
'Shift-Ctrl-M' : this.makeDiv,
'Shift-Cmd-M' : this.makeDiv,
'Ctrl-/' : this.makeComment,
'Cmd-/' : this.makeComment,
'Ctrl-K' : this.makeLink,
'Cmd-K' : this.makeLink,
'Ctrl-L' : ()=>this.makeList('UL'),
'Cmd-L' : ()=>this.makeList('UL'),
'Shift-Ctrl-L' : ()=>this.makeList('OL'),
'Shift-Cmd-L' : ()=>this.makeList('OL'),
'Shift-Ctrl-1' : ()=>this.makeHeader(1),
'Shift-Ctrl-2' : ()=>this.makeHeader(2),
'Shift-Ctrl-3' : ()=>this.makeHeader(3),
'Shift-Ctrl-4' : ()=>this.makeHeader(4),
'Shift-Ctrl-5' : ()=>this.makeHeader(5),
'Shift-Ctrl-6' : ()=>this.makeHeader(6),
'Shift-Cmd-1' : ()=>this.makeHeader(1),
'Shift-Cmd-2' : ()=>this.makeHeader(2),
'Shift-Cmd-3' : ()=>this.makeHeader(3),
'Shift-Cmd-4' : ()=>this.makeHeader(4),
'Shift-Cmd-5' : ()=>this.makeHeader(5),
'Shift-Cmd-6' : ()=>this.makeHeader(6),
'Shift-Ctrl-Enter' : this.newColumn,
'Shift-Cmd-Enter' : this.newColumn,
'Ctrl-Enter' : this.newPage,
'Cmd-Enter' : this.newPage,
'Ctrl-Alt-F' : this.findPersistent,
'Cmd-Opt-F' : this.findPersistent,
'Ctrl-[' : this.foldAllCode,
'Cmd-[' : this.foldAllCode,
'Ctrl-]' : this.unfoldAllCode,
'Cmd-]' : this.unfoldAllCode
}, },
foldGutter : true, foldGutter : true,
foldOptions : { foldOptions : {
@@ -133,6 +166,14 @@ const CodeEditor = createClass({
this.updateSize(); this.updateSize();
}, },
makeHeader : function (number) {
const selection = this.codeMirror.getSelection();
const header = Array(number).fill('#').join('');
this.codeMirror.replaceSelection(`${header} ${selection}`, 'around');
const cursor = this.codeMirror.getCursor();
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch + selection.length + number + 1 });
},
makeBold : function() { makeBold : function() {
const selection = this.codeMirror.getSelection(), t = selection.slice(0, 2) === '**' && selection.slice(-2) === '**'; const selection = this.codeMirror.getSelection(), t = selection.slice(0, 2) === '**' && selection.slice(-2) === '**';
this.codeMirror.replaceSelection(t ? selection.slice(2, -2) : `**${selection}**`, 'around'); this.codeMirror.replaceSelection(t ? selection.slice(2, -2) : `**${selection}**`, 'around');
@@ -143,14 +184,55 @@ const CodeEditor = createClass({
}, },
makeItalic : function() { makeItalic : function() {
const selection = this.codeMirror.getSelection(), t = selection.slice(0, 1) === '_' && selection.slice(-1) === '_'; const selection = this.codeMirror.getSelection(), t = selection.slice(0, 1) === '*' && selection.slice(-1) === '*';
this.codeMirror.replaceSelection(t ? selection.slice(1, -1) : `_${selection}_`, 'around'); this.codeMirror.replaceSelection(t ? selection.slice(1, -1) : `*${selection}*`, 'around');
if(selection.length === 0){ if(selection.length === 0){
const cursor = this.codeMirror.getCursor(); const cursor = this.codeMirror.getCursor();
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 1 }); this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 1 });
} }
}, },
makeNbsp : function() {
this.codeMirror.replaceSelection('&nbsp;', 'end');
},
makeSpace : function() {
const selection = this.codeMirror.getSelection();
const t = selection.slice(0, 8) === '{{width:' && selection.slice(0 -4) === '% }}';
if(t){
const percent = parseInt(selection.slice(8, -4)) + 10;
this.codeMirror.replaceSelection(percent < 90 ? `{{width:${percent}% }}` : '{{width:100% }}', 'around');
} else {
this.codeMirror.replaceSelection(`{{width:10% }}`, 'around');
}
},
removeSpace : function() {
const selection = this.codeMirror.getSelection();
const t = selection.slice(0, 8) === '{{width:' && selection.slice(0 -4) === '% }}';
if(t){
const percent = parseInt(selection.slice(8, -4)) - 10;
this.codeMirror.replaceSelection(percent > 10 ? `{{width:${percent}% }}` : '', 'around');
}
},
newColumn : function() {
this.codeMirror.replaceSelection('\n\\column\n\n', 'end');
},
newPage : function() {
this.codeMirror.replaceSelection('\n\\page\n\n', 'end');
},
makeUnderline : function() {
const selection = this.codeMirror.getSelection(), t = selection.slice(0, 3) === '<u>' && selection.slice(-4) === '</u>';
this.codeMirror.replaceSelection(t ? selection.slice(3, -4) : `<u>${selection}</u>`, 'around');
if(selection.length === 0){
const cursor = this.codeMirror.getCursor();
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 4 });
}
},
makeSpan : function() { makeSpan : function() {
const selection = this.codeMirror.getSelection(), t = selection.slice(0, 2) === '{{' && selection.slice(-2) === '}}'; const selection = this.codeMirror.getSelection(), t = selection.slice(0, 2) === '{{' && selection.slice(-2) === '}}';
this.codeMirror.replaceSelection(t ? selection.slice(2, -2) : `{{ ${selection}}}`, 'around'); this.codeMirror.replaceSelection(t ? selection.slice(2, -2) : `{{ ${selection}}}`, 'around');
@@ -160,12 +242,72 @@ const CodeEditor = createClass({
} }
}, },
makeComment : function() { makeDiv : function() {
const selection = this.codeMirror.getSelection(), t = selection.slice(0, 4) === '<!--' && selection.slice(-3) === '-->'; const selection = this.codeMirror.getSelection(), t = selection.slice(0, 2) === '{{' && selection.slice(-2) === '}}';
this.codeMirror.replaceSelection(t ? selection.slice(4, -3) : `<!-- ${selection} -->`, 'around'); this.codeMirror.replaceSelection(t ? selection.slice(2, -2) : `{{\n${selection}\n}}`, 'around');
if(selection.length === 0){ if(selection.length === 0){
const cursor = this.codeMirror.getCursor(); const cursor = this.codeMirror.getCursor();
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 4 }); this.codeMirror.setCursor({ line: cursor.line - 1, ch: cursor.ch }); // set to -2? if wanting to enter classes etc. if so, get rid of first \n when replacing selection
}
},
makeComment : function() {
let regex;
let cursorPos;
let newComment;
const selection = this.codeMirror.getSelection();
if(this.props.language === 'gfm'){
regex = /^\s*(<!--\s?)(.*?)(\s?-->)\s*$/gs;
cursorPos = 4;
newComment = `<!-- ${selection} -->`;
} else {
regex = /^\s*(\/\*\s?)(.*?)(\s?\*\/)\s*$/gs;
cursorPos = 3;
newComment = `/* ${selection} */`;
}
this.codeMirror.replaceSelection(regex.test(selection) == true ? selection.replace(regex, '$2') : newComment, 'around');
if(selection.length === 0){
const cursor = this.codeMirror.getCursor();
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - cursorPos });
};
},
makeLink : function() {
const isLink = /^\[(.*)\]\((.*)\)$/;
const selection = this.codeMirror.getSelection().trim();
let match;
if(match = isLink.exec(selection)){
const altText = match[1];
const url = match[2];
this.codeMirror.replaceSelection(`${altText} ${url}`);
const cursor = this.codeMirror.getCursor();
this.codeMirror.setSelection({ line: cursor.line, ch: cursor.ch - url.length }, { line: cursor.line, ch: cursor.ch });
} else {
this.codeMirror.replaceSelection(`[${selection || 'alt text'}](url)`);
const cursor = this.codeMirror.getCursor();
this.codeMirror.setSelection({ line: cursor.line, ch: cursor.ch - 4 }, { line: cursor.line, ch: cursor.ch - 1 });
}
},
makeList : function(listType) {
const selectionStart = this.codeMirror.getCursor('from'), selectionEnd = this.codeMirror.getCursor('to');
this.codeMirror.setSelection(
{ line: selectionStart.line, ch: 0 },
{ line: selectionEnd.line, ch: this.codeMirror.getLine(selectionEnd.line).length }
);
const newSelection = this.codeMirror.getSelection();
const regex = /^\d+\.\s|^-\s/gm;
if(newSelection.match(regex) != null){ // if selection IS A LIST
this.codeMirror.replaceSelection(newSelection.replace(regex, ''), 'around');
} else { // if selection IS NOT A LIST
listType == 'UL' ? this.codeMirror.replaceSelection(newSelection.replace(/^/gm, `- `), 'around') :
this.codeMirror.replaceSelection(newSelection.replace(/^/gm, (()=>{
let n = 1;
return ()=>{
return `${n++}. `;
};
})()), 'around');
} }
}, },

View File

@@ -79,7 +79,7 @@ body {
p{ p{
overflow-wrap : break-word; //TODO: MAKE ALL MARGINS TOP-ONLY. USE * + * STYLE SELECTORS overflow-wrap : break-word; //TODO: MAKE ALL MARGINS TOP-ONLY. USE * + * STYLE SELECTORS
display : block; display : block;
line-height : 1.3em; line-height : 1.25em;
&+* { &+* {
margin-top : 0.325cm; margin-top : 0.325cm;
} }
@@ -90,14 +90,14 @@ body {
ul{ ul{
margin-bottom : 0.8em; margin-bottom : 0.8em;
padding-left : 1.4em; padding-left : 1.4em;
line-height : 1.3em; line-height : 1.25em;
list-style-position : outside; list-style-position : outside;
list-style-type : disc; list-style-type : disc;
} }
ol{ ol{
margin-bottom : 0.8em; margin-bottom : 0.8em;
padding-left : 1.4em; padding-left : 1.4em;
line-height : 1.3em; line-height : 1.25em;
list-style-position : outside; list-style-position : outside;
list-style-type : decimal; list-style-type : decimal;
} }
@@ -162,17 +162,20 @@ body {
//margin-top : 0px; //Font is misaligned. Shift up slightly //margin-top : 0px; //Font is misaligned. Shift up slightly
//margin-bottom : 0.05cm; //margin-bottom : 0.05cm;
font-size : 0.75cm; font-size : 0.75cm;
line-height : 0.988em; //Font is misaligned. Shift up slightly
} }
h3{ h3{
//margin-top : -0.1cm; //Font is misaligned. Shift up slightly //margin-top : -0.1cm; //Font is misaligned. Shift up slightly
//margin-bottom : 0.1cm; //margin-bottom : 0.1cm;
font-size : 0.575cm; font-size : 0.575cm;
border-bottom : 2px solid @headerUnderline; border-bottom : 2px solid @headerUnderline;
line-height : 0.995em; //Font is misaligned. Shift up slightly
} }
h4{ h4{
//margin-top : -0.02cm; //Font is misaligned. Shift up slightly //margin-top : -0.02cm; //Font is misaligned. Shift up slightly
//margin-bottom : 0.02cm; //margin-bottom : 0.02cm;
font-size : 0.458cm; font-size : 0.458cm;
line-height : 0.971em; //Font is misaligned. Shift up slightly
} }
h5{ h5{
//margin-top : -0.02cm; //Font is misaligned. Shift up slightly //margin-top : -0.02cm; //Font is misaligned. Shift up slightly
@@ -180,6 +183,7 @@ body {
font-family : ScalySansSmallCapsRemake; font-family : ScalySansSmallCapsRemake;
font-size : 0.423cm; font-size : 0.423cm;
font-weight : 900; font-weight : 900;
line-height : 0.951em; //Font is misaligned. Shift up slightly
& + * { & + * {
margin-top : 0.2cm; margin-top : 0.2cm;
} }
@@ -238,9 +242,6 @@ body {
display : block; display : block;
padding-bottom : 0px; padding-bottom : 0px;
} }
p + p {
padding-top : .8em;
}
:last-child { :last-child {
margin-bottom : 0; margin-bottom : 0;
} }
@@ -271,9 +272,6 @@ body {
padding-bottom : 0px; padding-bottom : 0px;
line-height : 1.5em; line-height : 1.5em;
} }
p + p {
padding-top : .8em;
}
:last-child { :last-child {
margin-bottom : 0; margin-bottom : 0;
} }
@@ -513,7 +511,8 @@ body {
color : #58180d; color : #58180d;
background-color : #faf7ea; background-color : #faf7ea;
border-radius : 4px; border-radius : 4px;
white-space : pre-wrap white-space : pre-wrap;
overflow-wrap : break-word;
} }
pre code{ pre code{
@@ -581,7 +580,7 @@ body {
} }
p, ul{ p, ul{
font-size : 0.352cm; font-size : 0.352cm;
line-height : 1.3em; line-height : 1.265em;
} }
ul{ ul{
margin-bottom : 0.5em; margin-bottom : 0.5em;
@@ -741,7 +740,7 @@ body {
// *****************************/ // *****************************/
.page { .page {
dl { dl {
line-height : 1.3em; line-height : 1.25em;
padding-left : 1em; padding-left : 1em;
white-space : pre-line; white-space : pre-line;
& + * { & + * {

View File

@@ -63,7 +63,7 @@ body {
// *****************************/ // *****************************/
p{ p{
padding-bottom : 0.8em; padding-bottom : 0.8em;
line-height : 1.3em; line-height : 1.269em;
&+p{ &+p{
margin-top : -0.8em; margin-top : -0.8em;
} }
@@ -71,14 +71,14 @@ body {
ul{ ul{
margin-bottom : 0.8em; margin-bottom : 0.8em;
padding-left : 1.4em; padding-left : 1.4em;
line-height : 1.3em; line-height : 1.269em;
list-style-position : outside; list-style-position : outside;
list-style-type : disc; list-style-type : disc;
} }
ol{ ol{
margin-bottom : 0.8em; margin-bottom : 0.8em;
padding-left : 1.4em; padding-left : 1.4em;
line-height : 1.3em; line-height : 1.269em;
list-style-position : outside; list-style-position : outside;
list-style-type : decimal; list-style-type : decimal;
} }
@@ -126,7 +126,7 @@ body {
font-family : Solberry; font-family : Solberry;
font-size : 10em; font-size : 10em;
color : #222; color : #222;
line-height : 0.8em; line-height : 0.795em;
} }
} }
h2{ h2{
@@ -191,7 +191,7 @@ body {
box-shadow : 1px 4px 14px #888; box-shadow : 1px 4px 14px #888;
p, ul{ p, ul{
font-size : 0.352cm; font-size : 0.352cm;
line-height : 1.1em; line-height : 1.083em;
} }
} }
//If a note starts a column, give it space at the top to render border //If a note starts a column, give it space at the top to render border
@@ -371,7 +371,7 @@ body {
} }
p, ul{ p, ul{
font-size : 0.352cm; font-size : 0.352cm;
line-height : 1.3em; line-height : 1.263em;
} }
ul{ ul{
margin-bottom : 0.5em; margin-bottom : 0.5em;
@@ -425,7 +425,7 @@ body {
p{ p{
display : block; display : block;
padding-bottom : 0px; padding-bottom : 0px;
line-height : 1.5em; line-height : 1.47em;
} }
p + p { p + p {
padding-top : .8em; padding-top : .8em;
@@ -457,7 +457,7 @@ body {
p, p + p { p, p + p {
margin : unset; margin : unset;
text-indent : unset; text-indent : unset;
line-height : 1em; line-height : 0.941em;
} }
h5 { h5 {
font-size : 1.3em; font-size : 1.3em;