Merge branch 'borderShadows' into v3
@@ -2,7 +2,12 @@ var React = require('react');
|
||||
var Nav = require('naturalcrit/nav/nav.jsx');
|
||||
|
||||
module.exports = function(props){
|
||||
return <Nav.item newTab={true} href='https://github.com/stolksdorf/homebrewery/issues' color='red' icon='fa-bug'>
|
||||
return <Nav.item
|
||||
{...props}
|
||||
newTab={true}
|
||||
href='https://github.com/stolksdorf/homebrewery/issues'
|
||||
color='red'
|
||||
icon='fa-bug'>
|
||||
report issue
|
||||
</Nav.item>
|
||||
};
|
||||
@@ -3,6 +3,7 @@ var Nav = require('naturalcrit/nav/nav.jsx');
|
||||
|
||||
module.exports = function(props){
|
||||
return <Nav.item
|
||||
{...props}
|
||||
className='patreon'
|
||||
newTab={true}
|
||||
href='https://www.patreon.com/stolksdorf'
|
||||
|
||||
@@ -8,6 +8,9 @@ var Nav = require('naturalcrit/nav/nav.jsx');
|
||||
const VIEW_KEY = 'homebrewery-recently-viewed';
|
||||
const EDIT_KEY = 'homebrewery-recently-edited';
|
||||
|
||||
//DEPRICATED
|
||||
|
||||
|
||||
var BaseItem = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
@@ -28,6 +31,8 @@ var BaseItem = React.createClass({
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
console.log('Recent nav item is depricated');
|
||||
|
||||
var brews = JSON.parse(localStorage.getItem(this.props.storageKey) || '[]');
|
||||
|
||||
brews = _.filter(brews, (brew)=>{
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
//TODO: Depricate
|
||||
|
||||
module.exports = function(shareId){
|
||||
return function(event){
|
||||
event = event || window.event;
|
||||
if((event.ctrlKey || event.metaKey) && event.keyCode == 80){
|
||||
var win = window.open(`/homebrew/print/${shareId}?dialog=true`, '_blank');
|
||||
win.focus();
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -24,10 +24,10 @@ const HomePage = React.createClass({
|
||||
renderNavbar : function(){
|
||||
return <Navbar>
|
||||
<Nav.section>
|
||||
<PatreonNavItem />
|
||||
<IssueNavItem />
|
||||
<Nav.item newTab={true} href='/changelog' color='purple' icon='fa-file-text-o'>
|
||||
Changelog
|
||||
<PatreonNavItem collaspe={true} />
|
||||
<IssueNavItem collaspe={true} />
|
||||
<Nav.item newTab={true} href='/changelog' color='purple' icon='fa-star' collaspe={true}>
|
||||
What's new
|
||||
</Nav.item>
|
||||
<RecentNavItem.both />
|
||||
<AccountNavItem />
|
||||
|
||||
@@ -47,7 +47,7 @@ const NewPage = React.createClass({
|
||||
<Nav.item color='purple' icon='fa-file-pdf-o' onClick={Actions.localPrint}>
|
||||
get PDF
|
||||
</Nav.item>
|
||||
<Items.Issue />
|
||||
<Items.Issue collaspe={true} />
|
||||
<Items.Account />
|
||||
</Nav.section>
|
||||
</Navbar>
|
||||
|
||||
@@ -3,38 +3,62 @@ const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
const Markdown = require('homebrewery/markdown.js');
|
||||
|
||||
const Headtags = require('vitreum/headtags');
|
||||
|
||||
const PrintPage = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
query : {},
|
||||
brew : {
|
||||
text : '',
|
||||
style : ''
|
||||
}
|
||||
};
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {
|
||||
brewText: this.props.brew.text
|
||||
brew: this.props.brew
|
||||
};
|
||||
},
|
||||
componentDidMount: function() {
|
||||
if(this.props.query.local){
|
||||
this.setState({ brewText : localStorage.getItem(this.props.query.local)});
|
||||
try{
|
||||
this.setState({
|
||||
brew : JSON.parse(
|
||||
localStorage.getItem(this.props.query.local)
|
||||
)
|
||||
});
|
||||
}catch(e){}
|
||||
}
|
||||
if(this.props.query.dialog) window.print();
|
||||
},
|
||||
//TODO: Print page shouldn't replicate functionality in brew renderer
|
||||
renderStyle : function(){
|
||||
if(!this.state.brew.style) return;
|
||||
return <style>{this.state.brew.style.replace(/;/g, ' !important;')}</style>
|
||||
},
|
||||
renderPages : function(){
|
||||
return _.map(this.state.brewText.split('\\page'), (page, index) => {
|
||||
return _.map(this.state.brew.text.split('\\page'), (page, index) => {
|
||||
return <div
|
||||
className='phb'
|
||||
className='phb v2'
|
||||
id={`p${index + 1}`}
|
||||
dangerouslySetInnerHTML={{__html:Markdown.render(page)}}
|
||||
key={index} />;
|
||||
});
|
||||
},
|
||||
|
||||
renderPrintInstructions : function(){
|
||||
return <div className='printInstructions'>
|
||||
Hey, I'm really cool instructions!!!!!
|
||||
|
||||
</div>
|
||||
},
|
||||
|
||||
render : function(){
|
||||
return <div>
|
||||
return <div className='printPage'>
|
||||
<Headtags.title>{this.state.brew.title}</Headtags.title>
|
||||
{this.renderPrintInstructions()}
|
||||
{this.renderStyle()}
|
||||
{this.renderPages()}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
.printPage{
|
||||
|
||||
.printPage{
|
||||
position : relative;
|
||||
@media print{
|
||||
.printInstructions{
|
||||
display : none;
|
||||
}
|
||||
}
|
||||
.printInstructions{
|
||||
position : absolute;
|
||||
top : 0px;
|
||||
right : 0px;
|
||||
z-index : 100000;
|
||||
padding : 30px;
|
||||
background-color : @blue;
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,12 @@
|
||||
"dev": "node scripts/dev.js",
|
||||
"quick": "node scripts/quick.js",
|
||||
"build": "node scripts/build.js",
|
||||
"phb": "node scripts/phb.js",
|
||||
"populate": "node scripts/populate.js",
|
||||
"prod": "set NODE_ENV=production&& npm run build",
|
||||
"postinstall": "npm run build",
|
||||
"start": "node server.js",
|
||||
"snippet": "nodemon scripts/snippet.test.js",
|
||||
"todo": "./node_modules/.bin/fixme -i node_modules/** -i build/**",
|
||||
"test": "mocha tests",
|
||||
"test:dev": "nodemon -x mocha tests || exit 0",
|
||||
"test:markdown": "nodemon -x mocha tests/markdown.test.js || exit 0"
|
||||
@@ -46,6 +47,7 @@
|
||||
"chai": "^3.5.0",
|
||||
"chai-as-promised": "^6.0.0",
|
||||
"chai-subset": "^1.4.0",
|
||||
"fixme": "^0.4.3",
|
||||
"mocha": "^3.2.0",
|
||||
"supertest": "^2.0.1",
|
||||
"supertest-as-promised": "^4.0.2"
|
||||
|
||||
@@ -2,19 +2,20 @@ const label = 'build';
|
||||
console.time(label);
|
||||
|
||||
const clean = require('vitreum/steps/clean.js');
|
||||
const jsx = require('vitreum/steps/jsx.js').partial;
|
||||
const lib = require('vitreum/steps/libs.js').partial;
|
||||
const less = require('vitreum/steps/less.js').partial;
|
||||
const asset = require('vitreum/steps/assets.js').partial;
|
||||
const jsx = require('vitreum/steps/jsx.js');
|
||||
const lib = require('vitreum/steps/libs.js');
|
||||
const less = require('vitreum/steps/less.js');
|
||||
const asset = require('vitreum/steps/assets.js');
|
||||
|
||||
const Proj = require('./project.json');
|
||||
|
||||
clean()
|
||||
.then(lib(Proj.libs))
|
||||
.then(jsx('homebrew', './client/homebrew/homebrew.jsx', Proj.libs, ['./shared']))
|
||||
.then(less('homebrew', ['./shared']))
|
||||
.then(jsx('admin', './client/admin/admin.jsx', Proj.libs, ['./shared']))
|
||||
.then(less('admin', ['./shared']))
|
||||
.then(asset(Proj.assets, ['./shared', './client']))
|
||||
.then(console.timeEnd.bind(console, label))
|
||||
Promise.resolve()
|
||||
.then(()=>clean())
|
||||
.then(()=>lib(Proj.libs))
|
||||
.then(()=>jsx('homebrew', './client/homebrew/homebrew.jsx', Proj.libs, ['./shared']))
|
||||
.then((deps)=>less('homebrew', ['./shared'], deps))
|
||||
.then(()=>jsx('admin', './client/admin/admin.jsx', Proj.libs, ['./shared']))
|
||||
.then((deps)=>less('admin', ['./shared'], deps))
|
||||
.then(()=>asset(Proj.assets, ['./shared', './client']))
|
||||
.then(()=>console.timeEnd.bind(console, label))
|
||||
.catch(console.error);
|
||||
@@ -1,21 +1,21 @@
|
||||
const label = 'dev';
|
||||
console.time(label);
|
||||
|
||||
const jsx = require('vitreum/steps/jsx.watch.js').partial;
|
||||
const less = require('vitreum/steps/less.watch.js').partial;
|
||||
const assets = require('vitreum/steps/assets.watch.js').partial;
|
||||
const server = require('vitreum/steps/server.watch.js').partial;
|
||||
const livereload = require('vitreum/steps/livereload.js').partial;
|
||||
const jsx = require('vitreum/steps/jsx.watch.js');
|
||||
const less = require('vitreum/steps/less.watch.js');
|
||||
const assets = require('vitreum/steps/assets.watch.js');
|
||||
const server = require('vitreum/steps/server.watch.js');
|
||||
const livereload = require('vitreum/steps/livereload.js');
|
||||
|
||||
const Proj = require('./project.json');
|
||||
|
||||
Promise.resolve()
|
||||
.then(jsx('homebrew', './client/homebrew/homebrew.jsx', Proj.libs, './shared'))
|
||||
.then(less('homebrew', './shared'))
|
||||
.then(jsx('admin', './client/admin/admin.jsx', Proj.libs, './shared'))
|
||||
.then(less('admin', './shared'))
|
||||
.then(assets(Proj.assets, ['./shared', './client']))
|
||||
.then(livereload())
|
||||
.then(server('./server.js', ['server']))
|
||||
.then(console.timeEnd.bind(console, label))
|
||||
.then(()=>jsx('homebrew', './client/homebrew/homebrew.jsx', Proj.libs, './shared'))
|
||||
.then((deps)=>less('homebrew', './shared', deps))
|
||||
.then(()=>jsx('admin', './client/admin/admin.jsx', Proj.libs, './shared'))
|
||||
.then((deps)=>less('admin', './shared', deps))
|
||||
.then(()=>assets(Proj.assets, ['./shared', './client']))
|
||||
.then(()=>livereload())
|
||||
.then(()=>server('./server.js', ['server']))
|
||||
.then(()=>console.timeEnd.bind(console, label))
|
||||
.catch(console.error)
|
||||
8
scripts/notes.js
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
require('fixme')({
|
||||
path: process.cwd(),
|
||||
ignored_directories: ['node_modules/**', '.git/**', 'build/**'],
|
||||
file_patterns: ['**/*.js', '**/*.jsx', '**/*.less'],
|
||||
file_encoding: 'utf8',
|
||||
line_length_limit: 200
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
//DEPRICATE
|
||||
|
||||
const less = require('less');
|
||||
const fs = require('fs');
|
||||
|
||||
|
||||
console.log('you should not b using this');
|
||||
|
||||
|
||||
less.render(fs.readFileSync('./client/homebrew/phbStyle/phb.style.less', 'utf8'), {compress : true})
|
||||
.then((output) => {
|
||||
fs.writeFileSync('./phb.standalone.css', output.css);
|
||||
console.log('phb.standalone.css created!');
|
||||
}, (err) => {
|
||||
console.error(err);
|
||||
});
|
||||
8
scripts/snippet.test.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const snippets = require('../shared/homebrewery/snippets');
|
||||
|
||||
console.log(snippets);
|
||||
|
||||
//console.log(snippets.brew.spell());
|
||||
//console.log(snippets.brew.table());
|
||||
|
||||
console.log(snippets.brew.noncasterTable());
|
||||
@@ -8,10 +8,11 @@ const mw = require('./middleware.js');
|
||||
|
||||
|
||||
const statics = {
|
||||
welcomeBrew : fs.readFileSync('./welcome.brew.md', 'utf8'),
|
||||
changelog : fs.readFileSync('./changelog.md', 'utf8'),
|
||||
testBrew : fs.readFileSync('./statics/test.brew.md', 'utf8'),
|
||||
welcomeBrew : fs.readFileSync('./statics/welcome.brew.md', 'utf8'),
|
||||
changelog : fs.readFileSync('./statics/changelog.md', 'utf8'),
|
||||
faq : fs.readFileSync('./statics/faq.md', 'utf8'),
|
||||
|
||||
testBrew : fs.readFileSync('./statics/test.brew.md', 'utf8'),
|
||||
oldTest : fs.readFileSync('./statics/oldTest.brew.md', 'utf8'),
|
||||
};
|
||||
|
||||
@@ -44,6 +45,7 @@ router.get('/edit/:editId', mw.loadBrew, renderPage);
|
||||
|
||||
//Print Page
|
||||
router.get('/print/:shareId', mw.viewBrew, renderPage);
|
||||
router.get('/print', renderPage);
|
||||
|
||||
//Source page
|
||||
router.get('/source/:sharedId', mw.viewBrew, (req, res, next)=>{
|
||||
@@ -81,6 +83,17 @@ router.get('/changelog', (req, res, next) => {
|
||||
return next();
|
||||
}, renderPage);
|
||||
|
||||
//faq Page
|
||||
router.get('/faq', (req, res, next) => {
|
||||
req.brew = {
|
||||
text : statics.faq,
|
||||
title : 'FAQ',
|
||||
|
||||
editId : true
|
||||
};
|
||||
return next();
|
||||
}, renderPage);
|
||||
|
||||
//New Page
|
||||
router.get('/new', renderPage);
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ const getTOC = (pages) => {
|
||||
}
|
||||
|
||||
module.exports = function(brew){
|
||||
const pages = brew.split('\\page');
|
||||
|
||||
const TOC = getTOC(pages);
|
||||
const markdown = _.reduce(TOC, (r, g1, idx1)=>{
|
||||
r.push(`- **[${idx1 + 1} ${g1.title}](#p${g1.page})**`)
|
||||
@@ -70,8 +70,9 @@ const Actions = {
|
||||
},
|
||||
|
||||
localPrint : ()=>{
|
||||
localStorage.setItem('print', Store.getBrewText());
|
||||
window.open('/print?dialog=true&local=print','_blank');
|
||||
const key = 'print';
|
||||
localStorage.setItem(key, JSON.stringify(Store.getBrew()));
|
||||
window.open(`/print?dialog=true&local=${key}`,'_blank');
|
||||
},
|
||||
print : ()=>{
|
||||
window.open(`/print/${Store.getBrew().shareId}?dialog=true`, '_blank').focus();
|
||||
|
||||
@@ -3,9 +3,7 @@ const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
|
||||
const CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx');
|
||||
const SnippetBar = require('./snippetbar/snippetbar.jsx');
|
||||
const MetadataEditor = require('./metadataEditor/metadataEditor.jsx');
|
||||
|
||||
const Menubar = require('./menubar/menubar.jsx');
|
||||
|
||||
const splice = function(str, index, inject){
|
||||
@@ -32,6 +30,10 @@ const BrewEditor = React.createClass({
|
||||
view : 'code', //'code', 'style', 'meta'
|
||||
};
|
||||
},
|
||||
isCode : function(){ return this.state.view == 'code' },
|
||||
isStyle : function(){ return this.state.view == 'style' },
|
||||
isMeta : function(){ return this.state.view == 'meta' },
|
||||
|
||||
|
||||
componentDidMount: function() {
|
||||
this.updateEditorSize();
|
||||
@@ -53,11 +55,16 @@ const BrewEditor = React.createClass({
|
||||
|
||||
|
||||
handleInject : function(injectText){
|
||||
const lines = this.props.value.split('\n');
|
||||
lines[this.cursorPosition.line] = splice(lines[this.cursorPosition.line], this.cursorPosition.ch, injectText);
|
||||
const text = (this.isCode() ? this.props.brew.text : this.props.brew.style);
|
||||
|
||||
this.handleTextChange(lines.join('\n'));
|
||||
this.refs.codeEditor.setCursorPosition(this.cursorPosition.line, this.cursorPosition.ch + injectText.length);
|
||||
const lines = text.split('\n');
|
||||
const cursorPos = this.refs.codeEditor.getCursorPosition();
|
||||
lines[cursorPos.line] = splice(lines[cursorPos.line], cursorPos.ch, injectText);
|
||||
|
||||
this.refs.codeEditor.setCursorPosition(cursorPos.line, cursorPos.ch + injectText.length);
|
||||
|
||||
if(this.state.view == 'code') this.props.onCodeChange(lines.join('\n'));
|
||||
if(this.state.view == 'style') this.props.onStyleChange(lines.join('\n'));
|
||||
},
|
||||
|
||||
|
||||
@@ -87,6 +94,9 @@ const BrewEditor = React.createClass({
|
||||
//MOve this to a util.sj file
|
||||
highlightPageLines : function(){
|
||||
if(!this.refs.codeEditor) return;
|
||||
if(!this.isCode()) return;
|
||||
|
||||
|
||||
const codeMirror = this.refs.codeEditor.codeMirror;
|
||||
|
||||
const lineNumbers = _.reduce(this.props.brew.text.split('\n'), (r, line, lineNumber)=>{
|
||||
@@ -95,6 +105,11 @@ const BrewEditor = React.createClass({
|
||||
r.push(lineNumber);
|
||||
}
|
||||
|
||||
if(line.indexOf('\\column') === 0){
|
||||
codeMirror.addLineClass(lineNumber, 'text', 'columnSplit');
|
||||
r.push(lineNumber);
|
||||
}
|
||||
|
||||
if(_.startsWith(line, '{{') || _.startsWith(line, '}}')){
|
||||
codeMirror.addLineClass(lineNumber, 'text', 'block');
|
||||
}
|
||||
@@ -116,19 +131,19 @@ const BrewEditor = React.createClass({
|
||||
|
||||
|
||||
renderEditor : function(){
|
||||
if(this.state.view == 'meta'){
|
||||
if(this.isMeta()){
|
||||
return <MetadataEditor
|
||||
metadata={this.props.brew}
|
||||
onChange={this.props.onMetaChange} />
|
||||
}
|
||||
if(this.state.view == 'style'){
|
||||
if(this.isStyle()){
|
||||
return <CodeEditor key='style'
|
||||
ref='codeEditor'
|
||||
language='css'
|
||||
value={this.props.brew.style}
|
||||
onChange={this.props.onStyleChange} />
|
||||
}
|
||||
if(this.state.view == 'code'){
|
||||
if(this.isCode()){
|
||||
return <CodeEditor key='code'
|
||||
ref='codeEditor'
|
||||
language='gfm'
|
||||
@@ -140,16 +155,10 @@ const BrewEditor = React.createClass({
|
||||
render : function(){
|
||||
this.highlightPageLines();
|
||||
return <div className='brewEditor' ref='main'>
|
||||
{/*
|
||||
<SnippetBar
|
||||
brew={this.props.value}
|
||||
onInject={this.handleInject}
|
||||
onToggle={this.handgleToggle}
|
||||
showmeta={this.state.showMetadataEditor} />
|
||||
*/}
|
||||
<Menubar
|
||||
view={this.state.view}
|
||||
onViewChange={this.handleViewChange}
|
||||
onSnippetInject={this.handleInject}
|
||||
|
||||
/>
|
||||
|
||||
|
||||
@@ -9,9 +9,13 @@
|
||||
border-bottom : #333 solid 1px;
|
||||
}
|
||||
.block{
|
||||
color : blue;
|
||||
color : purple;
|
||||
//font-style: italic;
|
||||
}
|
||||
.columnSplit{
|
||||
font-style : italic;
|
||||
color : grey;
|
||||
}
|
||||
}
|
||||
|
||||
.brewJump{
|
||||
|
||||
@@ -1,19 +1,60 @@
|
||||
|
||||
const React = require('react');
|
||||
const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
|
||||
const SnippetMap = require('./snippet.map.js');
|
||||
const SnippetGroup = require('./snippetGroup/snippetGroup.jsx');
|
||||
|
||||
const Menubar = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
view : '',
|
||||
view : 'code',
|
||||
onViewChange : ()=>{},
|
||||
onSnippetInject : ()=>{},
|
||||
};
|
||||
},
|
||||
|
||||
//TODO: remove
|
||||
renderDevGroup : function(){
|
||||
const Snippets = require('homebrewery/snippets/brew');
|
||||
|
||||
const snippets = _.map(Snippets, (gen, name)=>{
|
||||
return {
|
||||
name,
|
||||
gen,
|
||||
icon : 'fa-question'
|
||||
}
|
||||
})
|
||||
|
||||
return <SnippetGroup
|
||||
name='All'
|
||||
icon='fa-rocket'
|
||||
snippets={snippets}
|
||||
onClick={this.props.onSnippetInject}
|
||||
key='dev'
|
||||
/>
|
||||
},
|
||||
|
||||
renderSnippets : function(){
|
||||
if(this.props.view == 'meta') return ;
|
||||
|
||||
let mapping;
|
||||
if(this.props.view == 'code') mapping = SnippetMap.brew;
|
||||
if(this.props.view == 'style') mapping = SnippetMap.style;
|
||||
|
||||
let groups = _.map(mapping, (group)=>{
|
||||
return <SnippetGroup {...group} onClick={this.props.onSnippetInject} key={group.name} />
|
||||
});
|
||||
|
||||
groups = groups.concat(this.renderDevGroup());
|
||||
|
||||
return <div className='snippets'>{groups} </div>
|
||||
},
|
||||
render: function(){
|
||||
return <div className='menubar'>
|
||||
|
||||
{this.renderSnippets()}
|
||||
|
||||
<div className='editors'>
|
||||
<div className={cx('code', {selected : this.props.view == 'code'})}
|
||||
onClick={this.props.onViewChange.bind(null, 'code')}>
|
||||
|
||||
@@ -32,4 +32,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.snippets{
|
||||
display : flex;
|
||||
height : 100%;
|
||||
}
|
||||
}
|
||||
48
shared/homebrewery/brewEditor/menubar/snippet.map.js
Normal file
@@ -0,0 +1,48 @@
|
||||
const Snippets = require('homebrewery/snippets');
|
||||
|
||||
module.exports = {
|
||||
brew : [
|
||||
{
|
||||
name : 'PHB',
|
||||
icon : 'fa-book',
|
||||
snippets : [
|
||||
{
|
||||
name : 'Spell',
|
||||
icon : 'fa-magic',
|
||||
gen : Snippets.brew.spell
|
||||
},
|
||||
{
|
||||
name : 'Table',
|
||||
icon : 'fa-table',
|
||||
gen : Snippets.brew.table
|
||||
},
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
name : 'Mods',
|
||||
icon : 'fa-gear',
|
||||
snippets : []
|
||||
}
|
||||
],
|
||||
|
||||
style : [
|
||||
{
|
||||
name : 'Print',
|
||||
icon : 'fa-print',
|
||||
snippets : [
|
||||
{
|
||||
name : 'Ink Friendly',
|
||||
icon : 'fa-tint',
|
||||
gen : Snippets.style.inkFriendly
|
||||
},
|
||||
{
|
||||
name : 'A4 Page Size',
|
||||
icon : 'fa-file',
|
||||
gen : Snippets.style.a4
|
||||
},
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
const React = require('react');
|
||||
const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
|
||||
const SnippetGroup = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
name : '',
|
||||
icon : 'fa-rocket',
|
||||
snippets : [],
|
||||
onClick : function(){},
|
||||
};
|
||||
},
|
||||
handleSnippetClick : function(snippet){
|
||||
this.props.onClick(snippet.gen());
|
||||
},
|
||||
renderSnippets : function(){
|
||||
return _.map(this.props.snippets, (snippet)=>{
|
||||
return <div className='snippet' key={snippet.name} onClick={this.handleSnippetClick.bind(this, snippet)}>
|
||||
<i className={'fa fa-fw ' + snippet.icon} />
|
||||
{snippet.name}
|
||||
</div>
|
||||
})
|
||||
},
|
||||
|
||||
render : function(){
|
||||
return <div className='snippetGroup'>
|
||||
<div className='text'>
|
||||
<i className={'fa fa-fw ' + this.props.icon} />
|
||||
<span className='groupName'>{this.props.name}</span>
|
||||
</div>
|
||||
<div className='dropdown'>
|
||||
{this.renderSnippets()}
|
||||
</div>
|
||||
</div>
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
module.exports = SnippetGroup;
|
||||
@@ -0,0 +1,56 @@
|
||||
.snippetGroup{
|
||||
//display : inline-block;
|
||||
display : flex;
|
||||
height : 100%;
|
||||
align-items : center;
|
||||
|
||||
//height : @menuHeight;
|
||||
padding : 0px 5px;
|
||||
cursor : pointer;
|
||||
font-size : 0.6em;
|
||||
font-weight : 800;
|
||||
///line-height : @menuHeight;
|
||||
text-transform : uppercase;
|
||||
border-right : 1px solid black;
|
||||
i{
|
||||
vertical-align : middle;
|
||||
margin-right : 3px;
|
||||
font-size : 1.2em;
|
||||
}
|
||||
&:hover, &.selected{
|
||||
background-color : #999;
|
||||
}
|
||||
.text{
|
||||
//line-height : @menuHeight;
|
||||
.groupName{
|
||||
font-size : 10px;
|
||||
}
|
||||
}
|
||||
&:hover{
|
||||
.dropdown{
|
||||
visibility : visible;
|
||||
}
|
||||
}
|
||||
.dropdown{
|
||||
position : absolute;
|
||||
top : 100%;
|
||||
visibility : hidden;
|
||||
z-index : 1000;
|
||||
margin-left : -5px;
|
||||
padding : 0px;
|
||||
background-color : #ddd;
|
||||
.snippet{
|
||||
.animate(background-color);
|
||||
padding : 10px;
|
||||
cursor : pointer;
|
||||
font-size : 10px;
|
||||
i{
|
||||
margin-right : 8px;
|
||||
font-size : 13px;
|
||||
}
|
||||
&:hover{
|
||||
background-color : #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
const React = require('react');
|
||||
const _ = require('lodash');
|
||||
const cx = require('classnames');
|
||||
|
||||
|
||||
const Snippets = require('./snippets/snippets.js');
|
||||
|
||||
const execute = function(val, brew){
|
||||
if(_.isFunction(val)) return val(brew);
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const Snippetbar = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
brew : '',
|
||||
onInject : ()=>{},
|
||||
onToggle : ()=>{},
|
||||
showmeta : false
|
||||
};
|
||||
},
|
||||
|
||||
handleSnippetClick : function(injectedText){
|
||||
this.props.onInject(injectedText)
|
||||
},
|
||||
|
||||
renderSnippetGroups : function(){
|
||||
return _.map(Snippets, (snippetGroup)=>{
|
||||
return <SnippetGroup
|
||||
brew={this.props.brew}
|
||||
groupName={snippetGroup.groupName}
|
||||
icon={snippetGroup.icon}
|
||||
snippets={snippetGroup.snippets}
|
||||
key={snippetGroup.groupName}
|
||||
onSnippetClick={this.handleSnippetClick}
|
||||
/>
|
||||
})
|
||||
},
|
||||
|
||||
render : function(){
|
||||
return <div className='snippetBar'>
|
||||
{this.renderSnippetGroups()}
|
||||
<div className={cx('toggleMeta', {selected: this.props.showmeta})}
|
||||
onClick={this.props.onToggle}>
|
||||
<i className='fa fa-bars' />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Snippetbar;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const SnippetGroup = React.createClass({
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
brew : '',
|
||||
groupName : '',
|
||||
icon : 'fa-rocket',
|
||||
snippets : [],
|
||||
onSnippetClick : function(){},
|
||||
};
|
||||
},
|
||||
handleSnippetClick : function(snippet){
|
||||
this.props.onSnippetClick(execute(snippet.gen, this.props.brew));
|
||||
},
|
||||
renderSnippets : function(){
|
||||
return _.map(this.props.snippets, (snippet)=>{
|
||||
return <div className='snippet' key={snippet.name} onClick={this.handleSnippetClick.bind(this, snippet)}>
|
||||
<i className={'fa fa-fw ' + snippet.icon} />
|
||||
{snippet.name}
|
||||
</div>
|
||||
})
|
||||
},
|
||||
|
||||
render : function(){
|
||||
return <div className='snippetGroup'>
|
||||
<div className='text'>
|
||||
<i className={'fa fa-fw ' + this.props.icon} />
|
||||
<span className='groupName'>{this.props.groupName}</span>
|
||||
</div>
|
||||
<div className='dropdown'>
|
||||
{this.renderSnippets()}
|
||||
</div>
|
||||
</div>
|
||||
},
|
||||
|
||||
});
|
||||
@@ -1,72 +0,0 @@
|
||||
|
||||
.snippetBar{
|
||||
@height : 25px;
|
||||
position : relative;
|
||||
height : @height;
|
||||
background-color : #ddd;
|
||||
.toggleMeta{
|
||||
position : absolute;
|
||||
top : 0px;
|
||||
right : 0px;
|
||||
height : @height;
|
||||
width : @height;
|
||||
cursor : pointer;
|
||||
line-height : @height;
|
||||
text-align : center;
|
||||
&:hover, &.selected{
|
||||
background-color : #999;
|
||||
}
|
||||
}
|
||||
.snippetGroup{
|
||||
display : inline-block;
|
||||
height : @height;
|
||||
padding : 0px 5px;
|
||||
cursor : pointer;
|
||||
font-size : 0.6em;
|
||||
font-weight : 800;
|
||||
line-height : @height;
|
||||
text-transform : uppercase;
|
||||
border-right : 1px solid black;
|
||||
i{
|
||||
vertical-align : middle;
|
||||
margin-right : 3px;
|
||||
font-size : 1.2em;
|
||||
}
|
||||
&:hover, &.selected{
|
||||
background-color : #999;
|
||||
}
|
||||
.text{
|
||||
line-height : @height;
|
||||
.groupName{
|
||||
font-size : 10px;
|
||||
}
|
||||
}
|
||||
&:hover{
|
||||
.dropdown{
|
||||
visibility : visible;
|
||||
}
|
||||
}
|
||||
.dropdown{
|
||||
position : absolute;
|
||||
top : 100%;
|
||||
visibility : hidden;
|
||||
z-index : 1000;
|
||||
margin-left : -5px;
|
||||
padding : 0px;
|
||||
background-color : #ddd;
|
||||
.snippet{
|
||||
.animate(background-color);
|
||||
padding : 5px;
|
||||
cursor : pointer;
|
||||
font-size : 10px;
|
||||
i{
|
||||
margin-right : 8px;
|
||||
font-size : 13px;
|
||||
}
|
||||
&:hover{
|
||||
background-color : #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,9 @@ const BrewRenderer = require('../brewRenderer/brewRenderer.smart.jsx');
|
||||
|
||||
|
||||
const BrewInterface = React.createClass({
|
||||
|
||||
handleSplitMove : function(){
|
||||
console.log('split move!');
|
||||
const BrewEditor = this.refs.editor.refs.wrappedComponent;
|
||||
BrewEditor.updateEditorSize();
|
||||
},
|
||||
render: function(){
|
||||
return <SplitPane onDragFinish={this.handleSplitMove} ref='pane'>
|
||||
|
||||
@@ -133,6 +133,11 @@ const BrewRenderer = React.createClass({
|
||||
return this.lastRender;
|
||||
},
|
||||
|
||||
//TODO: This is pretty bad
|
||||
renderStyle : function(){
|
||||
return <style>{this.props.brew.style.replace(/;/g, ' !important;')}</style>
|
||||
},
|
||||
|
||||
render : function(){
|
||||
if(this.props.brew.version == 1) return <OldBrewRenderer value={this.props.brew.text} />;
|
||||
|
||||
@@ -146,7 +151,7 @@ const BrewRenderer = React.createClass({
|
||||
<RenderWarnings />
|
||||
|
||||
|
||||
<style>{this.props.brew.style}</style>
|
||||
{this.renderStyle()}
|
||||
|
||||
<div className='pages' ref='pages'>
|
||||
{this.renderPages()}
|
||||
|
||||
@@ -26,11 +26,18 @@ renderer.paragraph = function(text){
|
||||
return res;
|
||||
};
|
||||
|
||||
renderer.image = function(href, title, text){
|
||||
return `<img src="${href}" class="${text.split(',').join(' ')}"></img>`;
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
marked : Markdown,
|
||||
render : (rawBrewText)=>{
|
||||
blockCount = 0;
|
||||
|
||||
rawBrewText = rawBrewText.replace(/\\column/g, '{{columnSplit }}')
|
||||
|
||||
let html = Markdown(rawBrewText, {renderer : renderer, sanitize: true});
|
||||
//Close all hanging block tags
|
||||
html += _.times(blockCount, ()=>{return '</div>'}).join('\n');
|
||||
|
||||
BIN
shared/homebrewery/phb_style/img/footer_flip.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
shared/homebrewery/phb_style/img/monster_bg.jpg
Normal file
|
After Width: | Height: | Size: 339 KiB |
|
Before Width: | Height: | Size: 327 B After Width: | Height: | Size: 327 B |
BIN
shared/homebrewery/phb_style/img/note_border - Copy.png
Normal file
|
After Width: | Height: | Size: 530 B |
BIN
shared/homebrewery/phb_style/img/note_border.pdn
Normal file
BIN
shared/homebrewery/phb_style/img/note_border_shadow.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
shared/homebrewery/phb_style/img/shadow_border.pdn
Normal file
BIN
shared/homebrewery/phb_style/img/shadow_border.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
211
shared/homebrewery/phb_style/phb.blocks.less
Normal file
@@ -0,0 +1,211 @@
|
||||
|
||||
///////////////////
|
||||
.spell{
|
||||
ul:first-of-type{
|
||||
margin-top : -0.5em;
|
||||
margin-bottom : 0.5em;
|
||||
|
||||
list-style-type : none;
|
||||
&+p{
|
||||
text-indent : 0em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.monster{
|
||||
.breakAvoid();
|
||||
.pseudoBorder();
|
||||
.pseudoShadow();
|
||||
padding : 17px 14px;
|
||||
table:nth-of-type(1){
|
||||
margin-bottom : 0.4em;
|
||||
margin-top : 0.4em;
|
||||
color : @crimson;
|
||||
tbody tr { background-color: transparent };
|
||||
}
|
||||
ul:nth-of-type(1),ul:nth-of-type(2){
|
||||
list-style: none;
|
||||
padding-left : 1em;
|
||||
text-indent : -1em;
|
||||
margin-bottom : 0.5em;
|
||||
strong{
|
||||
color : @crimson;
|
||||
}
|
||||
}
|
||||
&:before{
|
||||
top : 8px;
|
||||
right : 7px;
|
||||
bottom : 19px;
|
||||
left : 7px;
|
||||
background-color : #FDF1DC;
|
||||
border-image-slice : 8;
|
||||
border-image-source : @monsterBorder;
|
||||
border-image-width : 8px;
|
||||
}
|
||||
&.wide{
|
||||
column-count : 2;
|
||||
}
|
||||
}
|
||||
.note{
|
||||
.useSansSerif();
|
||||
.breakAvoid();
|
||||
.pseudoBorder();
|
||||
.pseudoShadow();
|
||||
margin : 9px 0px;
|
||||
padding : 17px 17px;
|
||||
&:before{
|
||||
top : 9px;
|
||||
right : 9px;
|
||||
bottom : 19px;
|
||||
left : 9px;
|
||||
background-color : @green;
|
||||
border-width : 11px;
|
||||
border-image-outset : 9px 0px;
|
||||
border-image-slice : 11;
|
||||
border-image-source : @noteBorder;
|
||||
}
|
||||
h2,h3,h4{
|
||||
.useSansSerif();
|
||||
color : black;
|
||||
}
|
||||
p, ul{
|
||||
font-size : 0.352cm;
|
||||
line-height : 1.1em;
|
||||
}
|
||||
&.alt{
|
||||
&:before{
|
||||
border-style : solid;
|
||||
border-width : 7px;
|
||||
border-image-outset : 4px;
|
||||
border-image-slice : 12;
|
||||
border-image-source : @descriptiveBorder;
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame{
|
||||
.breakAvoid();
|
||||
.pseudoBorder();
|
||||
padding : 25px 17px;
|
||||
&:before{
|
||||
top : 25px;
|
||||
right : 17px;
|
||||
bottom : 25px;
|
||||
left : 17px;
|
||||
background-color : white;
|
||||
border-image-outset : 25px 17px;
|
||||
border-image-slice : 150 200 150 200;
|
||||
border-image-source : @frameBorder;
|
||||
border-image-width : 47px;
|
||||
}
|
||||
}
|
||||
.footnote{
|
||||
position : absolute;
|
||||
right : 80px;
|
||||
bottom : 28px;
|
||||
z-index : 150;
|
||||
width : 200px;
|
||||
font-size : 0.9em;
|
||||
color : @gold;
|
||||
text-align : right;
|
||||
}
|
||||
//*****************************
|
||||
// * TABLE OF CONTENTS
|
||||
// *****************************/
|
||||
.toc{
|
||||
h1{
|
||||
text-align : center;
|
||||
}
|
||||
li{
|
||||
margin-bottom : 3px;
|
||||
strong, em::after{
|
||||
font-family : BookInsanity;
|
||||
font-size : 13px;
|
||||
font-style : normal;
|
||||
font-weight : 500;
|
||||
color : black;
|
||||
}
|
||||
em{
|
||||
display : block;
|
||||
overflow : hidden;
|
||||
width : auto;
|
||||
font-style : normal;
|
||||
white-space : nowrap;
|
||||
&:after{
|
||||
content : " ..............................................................................................................";
|
||||
}
|
||||
}
|
||||
strong{
|
||||
float : right;
|
||||
margin-left : 4px;
|
||||
}
|
||||
h3{
|
||||
margin-top : 15px;
|
||||
em{ color : @crimson; }
|
||||
em::after{ display : none; }
|
||||
}
|
||||
h4{
|
||||
margin-top : 10px;
|
||||
em{ color : @crimson; }
|
||||
}
|
||||
}
|
||||
a{
|
||||
color : black;
|
||||
text-decoration : none;
|
||||
&:hover{
|
||||
text-decoration : underline;
|
||||
}
|
||||
}
|
||||
ul{
|
||||
padding-left : 0;
|
||||
list-style-type : none;
|
||||
}
|
||||
}
|
||||
.wide{
|
||||
column-span : all;
|
||||
-webkit-column-span : all;
|
||||
-moz-column-span : all;
|
||||
}
|
||||
.oneColumn{
|
||||
column-count : 1;
|
||||
// column-gap : 1cm;
|
||||
}
|
||||
.twoColumn{
|
||||
column-count : 2;
|
||||
//column-fill: auto;
|
||||
////column-gap : 1cm;
|
||||
}
|
||||
.threeColumn{
|
||||
column-count : 3;
|
||||
//column-gap : 1cm;
|
||||
}
|
||||
.fourColumn{
|
||||
column-count : 4;
|
||||
//column-gap : 1cm;
|
||||
}
|
||||
.columnSplit{
|
||||
visibility : hidden;
|
||||
-webkit-column-break-bfore : always;
|
||||
break-before : column;
|
||||
}
|
||||
.brushed{
|
||||
border-image-outset : 25px 17px;
|
||||
border-image-repeat : round;
|
||||
border-image-slice : 1250 1250 1250 1250;
|
||||
border-image-width : 1250px;
|
||||
border-image-source : url('http : //i.imgur.com/nzPYZyD.png');
|
||||
}
|
||||
//basics
|
||||
.left{
|
||||
text-align : left;
|
||||
}
|
||||
.right{
|
||||
text-align : right;
|
||||
}
|
||||
.center{
|
||||
text-align : center;
|
||||
}
|
||||
.bold{
|
||||
font-weight : 800;
|
||||
}
|
||||
.sansSerif{
|
||||
.useSansSerif();
|
||||
}
|
||||
@@ -8,8 +8,6 @@
|
||||
@monsterStatBackground : #FDF1DC;
|
||||
|
||||
|
||||
@teal : blue;
|
||||
|
||||
|
||||
.colorElements(@color){
|
||||
table tbody{
|
||||
@@ -17,8 +15,24 @@
|
||||
background-color : @color;
|
||||
}
|
||||
}
|
||||
&.note:before{
|
||||
background-color: @color;
|
||||
}
|
||||
}
|
||||
|
||||
@crimson : #58180D;
|
||||
@red : #9c2b1b;
|
||||
@gold : #c9ad6a; //brown?
|
||||
@green : #e0e5c1;
|
||||
@yellow : #faf7ea; //same as background?
|
||||
@teal : blue;
|
||||
@blue : blue;
|
||||
|
||||
|
||||
//TODO make a color mixin generator
|
||||
.teal{ .colorElements(@teal); }
|
||||
.blue{ .colorElements(@blue); }
|
||||
.green{ .colorElements(@green); }
|
||||
.yellow{ .colorElements(@yellow); }
|
||||
.gold{ .colorElements(@gold); }
|
||||
.red{ .colorElements(@red); }
|
||||
|
||||
172
shared/homebrewery/phb_style/phb.elements.less
Normal file
@@ -0,0 +1,172 @@
|
||||
pre{
|
||||
font-family : monospace;
|
||||
background-color : @yellow;
|
||||
padding : 12px;
|
||||
border: 1px solid #bfbfbf;
|
||||
white-space: pre-wrap;
|
||||
color : #333;
|
||||
-webkit-column-break-inside : avoid;
|
||||
column-break-inside : avoid;
|
||||
}
|
||||
|
||||
|
||||
hr{
|
||||
visibility : visible;
|
||||
height : 6px;
|
||||
margin : 4px 0px;
|
||||
background-image : @dividerImg;
|
||||
background-size : 100% 100%;
|
||||
border : none;
|
||||
}
|
||||
|
||||
|
||||
p{
|
||||
padding-bottom : 0.8em;
|
||||
line-height : 1.3em;
|
||||
&+p{
|
||||
margin-top : -0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
blockquote{
|
||||
font-style : italic;
|
||||
&>p{
|
||||
line-height: 1.8em;
|
||||
&:first-child::first-line{
|
||||
|
||||
//TODO: Find the right font for block quotes
|
||||
font-style: normal;
|
||||
font-family: ScalySansSmallCaps;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
.cite{
|
||||
font-style: normal;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//Indents after p or lists
|
||||
p+p, ul+p, ol+p{
|
||||
text-indent : 1em;
|
||||
}
|
||||
img{
|
||||
z-index : -1;
|
||||
}
|
||||
strong{
|
||||
font-weight : bold;
|
||||
letter-spacing : 0.03em;
|
||||
}
|
||||
em{
|
||||
font-style : italic;
|
||||
}
|
||||
sup{
|
||||
vertical-align : super;
|
||||
font-size : smaller;
|
||||
line-height : 0;
|
||||
}
|
||||
sub{
|
||||
vertical-align : sub;
|
||||
font-size : smaller;
|
||||
line-height : 0;
|
||||
}
|
||||
//*****************************
|
||||
// * HEADERS
|
||||
// *****************************/
|
||||
h1,h2,h3,h4{
|
||||
margin-top : 0.2em;
|
||||
margin-bottom : 0.2em;
|
||||
font-family : MrEaves;
|
||||
font-weight : 800;
|
||||
color : @headerText;
|
||||
}
|
||||
h1{
|
||||
column-span : all;
|
||||
font-size : 0.987cm;
|
||||
-webkit-column-span : all;
|
||||
-moz-column-span : all;
|
||||
&+p::first-letter{
|
||||
float : left;
|
||||
font-family : Solbera;
|
||||
font-size : 10em;
|
||||
color : #222;
|
||||
line-height : 0.8em;
|
||||
}
|
||||
}
|
||||
h2{
|
||||
font-size : 0.705cm;
|
||||
}
|
||||
h3{
|
||||
font-size : 0.529cm;
|
||||
border-bottom : 2px solid @headerUnderline;
|
||||
}
|
||||
h4{
|
||||
margin-bottom : 0.00em;
|
||||
font-size : 0.458cm;
|
||||
}
|
||||
h5{
|
||||
margin-bottom : 0.2em;
|
||||
font-family : ScalySansSmallCaps;
|
||||
font-size : 0.423cm;
|
||||
font-weight : 900;
|
||||
}
|
||||
|
||||
|
||||
//******************************
|
||||
// LISTS
|
||||
//******************************
|
||||
ul ul,ol ol,ul ol,ol ul{
|
||||
margin-bottom : 0px;
|
||||
margin-left : 1.5em;
|
||||
}
|
||||
li{
|
||||
-webkit-column-break-inside : avoid;
|
||||
column-break-inside : avoid;
|
||||
}
|
||||
ul{
|
||||
margin-bottom : 0.8em;
|
||||
padding-left : 1.4em;
|
||||
line-height : 1.3em;
|
||||
list-style-position : outside;
|
||||
list-style-type : disc;
|
||||
}
|
||||
ol{
|
||||
margin-bottom : 0.8em;
|
||||
padding-left : 1.4em;
|
||||
line-height : 1.3em;
|
||||
list-style-position : outside;
|
||||
list-style-type : decimal;
|
||||
}
|
||||
|
||||
|
||||
//*****************************
|
||||
// * TABLE
|
||||
// *****************************/
|
||||
table{
|
||||
.useSansSerif();
|
||||
width : 100%;
|
||||
margin-bottom : 1em;
|
||||
font-size : 10pt;
|
||||
thead{
|
||||
font-weight : 800;
|
||||
th{
|
||||
vertical-align : bottom;
|
||||
padding-bottom : 0.3em;
|
||||
padding-right : 0.1em;
|
||||
padding-left : 0.1em;
|
||||
}
|
||||
}
|
||||
tbody{
|
||||
tr{
|
||||
td{
|
||||
padding : 0.3em 0.1em;
|
||||
}
|
||||
&:nth-child(odd){
|
||||
background-color : @green;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,18 +53,3 @@
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
//TODO: move the useSansSerif into here
|
||||
|
||||
.useSansSerif(){
|
||||
font-family : ScalySans;
|
||||
em{
|
||||
font-family : ScalySans;
|
||||
font-style : italic;
|
||||
}
|
||||
strong{
|
||||
font-family : ScalySans;
|
||||
font-weight : 800;
|
||||
letter-spacing : -0.02em;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
@footerImg : url('/assets/homebrewery/phb_style/img/footer.png');
|
||||
@footerFlipImg : url('/assets/homebrewery/phb_style/img/footer_flip.png');
|
||||
@dividerImg : url('/assets/homebrewery/phb_style/img/divider.png');
|
||||
|
||||
@frameBorder : url('/assets/homebrewery/phb_style/img/frame_border.png');
|
||||
@monsterBorder : url('/assets/homebrewery/phb_style/img/monster_border.png');
|
||||
@noteBorder : url('/assets/homebrewery/phb_style/img/note_border.png');
|
||||
@descriptiveBorder : url('/assets/homebrewery/phb_style/img/desc_border.png');
|
||||
@shadowBorder : url('/assets/homebrewery/phb_style/img/shadow_border.png');
|
||||
|
||||
|
||||
@phbBG : url('/assets/homebrewery/phb_style/img/phb_bg.jpg');
|
||||
@darkBG : url('/assets/homebrewery/phb_style/img/phb_dark_bg.jpg');
|
||||
@dmgBG : url('/assets/homebrewery/phb_style/img/dmg_bg.jpg');
|
||||
@monsterBG : url('/assets/homebrewery/phb_style/img/monster_bg.jpg');
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
|
||||
//TODO: Remove this
|
||||
/*
|
||||
@media print {
|
||||
.phb.v2{
|
||||
.descriptive, blockquote{
|
||||
@@ -5,34 +8,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
.phb.v2{
|
||||
@import './phb.mixins.less';
|
||||
@import './phb.fonts.less';
|
||||
@import './phb.colors.less';
|
||||
@import './phb.img.less';
|
||||
|
||||
|
||||
@page { margin: 0; }
|
||||
|
||||
|
||||
.useColumns(@multiplier : 1){
|
||||
column-count : 2;
|
||||
column-fill : auto;
|
||||
column-gap : 1cm;
|
||||
column-width : 8cm * @multiplier;
|
||||
-webkit-column-count : 2;
|
||||
-moz-column-count : 2;
|
||||
-webkit-column-width : 8cm * @multiplier;
|
||||
-moz-column-width : 8cm * @multiplier;
|
||||
-webkit-column-gap : 1cm;
|
||||
-moz-column-gap : 1cm;
|
||||
}
|
||||
& *{
|
||||
-webkit-print-color-adjust : exact;
|
||||
}
|
||||
.useColumns();
|
||||
@import './phb.blocks.less';
|
||||
@import './phb.elements.less';
|
||||
counter-increment : phb-page-numbers;
|
||||
position : relative;
|
||||
z-index : 15;
|
||||
@@ -42,6 +27,10 @@
|
||||
width : 215.9mm;
|
||||
padding : 1.0cm 1.7cm;
|
||||
padding-bottom : 1.5cm;
|
||||
column-count : 2;
|
||||
column-fill : auto;
|
||||
column-gap : 1cm;
|
||||
column-width : 8cm;
|
||||
background-color : @background;
|
||||
background-image : @phbBG;
|
||||
font-family : BookInsanity;
|
||||
@@ -49,150 +38,64 @@
|
||||
text-rendering : optimizeLegibility;
|
||||
page-break-before : always;
|
||||
page-break-after : always;
|
||||
@page { margin: 0; } //TODO: ????
|
||||
& *{
|
||||
-webkit-print-color-adjust : exact;
|
||||
}
|
||||
//*****************************
|
||||
// * FOOTER
|
||||
// *****************************/
|
||||
&:after{
|
||||
content : "Made with The Homebrewery";
|
||||
position : absolute;
|
||||
bottom : 0px;
|
||||
left : 0px;
|
||||
z-index : 100;
|
||||
height : 50px;
|
||||
width : 100%;
|
||||
background-image : @footerImg;
|
||||
background-size : cover;
|
||||
padding: 28px 63px;
|
||||
box-sizing: border-box;
|
||||
color : lighten(@gold, 0%);
|
||||
font-size: 0.7em;
|
||||
}
|
||||
&:nth-child(even){
|
||||
&:after{
|
||||
background-image: @footerFlipImg;
|
||||
text-align: right;
|
||||
}
|
||||
&:before{
|
||||
left : 2px;
|
||||
}
|
||||
.footnote{
|
||||
left : 80px;
|
||||
text-align : left;
|
||||
}
|
||||
}
|
||||
|
||||
&:before{
|
||||
content : counter(phb-page-numbers);
|
||||
position : absolute;
|
||||
right : 2px;
|
||||
bottom : 22px;
|
||||
width : 50px;
|
||||
font-size : 0.9em;
|
||||
color : @gold;
|
||||
text-align : center;
|
||||
}
|
||||
|
||||
//*****************************
|
||||
// * BASE
|
||||
// *****************************/
|
||||
p{
|
||||
padding-bottom : 0.8em;
|
||||
line-height : 1.3em;
|
||||
&+p{
|
||||
margin-top : -0.8em;
|
||||
}
|
||||
}
|
||||
ul{
|
||||
margin-bottom : 0.8em;
|
||||
padding-left : 1.4em;
|
||||
line-height : 1.3em;
|
||||
list-style-position : outside;
|
||||
list-style-type : disc;
|
||||
}
|
||||
ol{
|
||||
margin-bottom : 0.8em;
|
||||
padding-left : 1.4em;
|
||||
line-height : 1.3em;
|
||||
list-style-position : outside;
|
||||
list-style-type : decimal;
|
||||
}
|
||||
//Indents after p or lists
|
||||
p+p, ul+p, ol+p{
|
||||
text-indent : 1em;
|
||||
}
|
||||
img{
|
||||
z-index : -1;
|
||||
}
|
||||
strong{
|
||||
font-weight : bold;
|
||||
letter-spacing : 0.03em;
|
||||
}
|
||||
em{
|
||||
font-style : italic;
|
||||
}
|
||||
sup{
|
||||
vertical-align : super;
|
||||
font-size : smaller;
|
||||
line-height : 0;
|
||||
}
|
||||
sub{
|
||||
vertical-align : sub;
|
||||
font-size : smaller;
|
||||
line-height : 0;
|
||||
}
|
||||
//*****************************
|
||||
// * HEADERS
|
||||
// *****************************/
|
||||
h1,h2,h3,h4{
|
||||
margin-top : 0.2em;
|
||||
margin-bottom : 0.2em;
|
||||
font-family : MrEaves;
|
||||
font-weight : 800;
|
||||
color : @headerText;
|
||||
}
|
||||
h1{
|
||||
column-span : all;
|
||||
font-size : 0.987cm;
|
||||
-webkit-column-span : all;
|
||||
-moz-column-span : all;
|
||||
&+p::first-letter{
|
||||
float : left;
|
||||
font-family : Solbera;
|
||||
font-size : 10em;
|
||||
color : #222;
|
||||
line-height : 0.8em;
|
||||
}
|
||||
}
|
||||
h2{
|
||||
font-size : 0.705cm;
|
||||
}
|
||||
h3{
|
||||
font-size : 0.529cm;
|
||||
border-bottom : 2px solid @headerUnderline;
|
||||
}
|
||||
h4{
|
||||
margin-bottom : 0.00em;
|
||||
font-size : 0.458cm;
|
||||
}
|
||||
h5{
|
||||
margin-bottom : 0.2em;
|
||||
font-family : ScalySansSmallCaps;
|
||||
font-size : 0.423cm;
|
||||
font-weight : 900;
|
||||
}
|
||||
//*****************************
|
||||
// * TABLE
|
||||
// *****************************/
|
||||
table{
|
||||
.useSansSerif();
|
||||
width : 100%;
|
||||
margin-bottom : 1em;
|
||||
font-size : 10pt;
|
||||
thead{
|
||||
font-weight : 800;
|
||||
th{
|
||||
vertical-align : bottom;
|
||||
padding-bottom : 0.3em;
|
||||
padding-right : 0.1em;
|
||||
padding-left : 0.1em;
|
||||
}
|
||||
}
|
||||
tbody{
|
||||
tr{
|
||||
td{
|
||||
padding : 0.3em 0.1em;
|
||||
}
|
||||
&:nth-child(odd){
|
||||
background-color : @noteGreen;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//*****************************
|
||||
// * NOTE
|
||||
// *****************************/
|
||||
blockquote{
|
||||
.useSansSerif();
|
||||
box-sizing : border-box;
|
||||
margin-bottom : 1em;
|
||||
padding : 5px 10px;
|
||||
background-color : @noteGreen;
|
||||
border-style : solid;
|
||||
border-width : 11px;
|
||||
border-image : @noteBorder 11;
|
||||
border-image-outset : 9px 0px;
|
||||
box-shadow : 1px 4px 14px #888;
|
||||
p, ul{
|
||||
font-size : 0.352cm;
|
||||
line-height : 1.1em;
|
||||
}
|
||||
}
|
||||
//If a note starts a column, give it space at the top to render border
|
||||
pre+blockquote, h2+blockquote, h3+blockquote, h4+blockquote, h5+blockquote {
|
||||
margin-top : 13px;
|
||||
}
|
||||
//pre+blockquote, h2+blockquote, h3+blockquote, h4+blockquote, h5+blockquote {
|
||||
// margin-top : 13px;
|
||||
//}
|
||||
//*****************************
|
||||
// * MONSTER STAT BLOCK
|
||||
// *****************************/
|
||||
/*
|
||||
hr+blockquote{
|
||||
position : relative;
|
||||
padding-top : 15px;
|
||||
@@ -252,107 +155,46 @@
|
||||
border : none;
|
||||
}
|
||||
}
|
||||
//Full Width
|
||||
hr+hr+blockquote{
|
||||
.useColumns(0.96);
|
||||
}
|
||||
//*****************************
|
||||
// * FOOTER
|
||||
// *****************************/
|
||||
&:after{
|
||||
content : "";
|
||||
position : absolute;
|
||||
bottom : 0px;
|
||||
left : 0px;
|
||||
z-index : 100;
|
||||
height : 50px;
|
||||
width : 100%;
|
||||
background-image : @footerImg;
|
||||
background-size : cover;
|
||||
}
|
||||
&:nth-child(even){
|
||||
&:after{
|
||||
transform : scaleX(-1);
|
||||
}
|
||||
.pageNumber{
|
||||
left : 2px;
|
||||
}
|
||||
.footnote{
|
||||
left : 80px;
|
||||
text-align : left;
|
||||
}
|
||||
}
|
||||
.pageNumber{
|
||||
position : absolute;
|
||||
right : 2px;
|
||||
bottom : 22px;
|
||||
width : 50px;
|
||||
font-size : 0.9em;
|
||||
color : #c9ad6a;
|
||||
text-align : center;
|
||||
&.auto::after {
|
||||
content : counter(phb-page-numbers);
|
||||
}
|
||||
}
|
||||
.footnote{
|
||||
position : absolute;
|
||||
right : 80px;
|
||||
bottom : 32px;
|
||||
z-index : 150;
|
||||
width : 200px;
|
||||
font-size : 0.8em;
|
||||
color : #c9ad6a;
|
||||
text-align : right;
|
||||
}
|
||||
|
||||
//*****************************
|
||||
// * EXTRAS
|
||||
// *****************************/
|
||||
hr{
|
||||
visibility : hidden;
|
||||
margin : 0px;
|
||||
}
|
||||
//Modified unorder list, used in spells
|
||||
hr+ul{
|
||||
margin-bottom : 0.5em;
|
||||
padding-left : 1em;
|
||||
text-indent : -1em;
|
||||
list-style-type : none;
|
||||
}
|
||||
// hr+ul{
|
||||
// margin-bottom : 0.5em;
|
||||
// padding-left : 1em;
|
||||
// text-indent : -1em;
|
||||
// list-style-type : none;
|
||||
// }
|
||||
//Column Break
|
||||
/*
|
||||
pre, code{
|
||||
visibility : hidden;
|
||||
-webkit-column-break-after : always;
|
||||
break-after : always;
|
||||
-moz-column-break-after : always;
|
||||
}
|
||||
*/
|
||||
//Avoid breaking up
|
||||
/*
|
||||
p,blockquote,table{
|
||||
z-index : 15;
|
||||
-webkit-column-break-inside : avoid;
|
||||
column-break-inside : avoid;
|
||||
overflow: hidden; /* Firefox fix */
|
||||
}
|
||||
//Better spacing for spell blocks
|
||||
h4+p+hr+ul{
|
||||
margin-top : -0.5em
|
||||
}
|
||||
//Text indent right after table
|
||||
table+p{
|
||||
text-indent : 1em;
|
||||
}
|
||||
// h4+p+hr+ul{
|
||||
// margin-top : -0.5em
|
||||
// }
|
||||
// //Text indent right after table
|
||||
// table+p{
|
||||
// text-indent : 1em;
|
||||
// }
|
||||
// Nested lists
|
||||
ul ul,ol ol,ul ol,ol ul{
|
||||
margin-bottom : 0px;
|
||||
margin-left : 1.5em;
|
||||
}
|
||||
li{
|
||||
-webkit-column-break-inside : avoid;
|
||||
column-break-inside : avoid;
|
||||
}
|
||||
|
||||
//*****************************
|
||||
// * SPELL LIST
|
||||
// *****************************/
|
||||
/*
|
||||
.spellList{
|
||||
.useSansSerif();
|
||||
column-count : 4;
|
||||
@@ -378,42 +220,43 @@
|
||||
//*****************************
|
||||
// * PRINT
|
||||
// *****************************/
|
||||
&.print{
|
||||
blockquote{
|
||||
box-shadow : none;
|
||||
}
|
||||
}
|
||||
|
||||
// &.print{
|
||||
// blockquote{
|
||||
// box-shadow : none;
|
||||
// }
|
||||
// }
|
||||
//*****************************
|
||||
// * WIDE
|
||||
// *****************************/
|
||||
/*
|
||||
.wide{
|
||||
column-span : all;
|
||||
-webkit-column-span : all;
|
||||
-moz-column-span : all;
|
||||
}
|
||||
}*/
|
||||
//*****************************
|
||||
// * CLASS TABLE
|
||||
// *****************************/
|
||||
.classTable{
|
||||
margin-top : 25px;
|
||||
margin-bottom : 40px;
|
||||
border-collapse : separate;
|
||||
background-color : white;
|
||||
border : initial;
|
||||
border-style : solid;
|
||||
border-image-outset : 25px 17px;
|
||||
border-image-repeat : round;
|
||||
border-image-slice : 150 200 150 200;
|
||||
border-image-source : @frameBorder;
|
||||
border-image-width : 47px;
|
||||
h5{
|
||||
margin-bottom : 10px;
|
||||
}
|
||||
}
|
||||
// .classTable{
|
||||
// margin-top : 25px;
|
||||
// margin-bottom : 40px;
|
||||
// border-collapse : separate;
|
||||
// background-color : white;
|
||||
// border : initial;
|
||||
// border-style : solid;
|
||||
// border-image-outset : 25px 17px;
|
||||
// border-image-repeat : round;
|
||||
// border-image-slice : 150 200 150 200;
|
||||
// border-image-source : @frameBorder;
|
||||
// border-image-width : 47px;
|
||||
// h5{
|
||||
// margin-bottom : 10px;
|
||||
// }
|
||||
// }
|
||||
//*****************************
|
||||
// * CLASS TABLE
|
||||
// *****************************/
|
||||
/*
|
||||
.descriptive{
|
||||
display : block-inline;
|
||||
margin-bottom : 1em;
|
||||
@@ -445,61 +288,36 @@
|
||||
pre+.descriptive{
|
||||
margin-top : 8px;
|
||||
}
|
||||
//*****************************
|
||||
// * TABLE OF CONTENTS
|
||||
// *****************************/
|
||||
.toc{
|
||||
-webkit-column-break-inside : avoid;
|
||||
column-break-inside : avoid;
|
||||
a{
|
||||
color : black;
|
||||
text-decoration : none;
|
||||
&:hover{
|
||||
text-decoration : underline;
|
||||
}
|
||||
}
|
||||
ul{
|
||||
padding-left : 0;
|
||||
list-style-type : none;
|
||||
}
|
||||
&>ul>li{
|
||||
margin-bottom : 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
*/
|
||||
//*****************************
|
||||
// * Old Stuff
|
||||
// *****************************/
|
||||
|
||||
//Double hr for full width elements
|
||||
hr+hr+blockquote{
|
||||
column-span : all;
|
||||
-webkit-column-span : all;
|
||||
-moz-column-span : all;
|
||||
}
|
||||
|
||||
// //Double hr for full width elements
|
||||
// hr+hr+blockquote{
|
||||
// column-span : all;
|
||||
// -webkit-column-span : all;
|
||||
// -moz-column-span : all;
|
||||
// }
|
||||
//*****************************
|
||||
// * CLASS TABLE
|
||||
// *****************************/
|
||||
hr+table{
|
||||
margin-top : -5px;
|
||||
margin-bottom : 50px;
|
||||
padding-top : 10px;
|
||||
border-collapse : separate;
|
||||
background-color : white;
|
||||
border : initial;
|
||||
border-style : solid;
|
||||
border-image-outset : 37px 17px;
|
||||
border-image-repeat : round;
|
||||
border-image-slice : 150 200 150 200;
|
||||
border-image-source : @frameBorder;
|
||||
border-image-width : 47px;
|
||||
}
|
||||
h5+hr+table{
|
||||
column-span : all;
|
||||
-webkit-column-span : all;
|
||||
-moz-column-span : all;
|
||||
}
|
||||
// hr+table{
|
||||
// margin-top : -5px;
|
||||
// margin-bottom : 50px;
|
||||
// padding-top : 10px;
|
||||
// border-collapse : separate;
|
||||
// background-color : white;
|
||||
// border : initial;
|
||||
// border-style : solid;
|
||||
// border-image-outset : 37px 17px;
|
||||
// border-image-repeat : round;
|
||||
// border-image-slice : 150 200 150 200;
|
||||
// border-image-source : @frameBorder;
|
||||
// border-image-width : 47px;
|
||||
// }
|
||||
// h5+hr+table{
|
||||
// column-span : all;
|
||||
// -webkit-column-span : all;
|
||||
// -moz-column-span : all;
|
||||
// }
|
||||
}
|
||||
48
shared/homebrewery/phb_style/phb.mixins.less
Normal file
@@ -0,0 +1,48 @@
|
||||
.breakAvoid(){
|
||||
column-break-inside : avoid;
|
||||
-webkit-column-break-inside : avoid;
|
||||
}
|
||||
.pseudoBorder(){
|
||||
position : relative;
|
||||
&:before{
|
||||
content : '';
|
||||
position : absolute;
|
||||
z-index : -2;
|
||||
box-sizing : border-box;
|
||||
border-style : solid;
|
||||
border-image-repeat : round;
|
||||
}
|
||||
}
|
||||
|
||||
.pseudoShadow(){
|
||||
position : relative;
|
||||
&:after{
|
||||
content : '';
|
||||
position : absolute;
|
||||
z-index : -1;
|
||||
box-sizing : border-box;
|
||||
top : 4px;
|
||||
right : 0px;
|
||||
bottom : 10px;
|
||||
left : 0px;
|
||||
border-style : solid;
|
||||
border-image-repeat : round;
|
||||
border-image-slice : 13 13;
|
||||
border-image-source : @shadowBorder;
|
||||
border-image-width : 11px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.useSansSerif(){
|
||||
font-family : ScalySans;
|
||||
em{
|
||||
font-family : ScalySans;
|
||||
font-style : italic;
|
||||
}
|
||||
strong{
|
||||
font-family : ScalySans;
|
||||
font-weight : 800;
|
||||
letter-spacing : -0.02em;
|
||||
}
|
||||
}
|
||||
82
shared/homebrewery/snippets/brew/class.snippet.js
Normal file
@@ -0,0 +1,82 @@
|
||||
const _ = require('lodash');
|
||||
const Data = require('./random.data.js');
|
||||
|
||||
const getFeature = (level)=>{
|
||||
let res = []
|
||||
if(_.includes([4,6,8,12,14,16,19], level+1)){
|
||||
res = ['Ability Score Improvement']
|
||||
}
|
||||
res = _.union(res, _.sampleSize(Data.abilities, _.sample([0,1,1,1,1,1])));
|
||||
if(!res.length) return '─';
|
||||
return res.join(', ');
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
|
||||
casterTable : ()=>{
|
||||
|
||||
let featureScore = 1
|
||||
const rows = _.map(Data.levels, (lvlText, level)=>{
|
||||
featureScore += _.random(0,1);
|
||||
return '| ' + [
|
||||
lvlText,
|
||||
'+'+Math.floor(level/4 + 2),
|
||||
getFeature(level),
|
||||
'+'+featureScore
|
||||
].join(' | ') + ' |';
|
||||
}).join('\n');
|
||||
|
||||
return `{{frame,wide
|
||||
##### ${Data.rand('classes')}
|
||||
| Level | Proficiency Bonus | Features | Cantrips Known | Spells Known | 1st | 2nd | 3rd | 4th | 5th | 6th | 7th | 8th | 9th |
|
||||
|:---:|:---:|:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
${rows}
|
||||
}}`;
|
||||
},
|
||||
|
||||
|
||||
halfcasterTable : ()=>{
|
||||
let featureScore = 1
|
||||
const rows = _.map(Data.levels, (lvlText, level)=>{
|
||||
featureScore += _.random(0,1);
|
||||
return '| ' + [
|
||||
lvlText,
|
||||
'+'+Math.floor(level/4 + 2),
|
||||
getFeature(level),
|
||||
'+'+featureScore
|
||||
].join(' | ') + ' |';
|
||||
}).join('\n');
|
||||
|
||||
|
||||
return `{{frame,wide
|
||||
##### ${Data.rand('classes')}
|
||||
| Level | Proficiency Bonus | Features | 1st | 2nd | 3rd | 4th | 5th |
|
||||
|:---:|:---:|:---|:---:|:---:|:---:|:---:|:---:|
|
||||
${rows}
|
||||
}}`;
|
||||
|
||||
},
|
||||
|
||||
noncasterTable : ()=>{
|
||||
let featureScore = 1
|
||||
const rows = _.map(Data.levels, (lvlText, level)=>{
|
||||
featureScore += _.random(0,1);
|
||||
return '| ' + [
|
||||
lvlText,
|
||||
'+'+Math.floor(level/4 + 2),
|
||||
getFeature(level),
|
||||
'+'+featureScore
|
||||
].join(' | ') + ' |';
|
||||
}).join('\n');
|
||||
|
||||
return `{{frame
|
||||
##### ${Data.rand('classes')}
|
||||
| Level | Proficiency Bonus | Features | ${Data.rand('abilities')} |
|
||||
|:---:|:---:|:---|:---:|
|
||||
${rows}
|
||||
}}`;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
19
shared/homebrewery/snippets/brew/index.js
Normal file
@@ -0,0 +1,19 @@
|
||||
const _ = require('lodash');
|
||||
|
||||
module.exports = _.merge(
|
||||
require('./spell.snippet.js'),
|
||||
require('./table.snippet.js'),
|
||||
require('./class.snippet.js'),
|
||||
require('./note.snippet.js'),
|
||||
require('./monster.snippet.js'),
|
||||
require('./toc.snippet.js')
|
||||
|
||||
|
||||
//wide
|
||||
//colors
|
||||
//brushed
|
||||
//font
|
||||
//alignment
|
||||
|
||||
|
||||
);
|
||||
74
shared/homebrewery/snippets/brew/monster.snippet.js
Normal file
@@ -0,0 +1,74 @@
|
||||
const _ = require('lodash');
|
||||
const Data = require('./random.data.js');
|
||||
|
||||
|
||||
const getStats = function(){
|
||||
return '|' + _.times(6, function(){
|
||||
const num = _.random(1,20);
|
||||
const mod = Math.ceil(num/2 - 5)
|
||||
return num + " (" + (mod >= 0 ? '+'+mod : mod ) + ")"
|
||||
}).join('|') + '|';
|
||||
}
|
||||
|
||||
const getAttributes = ()=>{
|
||||
|
||||
|
||||
|
||||
|
||||
return `
|
||||
- **Saving Throws**
|
||||
- **Condition Immunities** " + genList(["groggy", "swagged", "weak-kneed", "buzzed", "groovy", "melancholy", "drunk"], 3),
|
||||
- **Senses** passive Perception " + _.random(3, 20),
|
||||
- **Languages** ${Data.rand(["Common", "Pottymouth", "Gibberish", "Latin", "Jive"], 2).join(', ')}
|
||||
- **Challenge** ${_.random(0, 15)} (${_.random(10,10000)} XP)
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
const getAbilities = ()=>{
|
||||
|
||||
}
|
||||
|
||||
const getActions = ()=>{
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
monster : ()=>{
|
||||
|
||||
const stats = '';
|
||||
|
||||
return `{{monster
|
||||
## ${Data.rand('creatures')}
|
||||
*${Data.rand('sizes')}, ${Data.rand('alignments')}*
|
||||
|
||||
---
|
||||
|
||||
- **Armor Class** ${_.random(10,20)}
|
||||
- **Hit Points** ${_.random(1, 150)} (1d4 + 5)
|
||||
- **Speed** ${ _.random(0,50)} ft
|
||||
|
||||
---
|
||||
|
||||
|STR|DEX|CON|INT|WIS|CHA|
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
${getStats()}
|
||||
|
||||
---
|
||||
|
||||
${getAttributes()}
|
||||
|
||||
---
|
||||
|
||||
Abilities
|
||||
|
||||
|
||||
### Actions
|
||||
|
||||
}}`
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
22
shared/homebrewery/snippets/brew/note.snippet.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const _ = require('lodash');
|
||||
const Data = require('./random.data.js');
|
||||
|
||||
|
||||
module.exports = {
|
||||
note : ()=>{
|
||||
return `{{note
|
||||
##### ${Data.rand('abilities')}
|
||||
${Data.rand('sentences', 6, 4).join(' ')}
|
||||
}}`
|
||||
|
||||
},
|
||||
|
||||
altnote : ()=>{
|
||||
return `{{note,alt
|
||||
##### ${Data.rand('abilities')}
|
||||
${Data.rand('sentences', 6, 4).join(' ')}
|
||||
}}`
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
421
shared/homebrewery/snippets/brew/random.data.js
Normal file
@@ -0,0 +1,421 @@
|
||||
const _ = require('lodash');
|
||||
|
||||
const Data = {
|
||||
rand : (name, max = 1, min = 1)=>{
|
||||
const data = (Data[name] ? Data[name] : name);
|
||||
return _.sampleSize(data, _.random(min, max));
|
||||
},
|
||||
|
||||
titles : [
|
||||
`The Burning Gallows`,
|
||||
`The Ring of Nenlast`,
|
||||
`Below the Blind Tavern`,
|
||||
`Below the Hungering River`,
|
||||
`Before Bahamut's Land`,
|
||||
`The Cruel Grave from Within`,
|
||||
`The Strength of Trade Road`,
|
||||
`Through The Raven Queen's Worlds`,
|
||||
`Within the Settlement`,
|
||||
`The Crown from Within`,
|
||||
`The Merchant Within the Battlefield`,
|
||||
`Ioun's Fading Traveler`,
|
||||
`The Legion Ingredient`,
|
||||
`The Explorer Lure`,
|
||||
`Before the Charming Badlands`,
|
||||
`The Living Dead Above the Fearful Cage`,
|
||||
`Vecna's Hidden Sage`,
|
||||
`Bahamut's Demonspawn`,
|
||||
`Across Gruumsh's Elemental Chaos`,
|
||||
`The Blade of Orcus`,
|
||||
`Beyond Revenge`,
|
||||
`Brain of Insanity`,
|
||||
`Breed Battle!, A New Beginning`,
|
||||
`Evil Lake, A New Beginning`,
|
||||
`Invasion of the Gigantic Cat, Part II`,
|
||||
`Kraken War 2020`,
|
||||
`The Body Whisperers`,
|
||||
`The Diabolical Tales of the Ape-Women`,
|
||||
`The Doctor Immortal`,
|
||||
`The Doctor from Heaven`,
|
||||
`Azure Core`,
|
||||
`Core Battle`,
|
||||
`Core of Heaven: The Guardian of Amazement`,
|
||||
`Deadly Amazement III`,
|
||||
`Dry Chaos IX`,
|
||||
`Gate Thunder`,
|
||||
`Guardian: Skies of the Dark Wizard`,
|
||||
`Lute of Eternity`,
|
||||
`Mercury's Planet: Brave Evolution`,
|
||||
`Ruby of Atlantis: The Quake of Peace`,
|
||||
`Vyse's Skies`,
|
||||
`White Greatness III`,
|
||||
`Yellow Divinity`,
|
||||
`Zidane's Ghost`
|
||||
],
|
||||
|
||||
subtitles : [
|
||||
`In an ominous universe, a botanist opposes terrorism.`,
|
||||
`In a demon-haunted city, in an age of lies and hate, a physicist tries to find an ancient treasure and battles a mob of aliens.`,
|
||||
`In a land of corruption, two cyberneticists and a dungeon delver search for freedom.`,
|
||||
`In an evil empire of horror, two rangers battle the forces of hell.`,
|
||||
`In a lost city, in an age of sorcery, a librarian quests for revenge.`,
|
||||
`In a universe of illusions and danger, three time travellers and an adventurer search for justice.`,
|
||||
`In a forgotten universe of barbarism, in an era of terror and mysticism, a virtual reality programmer and a spy try to find vengance and battle crime.`,
|
||||
`In a universe of demons, in an era of insanity and ghosts, three bodyguards and a bodyguard try to find vengance.`,
|
||||
`In a kingdom of corruption and battle, seven artificial intelligences try to save the last living fertile woman.`,
|
||||
`In a universe of virutal reality and agony, in an age of ghosts and ghosts, a fortune-teller and a wanderer try to avert the apocalypse.`,
|
||||
`In a crime-infested kingdom, three martial artists quest for the truth and oppose evil.`,
|
||||
`In a terrifying universe of lost souls, in an era of lost souls, eight dancers fight evil.`,
|
||||
`In a galaxy of confusion and insanity, three martial artists and a duke battle a mob of psychics.`,
|
||||
`In an amazing kingdom, a wizard and a secretary hope to prevent the destruction of mankind.`,
|
||||
`In a kingdom of deception, a reporter searches for fame.`,
|
||||
`In a hellish empire, a swordswoman and a duke try to find the ultimate weapon and battle a conspiracy.`,
|
||||
`In an evil galaxy of illusion, in a time of technology and misery, seven psychiatrists battle crime.`,
|
||||
`In a dark city of confusion, three swordswomen and a singer battle lawlessness.`,
|
||||
`In an ominous empire, in an age of hate, two philosophers and a student try to find justice and battle a mob of mages intent on stealing the souls of the innocent.`,
|
||||
`In a kingdom of panic, six adventurers oppose lawlessness.`,
|
||||
`In a land of dreams and hopelessness, three hackers and a cyborg search for justice.`,
|
||||
`On a planet of mysticism, three travelers and a fire fighter quest for the ultimate weapon and oppose evil.`,
|
||||
`In a wicked universe, five seers fight lawlessness.`,
|
||||
`In a kingdom of death, in an era of illusion and blood, four colonists search for fame.`,
|
||||
`In an amazing kingdom, in an age of sorcery and lost souls, eight space pirates quest for freedom.`,
|
||||
`In a cursed empire, five inventors oppose terrorism.`,
|
||||
`On a crime-ridden planet of conspiracy, a watchman and an artificial intelligence try to find love and oppose lawlessness.`,
|
||||
`In a forgotten land, a reporter and a spy try to stop the apocalypse.`,
|
||||
`In a forbidden land of prophecy, a scientist and an archivist oppose a cabal of barbarians intent on stealing the souls of the innocent.`,
|
||||
`On an infernal world of illusion, a grave robber and a watchman try to find revenge and combat a syndicate of mages intent on stealing the source of all magic.`,
|
||||
`In a galaxy of dark magic, four fighters seek freedom.`,
|
||||
`In an empire of deception, six tomb-robbers quest for the ultimate weapon and combat an army of raiders.`,
|
||||
`In a kingdom of corruption and lost souls, in an age of panic, eight planetologists oppose evil.`,
|
||||
`In a galaxy of misery and hopelessness, in a time of agony and pain, five planetologists search for vengance.`,
|
||||
`In a universe of technology and insanity, in a time of sorcery, a computer techician quests for hope.`,
|
||||
`On a planet of dark magic and barbarism, in an age of horror and blasphemy, seven librarians search for fame.`,
|
||||
`In an empire of dark magic, in a time of blood and illusions, four monks try to find the ultimate weapon and combat terrorism.`,
|
||||
`In a forgotten empire of dark magic, six kings try to prevent the destruction of mankind.`,
|
||||
`In a galaxy of dark magic and horror, in an age of hopelessness, four marines and an outlaw combat evil.`,
|
||||
`In a mysterious city of illusion, in an age of computerization, a witch-hunter tries to find the ultimate weapon and opposes an evil corporation.`,
|
||||
`In a damned kingdom of technology, a virtual reality programmer and a fighter seek fame.`,
|
||||
`In a hellish kingdom, in an age of blasphemy and blasphemy, an astrologer searches for fame.`,
|
||||
`In a damned world of devils, an alien and a ranger quest for love and oppose a syndicate of demons.`,
|
||||
`In a cursed galaxy, in a time of pain, seven librarians hope to avert the apocalypse.`,
|
||||
`In a crime-infested galaxy, in an era of hopelessness and panic, three champions and a grave robber try to solve the ultimate crime.`
|
||||
],
|
||||
|
||||
classes : [
|
||||
'Archivist',
|
||||
'Armadillomaster',
|
||||
'Beat Priest',
|
||||
'Beer Mentalist',
|
||||
'Berserker-Typist',
|
||||
'Bonsai Hooligan',
|
||||
'Candy Finder',
|
||||
'Coffeemancer',
|
||||
'Concierge',
|
||||
'Corn Theif',
|
||||
'Cottonsmith',
|
||||
'Dirtmistress',
|
||||
'Fancyman',
|
||||
'Fishmongerer',
|
||||
'Fletcher',
|
||||
'Flow Robber',
|
||||
'Haberdasher',
|
||||
'Hamster Lady',
|
||||
'Jam Robber',
|
||||
'Linguist',
|
||||
'Lizard Trainer',
|
||||
'Manicurist',
|
||||
'Markermaster',
|
||||
'Mint Handler',
|
||||
'Narwhalologer',
|
||||
'Notary',
|
||||
'Otter Mentalist',
|
||||
'Plastic Diviner',
|
||||
'Rhymemancer',
|
||||
'Rum Buster',
|
||||
'Whaleologer',
|
||||
],
|
||||
|
||||
|
||||
gear : [
|
||||
`a squeegee`,
|
||||
'6 rubber chickens',
|
||||
'10 lint fluffs',
|
||||
'1 button',
|
||||
'a cherished lost sock',
|
||||
'a small doll',
|
||||
'hopes and dreams',
|
||||
'1st born child',
|
||||
'3rd born child',
|
||||
'a crushed button worth at least 1cp',
|
||||
'discarded gum wrapper',
|
||||
`Broch of Air Blasts`,
|
||||
`Elven Leather Armor`,
|
||||
`Glaive of the Deathly Viper`,
|
||||
`Mystical Eagle's Ointment of the Eagles`,
|
||||
`Mystical Scintillating Cudgel`,
|
||||
`Wise Thinker's Anklet`,
|
||||
`The four fragments of the Disk of Madness`
|
||||
],
|
||||
|
||||
|
||||
|
||||
spellNames : [
|
||||
"Astral Rite of Acne",
|
||||
"Create Acne",
|
||||
"Cursed Ramen Erruption",
|
||||
"Dark Chant of the Dentists",
|
||||
"Erruption of Immaturity",
|
||||
"Flaming Disc of Inconvenience",
|
||||
"Heal Bad Hygene",
|
||||
"Heavenly Transfiguration of the Cream Devil",
|
||||
"Hellish Cage of Mucus",
|
||||
"Irritate Peanut Butter Fairy",
|
||||
"Luminous Erruption of Tea",
|
||||
"Mystic Spell of the Poser",
|
||||
"Sorcerous Enchantment of the Chimneysweep",
|
||||
"Steak Sauce Ray",
|
||||
"Talk to Groupie",
|
||||
"Astonishing Chant of Chocolate",
|
||||
"Astounding Pasta Puddle",
|
||||
"Ball of Annoyance",
|
||||
"Cage of Yarn",
|
||||
"Control Noodles Elemental",
|
||||
"Create Nervousness",
|
||||
"Cure Baldness",
|
||||
"Cursed Ritual of Bad Hair",
|
||||
"Dispell Piles in Dentist",
|
||||
"Eliminate Florists",
|
||||
"Illusionary Transfiguration of the Babysitter",
|
||||
"Necromantic Armor of Salad Dressing",
|
||||
"Occult Transfiguration of Foot Fetish",
|
||||
"Protection from Mucus Giant",
|
||||
"Tinsel Blast",
|
||||
"Alchemical Evocation of the Goths",
|
||||
"Call Fangirl",
|
||||
"Divine Spell of Crossdressing",
|
||||
"Dominate Ramen Giant",
|
||||
"Eliminate Vindictiveness in Gym Teacher",
|
||||
"Extra-Planar Spell of Irritation",
|
||||
"Induce Whining in Babysitter",
|
||||
"Invoke Complaining",
|
||||
"Magical Enchantment of Arrogance",
|
||||
"Occult Globe of Salad Dressing",
|
||||
"Overwhelming Enchantment of the Chocolate Fairy",
|
||||
"Sorcerous Dandruff Globe",
|
||||
"Spiritual Invocation of the Costumers",
|
||||
"Ultimate Rite of the Confetti Angel",
|
||||
"Ultimate Ritual of Mouthwash",
|
||||
|
||||
],
|
||||
|
||||
effects : [
|
||||
'Induces politicians to parade through the streets naked, and makes the nearest unbetrothed prince or princess dance around the maypole making dirty jokes.',
|
||||
'Tricks enchanted princesses to spin straw into gold, and makes princesses trapped in towers steal from the rich and give to the poor.',
|
||||
'Drives the man or woman of your dreams to jump up and down on the spot, and makes angry dragons grow onions wherever they walk.',
|
||||
'Causes enchanted talking animals to fall down dead, and makes large pumpkins attract love-struck unicorns.',
|
||||
'Induces officers of the law to adopt small, fluffy bunnies as pets, and makes enchanted wooden puppets vomit gold coins.',
|
||||
'Causes accountants to give you all of their possessions, and makes officers of the law grow mushrooms out of their ears.',
|
||||
'Induces goats to eat until they burst, and makes men with small heads vomit gold coins.',
|
||||
'Tricks enchanted princesses to turn into small pumpkins, and makes evil landlords declare themselves king.',
|
||||
'Induces your enemies to steal from the palace cook, and makes rich merchants propose marriage.',
|
||||
'Causes evil landlords to vomit gold coins, and makes the nearest unbetrothed prince or princess drink beer.',
|
||||
'Induces men with small heads to grow mushrooms out of their ears, and makes witches steal from the rich and give to the poor.',
|
||||
`Conjures food with energy equal to whatever was used to cast the spell.`,
|
||||
`Allows a living target to withstand extreme cold.`,
|
||||
`Conjures a thick fog that acts as a smoke screen.`,
|
||||
`Creates a bubble in which time is stopped for a short period.`,
|
||||
`Creates several bolts of shadowy energy.`,
|
||||
`Causes a living target to panic for a period of time.`,
|
||||
`Creates a floating scroll and quill that'll write down everything the caster or target says for a period of time.`,
|
||||
`Causes whoever is targeted to enter a state of confusion for a period of time.`,
|
||||
`Creates a magical barrier that blocks all with dark intentions or dark influences over them.`,
|
||||
`Creates a bolt of demonic energy.`,
|
||||
`Causes whoever is targeted to drop whatever they're holding.`
|
||||
],
|
||||
|
||||
effects2 : [
|
||||
'Unless they pass a Constitution save, the creature gains 1 level of Exhaustion.',
|
||||
'Pushed 5 feet unless they pass a Strength save. ',
|
||||
'Unless they pass a Wisdom save, the creature is Charmed.',
|
||||
'Unless they pass a Wisdom save, the creature is Frightened. The creature can remake this save on each of their turns.',
|
||||
'Unless they pass a Wisdom save, the creature is Frightened. The creature can remake this save on each of their turns.',
|
||||
'Unless they pass a Wisdom save, the creature is Paralyzed. The creature can remake this save on each of their turns.',
|
||||
'Pushed 25 feet unless they pass a Strength save. ',
|
||||
'Unless they pass a Constitution save, the creature is Poisoned. The creature can remake this save on each of their turns.',
|
||||
'Unless they pass a Wisdom save, the creature is Charmed.',
|
||||
'Unless they pass a Constitution save, the creature is Slowed. The creature can remake this save on each of their turns.',
|
||||
'Unless they pass a Constitution save, the creature is Slowed. The creature can remake this save on each of their turns.',
|
||||
'Knocked Prone unless they pass a Dexterity save. ',
|
||||
'Unless they pass a Constitution save, the creature is Deafened. The creature can remake this save on each of their turns.',
|
||||
'Knocked Prone unless they pass a Dexterity save. ',
|
||||
'Unless they pass a Constitution save, the creature gains 1 level of Exhaustion.',
|
||||
'Knocked Prone unless they pass a Dexterity save. ',
|
||||
'Unless they pass a Constitution save, the creature is Deafened. The creature can remake this save on each of their turns.',
|
||||
'Unless they pass a Constitution save, the creature gains 1 level of Exhaustion.',
|
||||
'Pushed 20 feet unless they pass a Strength save. ',
|
||||
'Resistance to Radiant damage until 1 round'
|
||||
|
||||
],
|
||||
|
||||
attacks : [
|
||||
`Aquatic Press of the Romantic Demons`,
|
||||
`Barbarian Raider Pinch of the Cemetary`,
|
||||
`Beetle Hold of the Fangs`,
|
||||
`Confident Badger Pinch of Lyres`,
|
||||
`Emperor's Roll of the Nine Volcanos`,
|
||||
`Firey Rake of the Endings`,
|
||||
`Fortuitous Underhook of the Wolves`,
|
||||
`God's Knee of Blessings`,
|
||||
`Hawk Dance`,
|
||||
`Heavenly Rat's Roll`,
|
||||
`Hellish Meteor`,
|
||||
`High Noose of the Ruthless Guardian`,
|
||||
`Hold of Poisons`,
|
||||
`King Drop of the Fighting Protectors`,
|
||||
`Leg Clap of the Dogs`,
|
||||
`Northeastern Seventeen Cats Claw`,
|
||||
`Phantasmal Plague Finger`,
|
||||
`Pose of Perfect Sunsets`,
|
||||
`Seal Hammer of the Forty Sages`,
|
||||
`Shaman Pull of Destructions`,
|
||||
`Southeastern Automaton Pull`,
|
||||
`Southwestern Eighty Chants Clap`,
|
||||
`Tackle of Foul Leaves`,
|
||||
`Tornado of the Uncounted Hawks`,
|
||||
`Yielding Throw of the Mills`,
|
||||
],
|
||||
|
||||
abilities : [
|
||||
"Astrological Botany",
|
||||
"Astrological Chemistry",
|
||||
"Biochemical Sorcery",
|
||||
"Civil Alchemy",
|
||||
"Consecrated Biochemistry",
|
||||
"Demonic Anthropology",
|
||||
"Divinatory Mineralogy",
|
||||
"Genetic Banishing",
|
||||
"Hermetic Geography",
|
||||
"Immunological Incantations",
|
||||
"Nuclear Illusionism",
|
||||
"Ritual Astronomy",
|
||||
"Seismological Divination",
|
||||
"Spiritual Biochemistry",
|
||||
"Statistical Occultism",
|
||||
"Police Necromancer",
|
||||
"Sixgun Poisoner",
|
||||
"Pharmaceutical Gunslinger",
|
||||
"Infernal Banker",
|
||||
"Spell Analyst",
|
||||
"Gunslinger Corruptor",
|
||||
"Torque Interfacer",
|
||||
"Exo Interfacer",
|
||||
"Gunpowder Torturer",
|
||||
"Orbital Gravedigger",
|
||||
"Phased Linguist",
|
||||
"Mathematical Pharmacist",
|
||||
"Plasma Outlaw",
|
||||
"Malefic Chemist",
|
||||
"Police Cultist"
|
||||
],
|
||||
|
||||
alignments : [
|
||||
"Annoying Evil",
|
||||
"Chaotic Gossipy",
|
||||
"Chaotic Sloppy",
|
||||
"Depressed Neutral",
|
||||
"Lawful Bogus",
|
||||
"Lawful Coy",
|
||||
"Manic-Depressive Evil",
|
||||
"Narrow-Minded Neutral",
|
||||
"Neutral Annoying",
|
||||
"Neutral Ignorant",
|
||||
"Oedpipal Neutral",
|
||||
"Silly Neutral",
|
||||
"Unoriginal Neutral",
|
||||
"Weird Neutral",
|
||||
"Wordy Evil",
|
||||
"Unaligned",
|
||||
"Lawful Gossipy",
|
||||
"Neurotic Good",
|
||||
"Sarcastic Evil",
|
||||
"Snotty Neutral",
|
||||
"Wannabe Good"
|
||||
],
|
||||
|
||||
sizes : ['Microscopic', 'Tiny', 'Small', 'Medium', 'Large', 'Gargantuan', 'Stupidly vast'],
|
||||
levels : ["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th", "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th", "20th"],
|
||||
|
||||
|
||||
sentences : [
|
||||
`The suspicion arises the narrator of the tale is actually a demon.`,
|
||||
`There is a predicted hurricane - but it's not what was expected, and this complicates the plans of the protagonist.`,
|
||||
`The antagonist's believes their life has changed for the strange - this turns out to be this is due to being lied to by others`,
|
||||
`An accidental cuddle leads to complications.`,
|
||||
`It's revealed that everything that is happening is all a dream.`,
|
||||
`There is a sudden hurricane.`,
|
||||
`The alternate protagonist is revealed to be a different race/species than thought, which suddenly makes what's going on much clearer.`,
|
||||
`Thanks to alien forces, the characters end up in the earth's past.`,
|
||||
`Thanks to alien forces, the secondary protagonist ends up in a world after an apocalypse.`,
|
||||
`Due to a panic attack a character has to get psychological therapy.`,
|
||||
],
|
||||
|
||||
|
||||
|
||||
creatures : [
|
||||
"All-devouring Baseball Imp",
|
||||
"All-devouring Gumdrop Wraith",
|
||||
"Chocolate Hydra",
|
||||
"Devouring Peacock",
|
||||
"Economy-sized Colossus of the Lemonade Stand",
|
||||
"Ghost Pigeon",
|
||||
"Gibbering Duck",
|
||||
"Sparklemuffin Peacock Spider",
|
||||
"Gum Elemental",
|
||||
"Illiterate Construct of the Candy Store",
|
||||
"Ineffable Chihuahua",
|
||||
"Irritating Death Hamster",
|
||||
"Irritating Gold Mouse",
|
||||
"Juggernaut Snail",
|
||||
"Juggernaut of the Sock Drawer",
|
||||
"Koala of the Cosmos",
|
||||
"Mad Koala of the West",
|
||||
"Milk Djinni of the Lemonade Stand",
|
||||
"Mind Ferret",
|
||||
"Mystic Salt Spider",
|
||||
"Necrotic Halitosis Angel",
|
||||
"Pinstriped Famine Sheep",
|
||||
"Ritalin Leech",
|
||||
"Shocker Kangaroo",
|
||||
"Stellar Tennis Juggernaut",
|
||||
"Wailing Quail of the Sun",
|
||||
"Angel Pigeon",
|
||||
"Anime Sphinx",
|
||||
"Bored Avalanche Sheep of the Wasteland",
|
||||
"Devouring Nougat Sphinx of the Sock Drawer",
|
||||
"Djinni of the Footlocker",
|
||||
"Ectoplasmic Jazz Devil",
|
||||
"Flatuent Angel",
|
||||
"Gelatinous Duck of the Dream-Lands",
|
||||
"Gelatinous Mouse",
|
||||
"Golem of the Footlocker",
|
||||
"Lich Wombat",
|
||||
"Mechanical Sloth of the Past",
|
||||
"Milkshake Succubus",
|
||||
"Puffy Bone Peacock of the East",
|
||||
"Rainbow Manatee",
|
||||
"Rune Parrot",
|
||||
"Sand Cow",
|
||||
"Sinister Vanilla Dragon",
|
||||
"Snail of the North",
|
||||
"Spider of the Sewer",
|
||||
"Stellar Sawdust Leech",
|
||||
"Storm Anteater of Hell",
|
||||
"Stupid Spirit of the Brewery",
|
||||
"Time Kangaroo",
|
||||
"Tomb Poodle"
|
||||
|
||||
]
|
||||
|
||||
};
|
||||
|
||||
module.exports = Data;
|
||||
41
shared/homebrewery/snippets/brew/spell.snippet.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const _ = require('lodash');
|
||||
const Data = require('./random.data.js');
|
||||
|
||||
|
||||
const levels = ['1st', '2nd', '3rd', '4th', '5th', '6th', '7th', '8th', '9th'];
|
||||
const schools = ['abjuration', 'conjuration', 'divination', 'enchantment', 'evocation', 'illusion', 'necromancy', 'transmutation'];
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
spell : ()=>{
|
||||
|
||||
let components = _.sampleSize(['V', 'S', 'M'], _.random(1,3)).join(', ');
|
||||
if(components.indexOf('M') !== -1){
|
||||
components += ` (${Data.rand('gear',3).join(', ')})`
|
||||
}
|
||||
|
||||
const duration = _.sample([
|
||||
'Until dispelled',
|
||||
'1 round',
|
||||
'Instantaneous',
|
||||
'Concentration, up to 10 minutes',
|
||||
'1 hour'
|
||||
]);
|
||||
|
||||
const description = Data.rand('effects', 2).concat(Data.rand('effects2')).join(' ');
|
||||
|
||||
|
||||
return `{{spell
|
||||
#### ${_.sample(Data.spellNames)}
|
||||
*${_.sample(levels)}-level ${_.sample(schools)}*
|
||||
- **Casting Time:** ${_.sample(['1 action', 'Reaction', '10 minutes', '1 hour'])}
|
||||
- **Range:** ${_.sample(['Self', 'Touch', '30 feet', '60 feet'])}
|
||||
- **Components:** ${components}
|
||||
- **Duration:** ${duration}
|
||||
|
||||
${description}
|
||||
}}`;
|
||||
|
||||
}
|
||||
}
|
||||
62
shared/homebrewery/snippets/brew/table.snippet.js
Normal file
@@ -0,0 +1,62 @@
|
||||
const _ = require('lodash');
|
||||
const Data = require('./random.data.js');
|
||||
|
||||
|
||||
|
||||
/*
|
||||
- Roll
|
||||
- Level
|
||||
- Cost
|
||||
|
||||
- spell lists
|
||||
- cost
|
||||
- Class
|
||||
|
||||
*/
|
||||
|
||||
|
||||
const columns = {
|
||||
roll : (rows)=>{
|
||||
return _.concat([`d${rows}`, ':---:'], _.times(rows, (i)=>i+1));
|
||||
},
|
||||
level : (rows)=>{
|
||||
return _.concat([`${_.sample(Data.classes)} Level`, ':---:'], _.times(rows, (i)=>Data.levels[i*2]));
|
||||
},
|
||||
|
||||
spell : (rows)=>{
|
||||
return _.concat(['Spells', ':---'], _.times(rows, (i)=>{
|
||||
return `_${Data.rand('spellNames', 2).join(', ')}_`
|
||||
}));
|
||||
},
|
||||
cost : (rows)=>{
|
||||
return _.concat([`Cost`, '---:'], _.times(rows, (i)=>{
|
||||
return _.sample(['1 gp', '10 gp', '5 cp', '10,000 gp', '200 sp', '1 pp', '2 gp']);
|
||||
}));
|
||||
},
|
||||
gear : (rows)=>{
|
||||
return _.concat([_.sample(['Equipment', 'Reward', 'Treasure']), ':---'], _.times(rows, (i)=>{
|
||||
return Data.rand('gear');
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
table : () => {
|
||||
const rows = _.sample([4,6,8,10]);
|
||||
|
||||
const cols = [
|
||||
columns.roll(rows),
|
||||
columns.level(rows),
|
||||
columns.gear(rows)
|
||||
];
|
||||
|
||||
return _.times(rows + 2, (i)=>{
|
||||
if(i==1){
|
||||
return '|' + _.map(cols, (col)=>col[i]).join('|') + '|';
|
||||
}else{
|
||||
return '| ' + _.map(cols, (col)=>col[i]).join(' | ') + ' |';
|
||||
}
|
||||
}).join('\n');
|
||||
}
|
||||
}
|
||||
112
shared/homebrewery/snippets/brew/toc.snippet.js
Normal file
@@ -0,0 +1,112 @@
|
||||
const _ = require('lodash');
|
||||
const Store = require('homebrewery/brew.store.js');
|
||||
|
||||
const getTOC = (text) => {
|
||||
const pages = text.split('\\page');
|
||||
const add1 = (title, page)=>{
|
||||
res.push({
|
||||
title : title,
|
||||
page : page + 1,
|
||||
children : []
|
||||
});
|
||||
}
|
||||
const add2 = (title, page)=>{
|
||||
if(!_.last(res)) add1('', page);
|
||||
_.last(res).children.push({
|
||||
title : title,
|
||||
page : page + 1,
|
||||
children : []
|
||||
});
|
||||
}
|
||||
const add3 = (title, page)=>{
|
||||
if(!_.last(res)) add1('', page);
|
||||
if(!_.last(_.last(res).children)) add2('', page);
|
||||
_.last(_.last(res).children).children.push({
|
||||
title : title,
|
||||
page : page + 1,
|
||||
children : []
|
||||
});
|
||||
}
|
||||
|
||||
let res = [];
|
||||
_.each(pages, (page, pageNum)=>{
|
||||
const lines = page.split('\n');
|
||||
_.each(lines, (line) => {
|
||||
if(_.startsWith(line, '# ')){
|
||||
const title = line.replace('# ', '');
|
||||
add1(title, pageNum)
|
||||
}
|
||||
if(_.startsWith(line, '## ')){
|
||||
const title = line.replace('## ', '');
|
||||
add2(title, pageNum);
|
||||
}
|
||||
if(_.startsWith(line, '### ')){
|
||||
const title = line.replace('### ', '');
|
||||
add3(title, pageNum);
|
||||
}
|
||||
})
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
//TODO: TOC not perfect yet
|
||||
|
||||
toc : (text)=>{
|
||||
text = text || Store.getBrewCode();
|
||||
|
||||
console.log(getTOC(text));
|
||||
|
||||
const TOC = getTOC(text)
|
||||
|
||||
const markdown = _.reduce(TOC, (r, g1, idx1)=>{
|
||||
r.push(`- ### [**${g1.page}** *${g1.title}*](#p${g1.page})`)
|
||||
if(g1.children.length){
|
||||
_.each(g1.children, (g2, idx2) => {
|
||||
r.push(` - #### [**${g2.page}** *${g2.title}*](#p${g2.page})`)
|
||||
if(g2.children.length){
|
||||
_.each(g2.children, (g3, idx3) => {
|
||||
r.push(` - [**${g3.page}** *${g3.title}*](#p${g3.page})`)
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return r;
|
||||
}, []).join('\n');
|
||||
|
||||
|
||||
|
||||
return `{{toc
|
||||
# Contents
|
||||
|
||||
${markdown}
|
||||
|
||||
}}`;
|
||||
/*
|
||||
|
||||
- ### [**4** *Preface*](#p3)
|
||||
- ### [**5** *Introduction*](#p3)
|
||||
- [**5** *Worlds of Adventure*](#p5)
|
||||
- [**6** *Using This Book*](#p5)
|
||||
- [**6** *How to Play*](#p5)
|
||||
- [**7** *Adventures*](#p5)
|
||||
|
||||
- ### [**5** *Introduction*](#p3)
|
||||
- #### [**5** *Worlds of Adventure*](#p5)
|
||||
- [**6** *Using This Book*](#p5)
|
||||
- [**6** *How to Play*](#p5)
|
||||
- #### [**7** *Adventures*](#p5)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}}
|
||||
|
||||
|
||||
`;*/
|
||||
}
|
||||
}
|
||||
4
shared/homebrewery/snippets/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
brew : require('./brew'),
|
||||
style : require('./style')
|
||||
}
|
||||
8
shared/homebrewery/snippets/style/a4.snippet.js
Normal file
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
a4 : ()=>{
|
||||
return `.phb{
|
||||
width : 210mm;
|
||||
height : 296.8mm;
|
||||
}`;
|
||||
}
|
||||
}
|
||||
12
shared/homebrewery/snippets/style/bg.snippet.js
Normal file
@@ -0,0 +1,12 @@
|
||||
module.exports = {
|
||||
dmg : ()=>{
|
||||
return `.phb{
|
||||
background-image: url('/assets/homebrewery/phb_style/img/dmg_bg.jpg');
|
||||
}`;
|
||||
},
|
||||
dark: ()=>{
|
||||
return `.phb{
|
||||
background-image: url('/assets/homebrewery/phb_style/img/phb_dark_bg.jpg');
|
||||
}`;
|
||||
}
|
||||
}
|
||||
7
shared/homebrewery/snippets/style/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const _ = require('lodash');
|
||||
|
||||
module.exports = _.merge(
|
||||
require('./ink.snippet.js'),
|
||||
require('./a4.snippet.js'),
|
||||
require('./bg.snippet.js')
|
||||
);
|
||||
9
shared/homebrewery/snippets/style/ink.snippet.js
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
module.exports = {
|
||||
inkFriendly : ()=>{
|
||||
return `.phb{ background : white;}
|
||||
.phb img{ display : none;}
|
||||
.phb hr+blockquote{background : white;}`;
|
||||
}
|
||||
}
|
||||
@@ -38,28 +38,29 @@ var Nav = {
|
||||
href : null,
|
||||
newTab : false,
|
||||
onClick : function(){},
|
||||
color : null
|
||||
color : null,
|
||||
collaspe : false
|
||||
};
|
||||
},
|
||||
handleClick : function(){
|
||||
this.props.onClick();
|
||||
},
|
||||
render : function(){
|
||||
var classes = cx('navItem', this.props.color, this.props.className);
|
||||
var classes = cx('navItem', this.props.color, this.props.className, {collaspe : this.props.collaspe});
|
||||
|
||||
var icon;
|
||||
if(this.props.icon) icon = <i className={'fa ' + this.props.icon} />;
|
||||
|
||||
const props = _.omit(this.props, ['newTab']);
|
||||
const props = _.omit(this.props, ['newTab', 'collaspe']);
|
||||
|
||||
if(this.props.href){
|
||||
return <a {...props} className={classes} target={this.props.newTab ? '_blank' : '_self'} >
|
||||
{this.props.children}
|
||||
<span>{this.props.children}</span>
|
||||
{icon}
|
||||
</a>
|
||||
}else{
|
||||
return <div {...props} className={classes} onClick={this.handleClick} >
|
||||
{this.props.children}
|
||||
<span>{this.props.children}</span>
|
||||
{icon}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
nav{
|
||||
background-color : #333;
|
||||
.navContent{
|
||||
@@ -41,6 +42,7 @@ nav{
|
||||
}
|
||||
.navItem{
|
||||
.animate(background-color);
|
||||
display : inline-block;
|
||||
padding : 8px 12px;
|
||||
cursor : pointer;
|
||||
background-color : #333;
|
||||
@@ -53,6 +55,28 @@ nav{
|
||||
margin-left : 5px;
|
||||
font-size : 13px;
|
||||
}
|
||||
&.collaspe{
|
||||
overflow : hidden;
|
||||
i{
|
||||
margin-left : 0px;
|
||||
}
|
||||
span{
|
||||
display : inline-block;
|
||||
visibility : hidden;
|
||||
overflow : hidden;
|
||||
width : 0px;
|
||||
white-space : nowrap;
|
||||
}
|
||||
&:hover{
|
||||
span{
|
||||
visibility : visible;
|
||||
width : auto;
|
||||
}
|
||||
i{
|
||||
margin-left : 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.tealLight:hover{ background-color : @tealLight };
|
||||
&.teal:hover{ background-color : @teal };
|
||||
&.greenLight:hover{ background-color : @greenLight };
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# changelog
|
||||
|
||||
|
||||
The self-discovery aspect of the snippets isn't working out.
|
||||
|
||||
|
||||
## BIG NEWS
|
||||
With the next major release of Homebrewery, v3.0.0, this tool *will no longer support raw HTML input for brew code*. Most issues and errors users are having are because of this feature and it's become too taxing to help and fix these issues.
|
||||
|
||||
107
statics/faq.md
Normal file
@@ -0,0 +1,107 @@
|
||||
- Submitting work created on this site to DMs Guild
|
||||
|
||||
|
||||
# I lost my brew?
|
||||
- If you made any edits with an account, you can go to that account's page
|
||||
- Homebrewery stores the last handful of brews you've viewed or edited under the recent brews tab
|
||||
- Check your browser history for the edit link
|
||||
- If all of that fails, find the share link, open the source and copy it into a new brew.
|
||||
|
||||
|
||||
# Images
|
||||
Image basics
|
||||
- background images
|
||||
- Adding brushes
|
||||
|
||||
|
||||
How to make spacers
|
||||
|
||||
|
||||
- How to skip page numbers
|
||||
- How to set page number
|
||||
- How to hide footers
|
||||
#p1:before, #p1:after{ display:none }
|
||||
#p2:before{ counter-reset: phb-page-numbers 30; }
|
||||
|
||||
|
||||
- blockquotes, cite
|
||||
|
||||
styling images
|
||||
|
||||
|
||||
# Print
|
||||
- Saving ink
|
||||
- Changing page size
|
||||
- Printing to PDF
|
||||
|
||||
|
||||
|
||||
## Changing backgrounds
|
||||
{{wide
|
||||
In style
|
||||
```
|
||||
#p3{
|
||||
background-image : url('/assets/homebrewery/phb_style/img/dmg_bg.jpg')
|
||||
}
|
||||
|
||||
```
|
||||
}}
|
||||
|
||||
## Changes in v3
|
||||
|
||||
``` ``` -> \column
|
||||
|
||||
|
||||
\page
|
||||
|
||||
## Columns
|
||||
{{wide,twoColumn
|
||||
This is how columns work sdfsdfsdf
|
||||
```
|
||||
{{twoColumn
|
||||
|
||||
| d4 | Manicurist Level | Equipment |
|
||||
|:---:|:---:|:---|
|
||||
| 1 | 1st | The four fragments of the Disk of Madness |
|
||||
| 2 | 3rd | Broch of Air Blasts |
|
||||
| 3 | 5th | The four fragments of the Disk of Madness |
|
||||
| 4 | 7th | 3rd born child |
|
||||
|
||||
|
||||
| d4 | Manicurist Level | Equipment |
|
||||
|:---:|:---:|:---|
|
||||
| 1 | 1st | The four fragments of the Disk of Madness |
|
||||
| 2 | 3rd | Broch of Air Blasts |
|
||||
| 3 | 5th | The four fragments of the Disk of Madness |
|
||||
| 4 | 7th | 3rd born child |
|
||||
|
||||
}}
|
||||
|
||||
```
|
||||
|
||||
\column
|
||||
|
||||
|
||||
{{twoColumn
|
||||
|
||||
| d4 | Manicurist Level | Equipment |
|
||||
|:---:|:---:|:---|
|
||||
| 1 | 1st | The four fragments of the Disk of Madness |
|
||||
| 2 | 3rd | Broch of Air Blasts |
|
||||
| 3 | 5th | The four fragments of the Disk of Madness |
|
||||
| 4 | 7th | 3rd born child |
|
||||
|
||||
|
||||
| d4 | Manicurist Level | Equipment |
|
||||
|:---:|:---:|:---|
|
||||
| 1 | 1st | The four fragments of the Disk of Madness |
|
||||
| 2 | 3rd | Broch of Air Blasts |
|
||||
| 3 | 5th | The four fragments of the Disk of Madness |
|
||||
| 4 | 7th | 3rd born child |
|
||||
|
||||
}}
|
||||
|
||||
}}
|
||||
|
||||
this is after
|
||||
|
||||
@@ -18,22 +18,53 @@ Like this tool? Want to buy me a beer? [Head here](https://www.patreon.com/stolk
|
||||
|
||||
This tool will **always** be free, never have ads, and I will never offer any "premium" features or whatever.
|
||||
|
||||
{{note,yellow,alt
|
||||
##### PDF Exporting
|
||||
PDF Printing works best in Chrome. If you are having quality/consistency issues, try using Chrome to print instead.
|
||||
|
||||
After clicking the "Print" item in the navbar a new page will open and a print dialog will pop-up.
|
||||
* Set the **Destination** to "Save as PDF"
|
||||
* Set **Paper Size** to "Letter"
|
||||
* If you are printing on A4 paper, make sure to have the "A4 page size snippet" in your brew
|
||||
* In **Options** make sure "Background Images" is selected.
|
||||
* Hit print and enjoy! You're done!
|
||||
|
||||
If you want to save ink or have a monochrome printer, add the **Ink Friendly** snippet to your brew before you print
|
||||
|
||||
|
||||
>##### PDF Exporting
|
||||
> PDF Printing works best in Chrome. If you are having quality/consistency issues, try using Chrome to print instead.
|
||||
}}
|
||||
|
||||
> This is a cool blockquote fdgfgsfg sfd sdfsdfsdfsdfsdfsdf sdfsdfsdfssdfsdffgsdfgsdfg
|
||||
> You know?
|
||||
>
|
||||
> After clicking the "Print" item in the navbar a new page will open and a print dialog will pop-up.
|
||||
> * Set the **Destination** to "Save as PDF"
|
||||
> * Set **Paper Size** to "Letter"
|
||||
> * If you are printing on A4 paper, make sure to have the "A4 page size snippet" in your brew
|
||||
> * In **Options** make sure "Background Images" is selected.
|
||||
> * Hit print and enjoy! You're done!
|
||||
>
|
||||
> If you want to save ink or have a monochrome printer, add the **Ink Friendly** snippet to your brew before you print
|
||||
> One with quotes and what not
|
||||
> yeah yeah yeah
|
||||
> {{cite -- Very cool person }}
|
||||
|
||||
|
||||
{{note
|
||||
##### PDF Exporting
|
||||
PDF Printing works best in Chrome. If you are having quality/consistency issues, try using Chrome to print instead.
|
||||
|
||||
After clicking the "Print" item in the navbar a new page will open and a print dialog will pop-up.
|
||||
* Set the **Destination** to "Save as PDF"
|
||||
* Set **Paper Size** to "Letter"
|
||||
* If you are printing on A4 paper, make sure to have the "A4 page size snippet" in your brew
|
||||
* In **Options** make sure "Background Images" is selected.
|
||||
* Hit print and enjoy! You're done!
|
||||
|
||||
If you want to save ink or have a monochrome printer, add the **Ink Friendly** snippet to your brew before you print
|
||||
|
||||
|
||||
}}
|
||||
|
||||
|
||||

|
||||
|
||||
```
|
||||
cool stuff
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Big things coming in v3.0.0
|
||||
@@ -55,18 +86,15 @@ If you are looking for more 5e Homebrew resources check out [r/UnearthedArcana](
|
||||
|
||||
|
||||
|
||||
<img src='http://i.imgur.com/hMna6G0.png' style='position:absolute;bottom:50px;right:30px;width:280px' />
|
||||
|
||||
<div class='pageNumber'>1</div>
|
||||
<div class='footnote'>PART 1 | FANCINESS</div>
|
||||
|
||||
|
||||
{{footnote PART 1 | FANCINESS }}
|
||||
|
||||
|
||||
\page
|
||||
|
||||
|
||||
{{classTable,wide
|
||||
{{frame,wide
|
||||
##### The Archivist
|
||||
| Level | Proficiency Bonus | Features | Statistical Occultism|
|
||||
|:---:|:---:|:---|:---:|
|
||||
|
||||