diff --git a/.eslintrc.js b/.eslintrc.js index dd4bcd0d3..74e7bb660 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -15,7 +15,7 @@ module.exports = { rules : { /** Errors **/ 'camelcase' : ['error', { properties: 'never' }], - 'func-style' : ['error', 'expression', { allowArrowFunctions: true }], + //'func-style' : ['error', 'expression', { allowArrowFunctions: true }], 'no-array-constructor' : 'error', 'no-iterator' : 'error', 'no-nested-ternary' : 'error', diff --git a/changelog.md b/changelog.md index bfdb278e4..626a992d9 100644 --- a/changelog.md +++ b/changelog.md @@ -80,6 +80,63 @@ pre { ## changelog For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery). +### Thursday 17/08/2023 - v3.9.2 +{{taskList + +##### Calculuschild + +* [x] Fix links to certain old Google Drive files + +Fixes issue [#2917](https://github.com/naturalcrit/homebrewery/issues/2917) + +##### G-Ambatte + +* [x] Menus now open on click, and internally consistent + +Fixes issue [#2702](https://github.com/naturalcrit/homebrewery/issues/2702), [#2782](https://github.com/naturalcrit/homebrewery/issues/2782) + +* [x] Add smarter footer snippet + +Fixes issue [#2289](https://github.com/naturalcrit/homebrewery/issues/2289) + +* [x] Add sanitization in Style editor + +Fixes issue [#1437](https://github.com/naturalcrit/homebrewery/issues/1437) + +* [x] Rework class table snippets to remove unnecessary randomness + +Fixes issue [#2964](https://github.com/naturalcrit/homebrewery/issues/2964) + +* [x] Add User Page link to Google Drive file for file owners, add icons for additional storage locations + +Fixes issue [#2954](https://github.com/naturalcrit/homebrewery/issues/2954) + +* [x] Add default save location selection to Account Page + +Fixes issue [#2943](https://github.com/naturalcrit/homebrewery/issues/2943) + +##### 5e-Cleric + +* [x] Exclude cover pages from Table of Content generation (editing on mobile is still not recommended) + +Fixes issue [#2920](https://github.com/naturalcrit/homebrewery/issues/2920) + +##### Gazook89 + +* [x] Adjustments to improve mobile viewing + +}} + +### Wednesday 28/06/2023 - v3.9.1 +{{taskList + +##### G-Ambatte + +* [x] Better error pages with more useful information + +Fixes issue [#1924](https://github.com/naturalcrit/homebrewery/issues/1924) +}} + ### Friday 02/06/2023 - v3.9.0 {{taskList diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 27fef7e16..51921c8ca 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -108,6 +108,12 @@ const BrewRenderer = createClass({ return false; }, + sanitizeScriptTags : function(content) { + return content + .replace(/'; - const rendered = Markdown.render(source); - expect(rendered).toMatch('

<script></script>

\n'); -}); - test('Processes the markdown within an HTML block if its just a class wrapper', function() { const source = '
*Bold text*
'; const rendered = Markdown.render(source); diff --git a/themes/Legacy/5ePHB/snippets/tableOfContents.gen.js b/themes/Legacy/5ePHB/snippets/tableOfContents.gen.js index 4082ac4ef..40d64af22 100644 --- a/themes/Legacy/5ePHB/snippets/tableOfContents.gen.js +++ b/themes/Legacy/5ePHB/snippets/tableOfContents.gen.js @@ -47,8 +47,8 @@ const getTOC = (pages)=>{ return res; }; -module.exports = function(brew){ - const pages = brew.text.split('\\page'); +module.exports = function(props){ + const pages = props.brew.text.split('\\page'); const TOC = getTOC(pages); const markdown = _.reduce(TOC, (r, g1, idx1)=>{ r.push(`- **[${idx1 + 1} ${g1.title}](#p${g1.page})**`); diff --git a/themes/V3/5ePHB/snippets.js b/themes/V3/5ePHB/snippets.js index dba4ad8d5..506bc0edd 100644 --- a/themes/V3/5ePHB/snippets.js +++ b/themes/V3/5ePHB/snippets.js @@ -19,16 +19,6 @@ module.exports = [ icon : 'fas fa-pencil-alt', view : 'text', snippets : [ - { - name : 'Page Number', - icon : 'fas fa-bookmark', - gen : '{{pageNumber 1}}\n{{footnote PART 1 | SECTION NAME}}\n\n' - }, - { - name : 'Auto-incrementing Page Number', - icon : 'fas fa-sort-numeric-down', - gen : '{{pageNumber,auto}}\n{{footnote PART 1 | SECTION NAME}}\n\n' - }, { name : 'Table of Contents', icon : 'fas fa-book', @@ -230,34 +220,51 @@ module.exports = [ view : 'text', snippets : [ { - name : 'Class Table', - icon : 'fas fa-table', - gen : ClassTableGen.full('classTable,frame,decoration,wide'), - }, - { - name : 'Class Table (unframed)', - icon : 'fas fa-border-none', - gen : ClassTableGen.full('classTable,wide'), - }, - { - name : '1/2 Class Table', - icon : 'fas fa-list-alt', - gen : ClassTableGen.half('classTable,decoration,frame'), - }, - { - name : '1/2 Class Table (unframed)', - icon : 'fas fa-border-none', - gen : ClassTableGen.half('classTable'), - }, - { - name : '1/3 Class Table', - icon : 'fas fa-border-all', - gen : ClassTableGen.third('classTable,frame'), - }, - { - name : '1/3 Class Table (unframed)', - icon : 'fas fa-border-none', - gen : ClassTableGen.third('classTable'), + name : 'Class Tables', + icon : 'fas fa-table', + gen : ClassTableGen.full('classTable,frame,decoration,wide'), + subsnippets : [ + { + name : 'Martial Class Table', + icon : 'fas fa-table', + gen : ClassTableGen.non('classTable,frame,decoration'), + }, + { + name : 'Martial Class Table (unframed)', + icon : 'fas fa-border-none', + gen : ClassTableGen.non('classTable'), + }, + { + name : 'Full Caster Class Table', + icon : 'fas fa-table', + gen : ClassTableGen.full('classTable,frame,decoration,wide'), + }, + { + name : 'Full Caster Class Table (unframed)', + icon : 'fas fa-border-none', + gen : ClassTableGen.full('classTable,wide'), + }, + { + name : 'Half Caster Class Table', + icon : 'fas fa-list-alt', + gen : ClassTableGen.half('classTable,frame,decoration,wide'), + }, + { + name : 'Half Caster Class Table (unframed)', + icon : 'fas fa-border-none', + gen : ClassTableGen.half('classTable,wide'), + }, + { + name : 'Third Caster Spell Table', + icon : 'fas fa-border-all', + gen : ClassTableGen.third('classTable,frame,decoration'), + }, + { + name : 'Third Caster Spell Table (unframed)', + icon : 'fas fa-border-none', + gen : ClassTableGen.third('classTable'), + } + ] }, { name : 'Rune Table', diff --git a/themes/V3/5ePHB/snippets/classtable.gen.js b/themes/V3/5ePHB/snippets/classtable.gen.js index c1f6254f9..1fdff036f 100644 --- a/themes/V3/5ePHB/snippets/classtable.gen.js +++ b/themes/V3/5ePHB/snippets/classtable.gen.js @@ -1,132 +1,138 @@ const _ = require('lodash'); +const dedent = require('dedent-tabs').default; const features = [ - 'Astrological Botany', - 'Biochemical Sorcery', - 'Civil Divination', - 'Consecrated Augury', - 'Demonic Anthropology', - 'Divinatory Mineralogy', - 'Exo Interfacer', - 'Genetic Banishing', - 'Gunpowder Torturer', - 'Gunslinger Corruptor', - 'Hermetic Geography', - 'Immunological Cultist', - 'Malefic Chemist', - 'Mathematical Pharmacy', - 'Nuclear Biochemistry', - 'Orbital Gravedigger', - 'Pharmaceutical Outlaw', - 'Phased Linguist', - 'Plasma Gunslinger', - 'Police Necromancer', - 'Ritual Astronomy', - 'Sixgun Poisoner', - 'Seismological Alchemy', - 'Spiritual Illusionism', - 'Statistical Occultism', - 'Spell Analyst', - 'Torque Interfacer' + 'Astrological Botany', 'Biochemical Sorcery', 'Civil Divination', + 'Consecrated Augury', 'Demonic Anthropology', 'Divinatory Mineralogy', + 'Exo Interfacer', 'Genetic Banishing', 'Gunpowder Torturer', + 'Gunslinger Corruptor', 'Hermetic Geography', 'Immunological Cultist', + 'Malefic Chemist', 'Mathematical Pharmacy', 'Nuclear Biochemistry', + 'Orbital Gravedigger', 'Pharmaceutical Outlaw', 'Phased Linguist', + 'Plasma Gunslinger', 'Police Necromancer', 'Ritual Astronomy', + 'Sixgun Poisoner', 'Seismological Alchemy', 'Spiritual Illusionism', + 'Statistical Occultism', 'Spell Analyst', 'Torque Interfacer' +].map((f)=>_.padEnd(f, 21)); // Pad to equal length of 21 chars long + +const classnames = [ + 'Ackerman', 'Berserker-Typist', 'Concierge', 'Fishmonger', + 'Haberdasher', 'Manicurist', 'Netrunner', 'Weirkeeper' ]; -const classnames = ['Ackerman', 'Berserker-Typist', 'Concierge', 'Fishmonger', - 'Haberdasher', 'Manicurist', 'Netrunner', 'Weirkeeper']; - -const levels = ['1st', '2nd', '3rd', '4th', '5th', - '6th', '7th', '8th', '9th', '10th', - '11th', '12th', '13th', '14th', '15th', - '16th', '17th', '18th', '19th', '20th']; - -const profBonus = [2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6]; - -const maxes = [4, 3, 3, 3, 3, 2, 2, 1, 1]; - -const drawSlots = function(Slots, rows, padding){ - let slots = Number(Slots); - return _.times(rows, function(i){ - const max = maxes[i]; - if(slots < 1) return _.pad('—', padding); - const res = _.min([max, slots]); - slots -= res; - return _.pad(res.toString(), padding); - }).join(' | '); -}; - module.exports = { - full : function(classes){ - const classname = _.sample(classnames); - - - let cantrips = 3; - let spells = 1; - let slots = 2; - return `{{${classes}\n##### The ${classname}\n` + - `| Level | Proficiency | Features | Cantrips | Spells | --- Spell Slots Per Spell Level ---|||||||||\n`+ - `| ^| Bonus ^| ^| Known ^| Known ^|1st |2nd |3rd |4th |5th |6th |7th |8th |9th |\n`+ - `|:-----:|:-----------:|:-------------|:--------:|:------:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|\n${ - _.map(levels, function(levelName, level){ - const res = [ - _.pad(levelName, 5), - _.pad(`+${profBonus[level]}`, 2), - _.padEnd(_.sample(features), 21), - _.pad(cantrips.toString(), 8), - _.pad(spells.toString(), 6), - drawSlots(slots, 9, 2), - ].join(' | '); - - cantrips += _.random(0, 1); - spells += _.random(0, 1); - slots += _.random(0, 2); - - return `| ${res} |`; - }).join('\n')}\n}}\n\n`; + non : function(snippetClasses){ + return dedent` + {{${snippetClasses} + ##### The ${_.sample(classnames)} + | Level | Proficiency Bonus | Features | ${_.sample(features)} | + |:-----:|:-----------------:|:---------|:---------------------:| + | 1st | +2 | ${_.sample(features)} | 2 | + | 2nd | +2 | ${_.sample(features)} | 2 | + | 3rd | +2 | ${_.sample(features)} | 3 | + | 4th | +2 | ${_.sample(features)} | 3 | + | 5th | +3 | ${_.sample(features)} | 3 | + | 6th | +3 | ${_.sample(features)} | 4 | + | 7th | +3 | ${_.sample(features)} | 4 | + | 8th | +3 | ${_.sample(features)} | 4 | + | 9th | +4 | ${_.sample(features)} | 4 | + | 10th | +4 | ${_.sample(features)} | 4 | + | 11th | +4 | ${_.sample(features)} | 4 | + | 12th | +4 | ${_.sample(features)} | 5 | + | 13th | +5 | ${_.sample(features)} | 5 | + | 14th | +5 | ${_.sample(features)} | 5 | + | 15th | +5 | ${_.sample(features)} | 5 | + | 16th | +5 | ${_.sample(features)} | 5 | + | 17th | +6 | ${_.sample(features)} | 6 | + | 18th | +6 | ${_.sample(features)} | 6 | + | 19th | +6 | ${_.sample(features)} | 6 | + | 20th | +6 | ${_.sample(features)} | unlimited | + }}\n\n`; }, - half : function(classes){ - const classname = _.sample(classnames); - - let featureScore = 1; - return `{{${classes}\n##### The ${classname}\n` + - `| Level | Proficiency Bonus | Features | ${_.pad(_.sample(features), 21)} |\n` + - `|:-----:|:-----------------:|:---------|:---------------------:|\n${ - _.map(levels, function(levelName, level){ - const res = [ - _.pad(levelName, 5), - _.pad(`+${profBonus[level]}`, 2), - _.padEnd(_.sample(features), 23), - _.pad(`+${featureScore}`, 21), - ].join(' | '); - - featureScore += _.random(0, 1); - - return `| ${res} |`; - }).join('\n')}\n}}\n\n`; + full : function(snippetClasses){ + return dedent` + {{${snippetClasses} + ##### The ${_.sample(classnames)} + | Level | Proficiency | Features | Cantrips | --- Spell Slots Per Spell Level ---||||||||| + | ^| Bonus ^| ^| Known ^|1st |2nd |3rd |4th |5th |6th |7th |8th |9th | + |:-----:|:-----------:|:-------------|:--------:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:| + | 1st | +2 | ${_.sample(features)} | 2 | 2 | — | — | — | — | — | — | — | — | + | 2nd | +2 | ${_.sample(features)} | 2 | 3 | — | — | — | — | — | — | — | — | + | 3rd | +2 | ${_.sample(features)} | 2 | 4 | 2 | — | — | — | — | — | — | — | + | 4th | +2 | ${_.sample(features)} | 3 | 4 | 3 | — | — | — | — | — | — | — | + | 5th | +3 | ${_.sample(features)} | 3 | 4 | 3 | 2 | — | — | — | — | — | — | + | 6th | +3 | ${_.sample(features)} | 3 | 4 | 3 | 3 | — | — | — | — | — | — | + | 7th | +3 | ${_.sample(features)} | 3 | 4 | 3 | 3 | 1 | — | — | — | — | — | + | 8th | +3 | ${_.sample(features)} | 3 | 4 | 3 | 3 | 2 | — | — | — | — | — | + | 9th | +4 | ${_.sample(features)} | 3 | 4 | 3 | 3 | 2 | 1 | — | — | — | — | + | 10th | +4 | ${_.sample(features)} | 3 | 4 | 3 | 3 | 2 | 1 | — | — | — | — | + | 11th | +4 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | — | — | — | + | 12th | +4 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | — | — | — | + | 13th | +5 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | 1 | — | — | + | 14th | +5 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | 1 | — | — | + | 15th | +5 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | 1 | 1 | — | + | 16th | +5 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | 1 | 1 | — | + | 17th | +6 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 2 | 1 | 1 | 1 | 1 | 1 | + | 18th | +6 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 3 | 1 | 1 | 1 | 1 | 1 | + | 19th | +6 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 3 | 2 | 2 | 1 | 1 | 1 | + | 20th | +6 | ${_.sample(features)} | 4 | 4 | 3 | 3 | 3 | 2 | 2 | 2 | 1 | 1 | + }}\n\n`; }, - third : function(classes){ - const classname = _.sample(classnames); + half : function(snippetClasses){ + return dedent` + {{${snippetClasses} + ##### The ${_.sample(classnames)} + | Level | Proficiency | Features | Spells |--- Spell Slots Per Spell Level ---||||| + | ^| Bonus ^| ^| Known ^| 1st | 2nd | 3rd | 4th | 5th | + |:-----:|:-----------:|:-------------|:------:|:-----:|:-----:|:-----:|:-----:|:-----:| + | 1st | +2 | ${_.sample(features)} | — | — | — | — | — | — | + | 2nd | +2 | ${_.sample(features)} | 2 | 2 | — | — | — | — | + | 3rd | +2 | ${_.sample(features)} | 3 | 3 | — | — | — | — | + | 4th | +2 | ${_.sample(features)} | 3 | 3 | — | — | — | — | + | 5th | +3 | ${_.sample(features)} | 4 | 4 | 2 | — | — | — | + | 6th | +3 | ${_.sample(features)} | 4 | 4 | 2 | — | — | — | + | 7th | +3 | ${_.sample(features)} | 5 | 4 | 3 | — | — | — | + | 8th | +3 | ${_.sample(features)} | 5 | 4 | 3 | — | — | — | + | 9th | +4 | ${_.sample(features)} | 6 | 4 | 3 | 2 | — | — | + | 10th | +4 | ${_.sample(features)} | 6 | 4 | 3 | 2 | — | — | + | 11th | +4 | ${_.sample(features)} | 7 | 4 | 3 | 3 | — | — | + | 12th | +4 | ${_.sample(features)} | 7 | 4 | 3 | 3 | — | — | + | 13th | +5 | ${_.sample(features)} | 8 | 4 | 3 | 3 | 1 | — | + | 14th | +5 | ${_.sample(features)} | 8 | 4 | 3 | 3 | 1 | — | + | 15th | +5 | ${_.sample(features)} | 9 | 4 | 3 | 3 | 2 | — | + | 16th | +5 | ${_.sample(features)} | 9 | 4 | 3 | 3 | 2 | — | + | 17th | +6 | ${_.sample(features)} | 10 | 4 | 3 | 3 | 3 | 1 | + | 18th | +6 | ${_.sample(features)} | 10 | 4 | 3 | 3 | 3 | 1 | + | 19th | +6 | ${_.sample(features)} | 11 | 4 | 3 | 3 | 3 | 2 | + | 20th | +6 | ${_.sample(features)} | 11 | 4 | 3 | 3 | 3 | 2 | + }}\n\n`; + }, - let cantrips = 3; - let spells = 1; - let slots = 2; - return `{{${classes}\n##### ${classname} Spellcasting\n` + - `| Class | Cantrips | Spells |--- Spells Slots per Spell Level ---||||\n` + - `| Level ^| Known ^| Known ^| 1st | 2nd | 3rd | 4th |\n` + - `|:------:|:--------:|:-------:|:-------:|:-------:|:-------:|:-------:|\n${ - _.map(levels, function(levelName, level){ - const res = [ - _.pad(levelName, 6), - _.pad(cantrips.toString(), 8), - _.pad(spells.toString(), 7), - drawSlots(slots, 4, 7), - ].join(' | '); - - cantrips += _.random(0, 1); - spells += _.random(0, 1); - slots += _.random(0, 1); - - return `| ${res} |`; - }).join('\n')}\n}}\n\n`; + third : function(snippetClasses){ + return dedent` + {{${snippetClasses} + ##### ${_.sample(classnames)} Spellcasting + | Level | Cantrips | Spells |--- Spells Slots per Spell Level ---|||| + | ^| Known ^| Known ^| 1st | 2nd | 3rd | 4th | + |:-----:|:--------:|:------:|:-------:|:-------:|:-------:|:-------:| + | 3rd | 2 | 3 | 2 | — | — | — | + | 4th | 2 | 4 | 3 | — | — | — | + | 5th | 2 | 4 | 3 | — | — | — | + | 6th | 2 | 4 | 3 | — | — | — | + | 7th | 2 | 5 | 4 | 2 | — | — | + | 8th | 2 | 6 | 4 | 2 | — | — | + | 9th | 2 | 6 | 4 | 2 | — | — | + | 10th | 3 | 7 | 4 | 3 | — | — | + | 11th | 3 | 8 | 4 | 3 | — | — | + | 12th | 3 | 8 | 4 | 3 | — | — | + | 13th | 3 | 9 | 4 | 3 | 2 | — | + | 14th | 3 | 10 | 4 | 3 | 2 | — | + | 15th | 3 | 10 | 4 | 3 | 2 | — | + | 16th | 3 | 11 | 4 | 3 | 3 | — | + | 17th | 3 | 11 | 4 | 3 | 3 | — | + | 18th | 3 | 11 | 4 | 3 | 3 | — | + | 19th | 3 | 12 | 4 | 3 | 3 | 1 | + | 20th | 3 | 13 | 4 | 3 | 3 | 1 | + }}\n\n`; } }; diff --git a/themes/V3/5ePHB/snippets/tableOfContents.gen.js b/themes/V3/5ePHB/snippets/tableOfContents.gen.js index 1c52d5cf7..97d82ed40 100644 --- a/themes/V3/5ePHB/snippets/tableOfContents.gen.js +++ b/themes/V3/5ePHB/snippets/tableOfContents.gen.js @@ -29,27 +29,29 @@ const getTOC = (pages)=>{ const 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); - } - }); + if(!page.includes("{{frontCover}}") && !page.includes("{{insideCover}}") && !page.includes("{{partCover}}") && !page.includes("{{backCover}}")) { + 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 = function(brew){ - const pages = brew.text.split('\\page'); +module.exports = function(props){ + const pages = props.brew.text.split('\\page'); const TOC = getTOC(pages); const markdown = _.reduce(TOC, (r, g1, idx1)=>{ if(g1.title !== null) { diff --git a/themes/V3/5ePHB/style.less b/themes/V3/5ePHB/style.less index c6e87c40a..90320f9be 100644 --- a/themes/V3/5ePHB/style.less +++ b/themes/V3/5ePHB/style.less @@ -42,7 +42,7 @@ body { margin-top : 0.1cm; } } -.useColumns(@multiplier : 1, @fillMode: balance){ +.useColumns(@multiplier : 1, @fillMode: auto){ column-count : 2; column-fill : @fillMode; column-gap : 0.9cm; diff --git a/themes/V3/Blank/snippets.js b/themes/V3/Blank/snippets.js index 9d64496c3..72372c297 100644 --- a/themes/V3/Blank/snippets.js +++ b/themes/V3/Blank/snippets.js @@ -2,6 +2,7 @@ const WatercolorGen = require('./snippets/watercolor.gen.js'); const ImageMaskGen = require('./snippets/imageMask.gen.js'); +const FooterGen = require('./snippets/footer.gen.js'); const dedent = require('dedent-tabs').default; module.exports = [ @@ -21,6 +22,53 @@ module.exports = [ icon : 'fas fa-file-alt', gen : '\n\\page\n' }, + { + name : 'Page Number', + icon : 'fas fa-bookmark', + gen : '{{pageNumber 1}}\n' + }, + { + name : 'Auto-incrementing Page Number', + icon : 'fas fa-sort-numeric-down', + gen : '{{pageNumber,auto}}\n' + }, + { + name : 'Footer', + icon : 'fas fa-shoe-prints', + gen : FooterGen.createFooterFunc(), + subsnippets : [ + { + name : 'Footer from H1', + icon : 'fas fa-dice-one', + gen : FooterGen.createFooterFunc(1) + }, + { + name : 'Footer from H2', + icon : 'fas fa-dice-two', + gen : FooterGen.createFooterFunc(2) + }, + { + name : 'Footer from H3', + icon : 'fas fa-dice-three', + gen : FooterGen.createFooterFunc(3) + }, + { + name : 'Footer from H4', + icon : 'fas fa-dice-four', + gen : FooterGen.createFooterFunc(4) + }, + { + name : 'Footer from H5', + icon : 'fas fa-dice-five', + gen : FooterGen.createFooterFunc(5) + }, + { + name : 'Footer from H6', + icon : 'fas fa-dice-six', + gen : FooterGen.createFooterFunc(6) + } + ] + }, { name : 'Vertical Spacing', icon : 'fas fa-arrows-alt-v', diff --git a/themes/V3/Blank/snippets/footer.gen.js b/themes/V3/Blank/snippets/footer.gen.js new file mode 100644 index 000000000..6583cd06e --- /dev/null +++ b/themes/V3/Blank/snippets/footer.gen.js @@ -0,0 +1,17 @@ +const Markdown = require('../../../../shared/naturalcrit/markdown.js'); + +module.exports = { + createFooterFunc : function(headerSize=1){ + return (props)=>{ + const cursorPos = props.cursorPos; + + const markdownText = props.brew.text.split('\n').slice(0, cursorPos.line).join('\n'); + const markdownTokens = Markdown.marked.lexer(markdownText); + const headerToken = markdownTokens.findLast((lexerToken)=>{ return lexerToken.type === 'heading' && lexerToken.depth === headerSize; }); + const headerText = headerToken?.tokens.map((token)=>{ return token.text; }).join(''); + const outputText = headerText || 'PART 1 | SECTION NAME'; + + return `\n{{footnote ${outputText}}}\n`; + }; + } +}; \ No newline at end of file diff --git a/themes/V3/Blank/style.less b/themes/V3/Blank/style.less index 38aa42f20..f233a2347 100644 --- a/themes/V3/Blank/style.less +++ b/themes/V3/Blank/style.less @@ -33,7 +33,7 @@ body { } } -.useColumns(@multiplier : 1, @fillMode: balance){ +.useColumns(@multiplier : 1, @fillMode: auto){ column-fill : @fillMode; column-count : 2; } @@ -42,6 +42,7 @@ body { column-span : all; columns : inherit; column-gap : inherit; + column-fill : inherit; } .page{ .useColumns(); diff --git a/themes/codeMirror/customEditorStyles.less b/themes/codeMirror/customEditorStyles.less new file mode 100644 index 000000000..3318e1305 --- /dev/null +++ b/themes/codeMirror/customEditorStyles.less @@ -0,0 +1,88 @@ +.editor .codeEditor .CodeMirror { + // Themes with dark backgrounds + &.cm-s-3024-night, + &.cm-s-abbott, + &.cm-s-abcdef, + &.cm-s-ambiance, + &.cm-s-ayu-dark, + &.cm-s-ayu-mirage, + &.cm-s-base16-dark, + &.cm-s-bespin, + &.cm-s-blackboard, + &.cm-s-cobalt, + &.cm-s-colorforth, + &.cm-s-darcula, + &.cm-s-dracula, + &.cm-s-duotone-dark, + &.cm-s-erlang-dark, + &.cm-s-gruvbox-dark, + &.cm-s-hopscotch, + &.cm-s-icecoder, + &.cm-s-isotope, + &.cm-s-lesser-dark, + &.cm-s-liquibyte, + &.cm-s-lucario, + &.cm-s-material, + &.cm-s-material-darker, + &.cm-s-material-ocean, + &.cm-s-material-palenight, + &.cm-s-mbo, + &.cm-s-midnight, + &.cm-s-monokai, + &.cm-s-moxer, + &.cm-s-night, + &.cm-s-nord, + &.cm-s-oceanic-next, + &.cm-s-panda-syntax, + &.cm-s-paraiso-dark, + &.cm-s-pastel-on-dark, + &.cm-s-railscasts, + &.cm-s-rubyblue, + &.cm-s-seti, + &.cm-s-shadowfox, + &.cm-s-the-matrix, + &.cm-s-tomorrow-night-bright, + &.cm-s-tomorrow-night-eighties, + &.cm-s-twilight, + &.cm-s-vibrant-ink, + &.cm-s-xq-dark, + &.cm-s-yonce, + &.cm-s-zenburn + { + .CodeMirror-code { + .block:not(.cm-comment) { + color: magenta; + } + .columnSplit { + color: black; + background-color: rgba(35,153,153,0.5); + } + .pageLine { + background-color: rgba(255,255,255,0.75); + & ~ pre.CodeMirror-line { + color: black; + } + } + } + } + // Themes with light backgrounds + &.cm-s-default, + &.cm-s-3024-day, + &.cm-s-ambiance-mobile, + &.cm-s-base16-light, + &.cm-s-duotone-light, + &.cm-s-eclipse, + &.cm-s-elegant, + &.cm-s-juejin, + &.cm-s-neat, + &.cm-s-neo, + &.cm-s-paraiso-lightm + &.cm-s-solarized, + &.cm-s-ssms, + &.cm-s-ttcn, + &.cm-s-xq-light, + &.cm-s-yeti { + // Future styling for themes with light backgrounds + --dummyVar: 'currently unused'; + } +} \ No newline at end of file diff --git a/themes/codeMirror/editorThemes.json b/themes/codeMirror/editorThemes.json new file mode 100644 index 000000000..a4dd74470 --- /dev/null +++ b/themes/codeMirror/editorThemes.json @@ -0,0 +1,68 @@ +[ +"default", +"3024-day", +"3024-night", +"abbott", +"abcdef", +"ambiance-mobile", +"ambiance", +"ayu-dark", +"ayu-mirage", +"base16-dark", +"base16-light", +"bespin", +"blackboard", +"cobalt", +"colorforth", +"darcula", +"dracula", +"duotone-dark", +"duotone-light", +"eclipse", +"elegant", +"erlang-dark", +"gruvbox-dark", +"hopscotch", +"icecoder", +"idea", +"isotope", +"juejin", +"lesser-dark", +"liquibyte", +"lucario", +"material-darker", +"material-ocean", +"material-palenight", +"material", +"mbo", +"mdn-like", +"midnight", +"monokai", +"moxer", +"neat", +"neo", +"night", +"nord", +"oceanic-next", +"panda-syntax", +"paraiso-dark", +"paraiso-light", +"pastel-on-dark", +"railscasts", +"rubyblue", +"seti", +"shadowfox", +"solarized", +"ssms", +"the-matrix", +"tomorrow-night-bright", +"tomorrow-night-eighties", +"ttcn", +"twilight", +"vibrant-ink", +"xq-dark", +"xq-light", +"yeti", +"yonce", +"zenburn" +]