diff --git a/.circleci/config.yml b/.circleci/config.yml
index 666a9564a..8a756b3de 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -64,6 +64,12 @@ jobs:
- run:
name: Test - Mustache Spans
command: npm run test:mustache-syntax
+ - run:
+ name: Test - Definition Lists
+ command: npm run test:definition-lists
+ - run:
+ name: Test - Variables
+ command: npm run test:variables
- run:
name: Test - Routes
command: npm run test:route
diff --git a/changelog.md b/changelog.md
index e86c2ea0f..18c3205f7 100644
--- a/changelog.md
+++ b/changelog.md
@@ -84,7 +84,70 @@ pre {
## changelog
For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery).
-### Wednesday 21/2/2024 - v3.11.0
+### Monday 18/3/2024 - v3.12.0
+{{taskList
+
+##### 5e-Cleric
+
+* [x] Fix language-specific hyphenation on print page
+
+Fixes issue [#3294](https://github.com/naturalcrit/homebrewery/issues/3294)
+
+* [x] Upgrade Font-Awesome to v6.51
+
+* [x] Allow downloaded files to be uploaded via {{openSans **NEW {{fa,fa-plus-square}} → FROM UPLOAD {{fa,fa-upload}}**}}
+
+##### G-Ambatte
+
+* [x] Fix an edge case crash with empty documents
+
+Fixes issue [#3315](https://github.com/naturalcrit/homebrewery/issues/3315)
+
+* [x] Brews on the user page can be searched by tag; clicking a tag adds it to the filter
+
+Fixes issue [#3164](https://github.com/naturalcrit/homebrewery/issues/3164)
+
+* [x] Add *DiceFont* icons {{df,d20-20}} `{{df,icon-name}}`
+
+##### abquintic
+
+* [x] Fix ^super^ and ^^sub^^ highlighting in the text editor
+
+* [x] Add new syntax for multiline Definition Lists:
+
+
+```
+Term
+::Definition 1
+::Definition 2
+with more text
+```
+
+produces:
+
+Term
+::Definition 1
+::Definition 2
+with more text
+
+Fixes issue [#2340](https://github.com/naturalcrit/homebrewery/issues/2340)
+
+##### RKuerten :
+* [x] Fix monster stat block backgrounds on print page
+
+Fixes issue [#3275](https://github.com/naturalcrit/homebrewery/issues/3275)
+
+* [x] Added new text editor theme: "Darkvision".
+
+##### calculuschild, G-Ambatte, 5e-Cleric
+
+* [x] Codebase and UI cleanup
+}}
+
+\page
+
+
+### Friday 21/2/2024 - v3.11.0
{{taskList
##### Gazook89
@@ -166,14 +229,16 @@ Fixes issue [1488](https://github.com/naturalcrit/homebrewery/issues/1488)
Fixes issues [2510](https://github.com/naturalcrit/homebrewery/issues/2510),
[2975](https://github.com/naturalcrit/homebrewery/issues/2975)
-* [x] New Variables syntax. See below for details.
+* [x] Brew Variables
}}
+\
+
{{wide
### Brew Variable Syntax
-You may already be familiar with `[link](url)` and `` syntax. We have expanded this to include a third `$[variable](text)` syntax. All three of these syntaxes now share a common set of features:
+You may already be familiar with `[link](url)` and `` synax. We have expanded this to include a third `$[variable](text)` syntax. All three of these syntaxes now share a common set of features:
{{varSyntaxTable
| syntax | description |
@@ -1512,7 +1577,7 @@ myStyle {color: black}
### Sunday, 29/05/2016 - v2.1.0
- Finally added a syntax for doing spell lists. A bit in-depth about why this took so long. Essentially I'm running out of syntax to use in stardard Markdown. There are too many unique elements in the PHB-style to be mapped. I solved this earlier by stacking certain elements together (eg. an `
` before a `blockquote` turns it into moster state block), but those are getting unweildly. I would like to simply wrap these in `div`s with classes, but unfortunately Markdown stops processing when within HTML blocks. To get around this I wrote my own override to the Markdown parser and lexer to process Markdown within a simple div class wrapper. This should open the door for more unique syntaxes in the future. Big step!
- Override Ctrl+P (and cmd+P) to launch to the print page. Many people try to just print either the editing or share page to get a PDF. While this dones;t make much sense, I do get a ton of issues about it. So now if you try to do this, it'll just bring you imediately to the print page. Everybody wins!
-- The onboarding flow has also been confusing a few users (Homepage -> new -> save -> edit page). If you edit the Homepage text now, a Call to Action to save your work will pop-up.
+- The onboarding flow has also been confusing a few users (Homepage → new → save → edit page). If you edit the Homepage text now, a Call to Action to save your work will pop-up.
- Added a 'Recently Edited' and 'Recently Viewed' nav item to the edit and share page respectively. Each will remember the last 8 items you edited or viewed and when you viewed it. Makes use of the new title attribute of brews to easy navigatation.
- Paragraphs now indent properly after lists (thanks u/slitjen!)
diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx
index 4f3ef44f5..a9efdb245 100644
--- a/client/homebrew/editor/editor.jsx
+++ b/client/homebrew/editor/editor.jsx
@@ -151,12 +151,19 @@ const Editor = createClass({
// definition lists
if(line.includes('::')){
- const regex = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym;
+ if(/^:*$/.test(line) == true){ return };
+ const regex = /^([^\n]*?:?\s?)(::[^\n]*)(?:\n|$)/ymd; // the `d` flag, for match indices, throws an ESLint error.
let match;
while ((match = regex.exec(line)) != null){
- codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[0]) }, { line: lineNumber, ch: line.indexOf(match[0]) + match[0].length }, { className: 'define' });
- codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[1]) }, { line: lineNumber, ch: line.indexOf(match[1]) + match[1].length }, { className: 'term' });
- codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[2]) }, { line: lineNumber, ch: line.indexOf(match[2]) + match[2].length }, { className: 'definition' });
+ codeMirror.markText({ line: lineNumber, ch: match.indices[0][0] }, { line: lineNumber, ch: match.indices[0][1] }, { className: 'dl-highlight' });
+ codeMirror.markText({ line: lineNumber, ch: match.indices[1][0] }, { line: lineNumber, ch: match.indices[1][1] }, { className: 'dt-highlight' });
+ codeMirror.markText({ line: lineNumber, ch: match.indices[2][0] }, { line: lineNumber, ch: match.indices[2][1] }, { className: 'dd-highlight' });
+ const ddIndex = match.indices[2][0];
+ let colons = /::/g;
+ let colonMatches = colons.exec(match[2]);
+ if(colonMatches !== null){
+ codeMirror.markText({ line: lineNumber, ch: colonMatches.index + ddIndex }, { line: lineNumber, ch: colonMatches.index + colonMatches[0].length + ddIndex }, { className: 'dl-colon-highlight'} )
+ }
}
}
diff --git a/client/homebrew/editor/editor.less b/client/homebrew/editor/editor.less
index b165f91db..d7950ead3 100644
--- a/client/homebrew/editor/editor.less
+++ b/client/homebrew/editor/editor.less
@@ -55,6 +55,16 @@
vertical-align : sub;
font-size : 0.9em;
}
+ .dl-highlight {
+ &.dl-colon-highlight {
+ font-weight : bold;
+ color : #949494;
+ background : #E5E5E5;
+ border-radius : 3px;
+ }
+ &.dt-highlight { color : rgb(96, 117, 143); }
+ &.dd-highlight { color : rgb(97, 57, 178); }
+ }
}
.brewJump {
diff --git a/client/homebrew/pages/errorPage/errors/errorIndex.js b/client/homebrew/pages/errorPage/errors/errorIndex.js
index c2de04142..7c7a3ae7f 100644
--- a/client/homebrew/pages/errorPage/errors/errorIndex.js
+++ b/client/homebrew/pages/errorPage/errors/errorIndex.js
@@ -122,6 +122,16 @@ const errorIndex = (props)=>{
An error occurred while attempting to remove the user from the Homebrewery document author list!
**Brew ID:** ${props.brew.brewId}`,
+
+ // Brew locked by Administrators error
+ '100' : dedent`
+ ## This brew has been locked.
+
+ Please contact the Administrators to unlock this document.
+
+ **Brew ID:** ${props.brew.brewId}
+
+ **Brew Title:** ${props.brew.brewTitle}`,
};
};
diff --git a/package-lock.json b/package-lock.json
index 29ea015b6..42b83939f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "homebrewery",
- "version": "3.11.0",
+ "version": "3.12.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "homebrewery",
- "version": "3.11.0",
+ "version": "3.12.0",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -36,7 +36,7 @@
"marked-smartypants-lite": "^1.0.2",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.30.1",
- "mongoose": "^8.2.1",
+ "mongoose": "^8.2.2",
"nanoid": "3.3.4",
"nconf": "^0.12.1",
"react": "^18.2.0",
@@ -50,7 +50,7 @@
"devDependencies": {
"eslint": "^8.57.0",
"eslint-plugin-jest": "^27.9.0",
- "eslint-plugin-react": "^7.34.0",
+ "eslint-plugin-react": "^7.34.1",
"jest": "^29.7.0",
"jest-expect-message": "^1.1.3",
"postcss-less": "^6.0.0",
@@ -5755,9 +5755,9 @@
}
},
"node_modules/eslint-plugin-react": {
- "version": "7.34.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.0.tgz",
- "integrity": "sha512-MeVXdReleBTdkz/bvcQMSnCXGi+c9kvy51IpinjnJgutl3YTHWsDdke7Z1ufZpGfDG8xduBDKyjtB9JH1eBKIQ==",
+ "version": "7.34.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz",
+ "integrity": "sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==",
"dev": true,
"dependencies": {
"array-includes": "^3.1.7",
@@ -10529,9 +10529,9 @@
}
},
"node_modules/mongoose": {
- "version": "8.2.1",
- "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.2.1.tgz",
- "integrity": "sha512-UgZZbXSJH0pdU936qj3FyVI+sBsMoGowFnL5R/RYrA50ayn6+ZYdVr8ehsRgNxRcMYwoNld5XzHIfkFRJTePEw==",
+ "version": "8.2.2",
+ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.2.2.tgz",
+ "integrity": "sha512-6sMxe1d3k/dBjiOX4ExNTNOP0g1x0iq8eXyg+ttgIXM3HLnQ0IUyXRwVVAPFFY6O4/8uYN5dB0Ec72FrexbPpw==",
"dependencies": {
"bson": "^6.2.0",
"kareem": "2.5.1",
diff --git a/package.json b/package.json
index e0c65e306..79e78931b 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "homebrewery",
"description": "Create authentic looking D&D homebrews using only markdown",
- "version": "3.11.0",
+ "version": "3.12.0",
"engines": {
"npm": "^10.2.x",
"node": "^20.8.x"
@@ -31,6 +31,7 @@
"test:mustache-syntax:inline": "jest '.*(mustache-syntax).*' -t '^Inline:.*' --verbose --noStackTrace",
"test:mustache-syntax:block": "jest '.*(mustache-syntax).*' -t '^Block:.*' --verbose --noStackTrace",
"test:mustache-syntax:injection": "jest '.*(mustache-syntax).*' -t '^Injection:.*' --verbose --noStackTrace",
+ "test:definition-lists": "jest tests/markdown/definition-lists.test.js --verbose --noStackTrace",
"test:route": "jest tests/routes/static-pages.test.js --verbose",
"phb": "node scripts/phb.js",
"prod": "set NODE_ENV=production && npm run build",
@@ -106,7 +107,7 @@
"marked-smartypants-lite": "^1.0.2",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.30.1",
- "mongoose": "^8.2.1",
+ "mongoose": "^8.2.2",
"nanoid": "3.3.4",
"nconf": "^0.12.1",
"react": "^18.2.0",
@@ -120,7 +121,7 @@
"devDependencies": {
"eslint": "^8.57.0",
"eslint-plugin-jest": "^27.9.0",
- "eslint-plugin-react": "^7.34.0",
+ "eslint-plugin-react": "^7.34.1",
"jest": "^29.7.0",
"jest-expect-message": "^1.1.3",
"postcss-less": "^6.0.0",
diff --git a/server/homebrew.api.js b/server/homebrew.api.js
index 20e13ec71..567dc9cf7 100644
--- a/server/homebrew.api.js
+++ b/server/homebrew.api.js
@@ -54,6 +54,10 @@ const api = {
});
stub = stub?.toObject();
+ if(stub?.lock?.locked && accessType != 'edit') {
+ throw { HBErrorCode: '100', code: stub.lock.code, message: stub.lock.message, brewId: stub.shareId, brewTitle: stub.title };
+ }
+
// If there is a google id, try to find the google brew
if(!stubOnly && (googleId || stub?.googleId)) {
let googleError;
diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js
index 55a8c414f..8a4748e38 100644
--- a/server/homebrew.api.spec.js
+++ b/server/homebrew.api.spec.js
@@ -117,7 +117,7 @@ describe('Tests for api', ()=>{
id : '123456789012345678901234567890123abcdefghijkl'
}
});
-
+
expect(googleId).toEqual('123456789012345678901234567890123');
expect(id).toEqual('abcdefghijkl');
});
@@ -128,7 +128,7 @@ describe('Tests for api', ()=>{
id : '123456789012345678901234567890123abcdefghij'
}
});
-
+
expect(googleId).toEqual('123456789012345678901234567890123');
expect(id).toEqual('abcdefghij');
});
@@ -298,6 +298,18 @@ describe('Tests for api', ()=>{
expect(model.get).toHaveBeenCalledWith({ shareId: '1' });
expect(google.getGoogleBrew).toHaveBeenCalledWith('2', '1', 'share');
});
+
+ it('access is denied to a locked brew', async()=>{
+ const lockBrew = { title: 'test brew', shareId: '1', lock: { locked: true, code: 404, message: 'brew locked' } };
+ model.get = jest.fn(()=>toBrewPromise(lockBrew));
+ api.getId = jest.fn(()=>({ id: '1', googleId: undefined }));
+
+ const fn = api.getBrew('share', false);
+ const req = { brew: {} };
+ const next = jest.fn();
+
+ await expect(fn(req, null, next)).rejects.toEqual({ 'HBErrorCode': '100', 'brewId': '1', 'brewTitle': 'test brew', 'code': 404, 'message': 'brew locked' });
+ });
});
describe('mergeBrewText', ()=>{
diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js
index 09f810907..939c2cc81 100644
--- a/shared/naturalcrit/markdown.js
+++ b/shared/naturalcrit/markdown.js
@@ -294,10 +294,10 @@ const superSubScripts = {
}
};
-const definitionLists = {
- name : 'definitionLists',
+const definitionListsInline = {
+ name : 'definitionListsInline',
level : 'block',
- start(src) { return src.match(/^.*?::.*/m)?.index; }, // Hint to Marked.js to stop and check for a match
+ start(src) { return src.match(/^[^\n]*?::[^\n]*/m)?.index; }, // Hint to Marked.js to stop and check for a match
tokenizer(src, tokens) {
const regex = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym;
let match;
@@ -312,7 +312,7 @@ const definitionLists = {
}
if(definitions.length) {
return {
- type : 'definitionLists',
+ type : 'definitionListsInline',
raw : src.slice(0, endIndex),
definitions
};
@@ -326,6 +326,51 @@ const definitionLists = {
}
};
+const definitionListsMultiline = {
+ name : 'definitionListsMultiline',
+ level : 'block',
+ start(src) { return src.match(/^[^\n]*\n::/m)?.index; }, // Hint to Marked.js to stop and check for a match
+ tokenizer(src, tokens) {
+ const regex = /(\n?\n?(?!::)[^\n]+?(?=\n::))|\n::(.(?:.|\n)*?(?=(?:\n::)|(?:\n\n)|$))/y;
+ let match;
+ let endIndex = 0;
+ const definitions = [];
+ while (match = regex.exec(src)) {
+ if(match[1]) {
+ if(this.lexer.blockTokens(match[1].trim())[0].type !== 'paragraph') // DT must not be another block-level token besides
+ break;
+ definitions.push({
+ dt : this.lexer.inlineTokens(match[1].trim()),
+ dds : []
+ });
+ }
+ if(match[2] && definitions.length) {
+ definitions[definitions.length - 1].dds.push(
+ this.lexer.inlineTokens(match[2].trim().replace(/\s/g, ' '))
+ );
+ }
+ endIndex = regex.lastIndex;
+ }
+ if(definitions.length) {
+ return {
+ type : 'definitionListsMultiline',
+ raw : src.slice(0, endIndex),
+ definitions
+ };
+ }
+ },
+ renderer(token) {
+ let returnVal = `
`;
+ token.definitions.forEach((def)=>{
+ const dds = def.dds.map((s)=>{
+ return `\n- ${this.parser.parseInline(s).trim()}
`;
+ }).join('');
+ returnVal += `- ${this.parser.parseInline(def.dt)}
${dds}\n`;
+ });
+ returnVal = returnVal.trim();
+ return `${returnVal}
`;
+ }
+};
//v=====--------------------< Variable Handling >-------------------=====v// 242 lines
const replaceVar = function(input, hoist=false, allowUnresolved=false) {
@@ -572,7 +617,7 @@ function MarkedVariables() {
//^=====--------------------< Variable Handling >-------------------=====^//
Marked.use(MarkedVariables());
-Marked.use({ extensions: [mustacheSpans, mustacheDivs, mustacheInjectInline, definitionLists, superSubScripts] });
+Marked.use({ extensions: [mustacheSpans, mustacheDivs, mustacheInjectInline, definitionListsMultiline, definitionListsInline, superSubScripts] });
Marked.use(mustacheInjectBlock);
Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false });
Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite());
diff --git a/tests/markdown/definition-lists.test.js b/tests/markdown/definition-lists.test.js
new file mode 100644
index 000000000..87ff6f617
--- /dev/null
+++ b/tests/markdown/definition-lists.test.js
@@ -0,0 +1,85 @@
+/* eslint-disable max-lines */
+
+const Markdown = require('naturalcrit/markdown.js');
+
+describe('Inline Definition Lists', ()=>{
+ test('No Term 1 Definition', function() {
+ const source = ':: My First Definition\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('- My First Definition
\n
');
+ });
+
+ test('Single Definition Term', function() {
+ const source = 'My term :: My First Definition\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('- My term
- My First Definition
\n
');
+ });
+
+ test('Multiple Definition Terms', function() {
+ const source = 'Term 1::Definition of Term 1\nTerm 2::Definition of Term 2\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('- Term 1
- Definition of Term 1
\n- Term 2
- Definition of Term 2
\n
');
+ });
+});
+
+describe('Multiline Definition Lists', ()=>{
+ test('Single Term, Single Definition', function() {
+ const source = 'Term 1\n::Definition 1\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('- Term 1
\n- Definition 1
');
+ });
+
+ test('Single Term, Plural Definitions', function() {
+ const source = 'Term 1\n::Definition 1\n::Definition 2\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('- Term 1
\n- Definition 1
\n- Definition 2
');
+ });
+
+ test('Multiple Term, Single Definitions', function() {
+ const source = 'Term 1\n::Definition 1\n\nTerm 2\n::Definition 1\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('- Term 1
\n- Definition 1
\n- Term 2
\n- Definition 1
');
+ });
+
+ test('Multiple Term, Plural Definitions', function() {
+ const source = 'Term 1\n::Definition 1\n::Definition 2\n\nTerm 2\n::Definition 1\n::Definition 2\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('- Term 1
\n- Definition 1
\n- Definition 2
\n- Term 2
\n- Definition 1
\n- Definition 2
');
+ });
+
+ test('Single Term, Single multi-line definition', function() {
+ const source = 'Term 1\n::Definition 1\nand more and\nmore and more\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('- Term 1
\n- Definition 1 and more and more and more
');
+ });
+
+ test('Single Term, Plural multi-line definitions', function() {
+ const source = 'Term 1\n::Definition 1\nand more and more\n::Definition 2\nand more\nand more\n::Definition 3\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('- Term 1
\n- Definition 1 and more and more
\n- Definition 2 and more and more
\n- Definition 3
');
+ });
+
+ test('Multiple Term, Single multi-line definition', function() {
+ const source = 'Term 1\n::Definition 1\nand more and more\n\nTerm 2\n::Definition 1\n::Definition 2\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('- Term 1
\n- Definition 1 and more and more
\n- Term 2
\n- Definition 1
\n- Definition 2
');
+ });
+
+ test('Multiple Term, Single multi-line definition, followed by an inline dl', function() {
+ const source = 'Term 1\n::Definition 1\nand more and more\n\nTerm 2\n::Definition 1\n::Definition 2\n\n::Inline Definition (no term)';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('- Term 1
\n- Definition 1 and more and more
\n- Term 2
\n- Definition 1
\n- Definition 2
- Inline Definition (no term)
\n
');
+ });
+
+ test('Multiple Term, Single multi-line definition, followed by paragraph', function() {
+ const source = 'Term 1\n::Definition 1\nand more and more\n\nTerm 2\n::Definition 1\n::Definition 2\n\nParagraph';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('- Term 1
\n- Definition 1 and more and more
\n- Term 2
\n- Definition 1
\n- Definition 2
Paragraph
');
+ });
+
+ test('Block Token cannot be the Term of a multi-line definition', function() {
+ const source = '## Header\n::Definition 1 of a single-line DL\n::Definition 1 of another single-line DL';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('\n- Definition 1 of a single-line DL
\n- Definition 1 of another single-line DL
\n
');
+ });
+});
diff --git a/themes/V3/5ePHB/style.less b/themes/V3/5ePHB/style.less
index ed3e8604c..a529d591f 100644
--- a/themes/V3/5ePHB/style.less
+++ b/themes/V3/5ePHB/style.less
@@ -1,6 +1,6 @@
-@import (less) './themes/fonts/5e/fonts.less';
@import (less) './themes/assets/assets.less';
@import (less) './themes/fonts/icon fonts/font-icons.less';
+@import (less) './themes/fonts/icon fonts/dicefont.less';
:root {
//Colors
diff --git a/themes/V3/Blank/style.less b/themes/V3/Blank/style.less
index 3e5b2290f..1d8ca6ee4 100644
--- a/themes/V3/Blank/style.less
+++ b/themes/V3/Blank/style.less
@@ -1,5 +1,6 @@
@import (less) './themes/fonts/5e/fonts.less';
@import (less) './themes/assets/assets.less';
+@import (less) './themes/fonts/icon fonts/dicefont.less';
:root {
//Colors
diff --git a/themes/fonts/5e/dicefont.less b/themes/fonts/5e/dicefont.less
deleted file mode 100644
index 887a7c27c..000000000
--- a/themes/fonts/5e/dicefont.less
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- Icon Font: dicefont
-*/
-@font-face {
- font-family: 'DiceFont';
- src: url('../../../fonts/5e/dicefont.woff2') format('woff2'),
- url('../../../fonts/5e/dicefont.woff') format('woff');
- font-weight: normal;
- font-style: normal;
-}
-
-.df {
- display: inline-block;
- font-family: 'DiceFont';
- font-style: normal;
- font-weight: normal;
- font-variant: normal;
- line-height: 1;
- text-decoration: inherit;
- text-rendering: optimizeLegibility;
- text-transform: none;
- -moz-osx-font-smoothing: grayscale;
- -webkit-font-smoothing: antialiased;
- font-smooth: antialiased;
- &.F:before { content: '\f190'; }
- &.F-minus:before { content: '\f191'; }
- &.F-plus:before { content: '\f192'; }
- &.F-zero:before { content: '\f193'; }
- &.d10:before { content: '\f194'; }
- &.d10-0:before { content: '\f100'; }
- &.d10-1:before { content: '\f101'; }
- &.d10-10:before { content: '\f102'; }
- &.d10-2:before { content: '\f103'; }
- &.d10-3:before { content: '\f104'; }
- &.d10-4:before { content: '\f105'; }
- &.d10-5:before { content: '\f106'; }
- &.d10-6:before { content: '\f107'; }
- &.d10-7:before { content: '\f108'; }
- &.d10-8:before { content: '\f109'; }
- &.d10-9:before { content: '\f10a'; }
- &.d12:before { content: '\f195'; }
- &.d12-1:before { content: '\f10b'; }
- &.d12-10:before { content: '\f10c'; }
- &.d12-11:before { content: '\f10d'; }
- &.d12-12:before { content: '\f10e'; }
- &.d12-2:before { content: '\f10f'; }
- &.d12-3:before { content: '\f110'; }
- &.d12-4:before { content: '\f111'; }
- &.d12-5:before { content: '\f112'; }
- &.d12-6:before { content: '\f113'; }
- &.d12-7:before { content: '\f114'; }
- &.d12-8:before { content: '\f115'; }
- &.d12-9:before { content: '\f116'; }
- &.d2:before { content: '\f196'; }
- &.d2-1:before { content: '\f117'; }
- &.d2-2:before { content: '\f118'; }
- &.d20:before { content: '\f197'; }
- &.d20-1:before { content: '\f119'; }
- &.d20-10:before { content: '\f11a'; }
- &.d20-11:before { content: '\f11b'; }
- &.d20-12:before { content: '\f11c'; }
- &.d20-13:before { content: '\f11d'; }
- &.d20-14:before { content: '\f11e'; }
- &.d20-15:before { content: '\f11f'; }
- &.d20-16:before { content: '\f120'; }
- &.d20-17:before { content: '\f121'; }
- &.d20-18:before { content: '\f122'; }
- &.d20-19:before { content: '\f123'; }
- &.d20-2:before { content: '\f124'; }
- &.d20-20:before { content: '\f125'; }
- &.d20-3:before { content: '\f126'; }
- &.d20-4:before { content: '\f127'; }
- &.d20-5:before { content: '\f128'; }
- &.d20-6:before { content: '\f129'; }
- &.d20-7:before { content: '\f12a'; }
- &.d20-8:before { content: '\f12b'; }
- &.d20-9:before { content: '\f12c'; }
- &.d4:before { content: '\f198'; }
- &.d4-1:before { content: '\f12d'; }
- &.d4-2:before { content: '\f12e'; }
- &.d4-3:before { content: '\f12f'; }
- &.d4-4:before { content: '\f130'; }
- &.d6:before { content: '\f199'; }
- &.d6-1:before { content: '\f131'; }
- &.d6-2:before { content: '\f132'; }
- &.d6-3:before { content: '\f133'; }
- &.d6-4:before { content: '\f134'; }
- &.d6-5:before { content: '\f135'; }
- &.d6-6:before { content: '\f136'; }
- &.d8:before { content: '\f19a'; }
- &.d8-1:before { content: '\f137'; }
- &.d8-2:before { content: '\f138'; }
- &.d8-3:before { content: '\f139'; }
- &.d8-4:before { content: '\f13a'; }
- &.d8-5:before { content: '\f13b'; }
- &.d8-6:before { content: '\f13c'; }
- &.d8-7:before { content: '\f13d'; }
- &.d8-8:before { content: '\f13e'; }
- &.dot-d6:before { content: '\f19b'; }
- &.dot-d6-1:before { content: '\f13f'; }
- &.dot-d6-2:before { content: '\f140'; }
- &.dot-d6-3:before { content: '\f141'; }
- &.dot-d6-4:before { content: '\f142'; }
- &.dot-d6-5:before { content: '\f143'; }
- &.dot-d6-6:before { content: '\f18f'; }
- &.small-dot-d6-1:before { content: '\f183'; }
- &.small-dot-d6-2:before { content: '\f184'; }
- &.small-dot-d6-3:before { content: '\f185'; }
- &.small-dot-d6-4:before { content: '\f186'; }
- &.small-dot-d6-5:before { content: '\f187'; }
- &.small-dot-d6-6:before { content: '\f188'; }
- &.solid-small-dot-d6-1:before { content: '\f189'; }
- &.solid-small-dot-d6-2:before { content: '\f18a'; }
- &.solid-small-dot-d6-3:before { content: '\f18b'; }
- &.solid-small-dot-d6-4:before { content: '\f18c'; }
- &.solid-small-dot-d6-5:before { content: '\f18d'; }
- &.solid-small-dot-d6-6:before { content: '\f18e'; }
-}
\ No newline at end of file
diff --git a/themes/fonts/5e/dicefont.woff b/themes/fonts/5e/dicefont.woff
deleted file mode 100644
index d6f54f38e..000000000
Binary files a/themes/fonts/5e/dicefont.woff and /dev/null differ
diff --git a/themes/fonts/5e/fonts.less b/themes/fonts/5e/fonts.less
index b59fe1671..8f089b51c 100644
--- a/themes/fonts/5e/fonts.less
+++ b/themes/fonts/5e/fonts.less
@@ -1,5 +1,3 @@
-@import url('./dicefont.less');
-
/* Main Font, serif */
@font-face {
font-family: BookInsanityRemake;
diff --git a/themes/fonts/icon fonts/dicefont.less b/themes/fonts/icon fonts/dicefont.less
new file mode 100644
index 000000000..78a88f03a
--- /dev/null
+++ b/themes/fonts/icon fonts/dicefont.less
@@ -0,0 +1,114 @@
+/* Icon Font: dicefont */
+@font-face {
+ font-family : 'DiceFont';
+ font-style : normal;
+ font-weight : normal;
+ src : url('../../../fonts/icon fonts/dicefont.woff2');
+}
+
+.df {
+ display : inline-block;
+ font-family : 'DiceFont';
+ font-style : normal;
+ font-weight : normal;
+ font-variant : normal;
+ line-height : 1;
+ text-decoration : inherit;
+ text-transform : none;
+ text-rendering : optimizeLegibility;
+ -moz-osx-font-smoothing : grayscale;
+ -webkit-font-smoothing : antialiased;
+ &.F::before { content : '\f190'; }
+ &.F-minus::before { content : '\f191'; }
+ &.F-plus::before { content : '\f192'; }
+ &.F-zero::before { content : '\f193'; }
+ &.d10::before { content : '\f194'; }
+ &.d10-0::before { content : '\f100'; }
+ &.d10-1::before { content : '\f101'; }
+ &.d10-10::before { content : '\f102'; }
+ &.d10-2::before { content : '\f103'; }
+ &.d10-3::before { content : '\f104'; }
+ &.d10-4::before { content : '\f105'; }
+ &.d10-5::before { content : '\f106'; }
+ &.d10-6::before { content : '\f107'; }
+ &.d10-7::before { content : '\f108'; }
+ &.d10-8::before { content : '\f109'; }
+ &.d10-9::before { content : '\f10a'; }
+ &.d12::before { content : '\f195'; }
+ &.d12-1::before { content : '\f10b'; }
+ &.d12-10::before { content : '\f10c'; }
+ &.d12-11::before { content : '\f10d'; }
+ &.d12-12::before { content : '\f10e'; }
+ &.d12-2::before { content : '\f10f'; }
+ &.d12-3::before { content : '\f110'; }
+ &.d12-4::before { content : '\f111'; }
+ &.d12-5::before { content : '\f112'; }
+ &.d12-6::before { content : '\f113'; }
+ &.d12-7::before { content : '\f114'; }
+ &.d12-8::before { content : '\f115'; }
+ &.d12-9::before { content : '\f116'; }
+ &.d2::before { content : '\f196'; }
+ &.d2-1::before { content : '\f117'; }
+ &.d2-2::before { content : '\f118'; }
+ &.d20::before { content : '\f197'; }
+ &.d20-1::before { content : '\f119'; }
+ &.d20-10::before { content : '\f11a'; }
+ &.d20-11::before { content : '\f11b'; }
+ &.d20-12::before { content : '\f11c'; }
+ &.d20-13::before { content : '\f11d'; }
+ &.d20-14::before { content : '\f11e'; }
+ &.d20-15::before { content : '\f11f'; }
+ &.d20-16::before { content : '\f120'; }
+ &.d20-17::before { content : '\f121'; }
+ &.d20-18::before { content : '\f122'; }
+ &.d20-19::before { content : '\f123'; }
+ &.d20-2::before { content : '\f124'; }
+ &.d20-20::before { content : '\f125'; }
+ &.d20-3::before { content : '\f126'; }
+ &.d20-4::before { content : '\f127'; }
+ &.d20-5::before { content : '\f128'; }
+ &.d20-6::before { content : '\f129'; }
+ &.d20-7::before { content : '\f12a'; }
+ &.d20-8::before { content : '\f12b'; }
+ &.d20-9::before { content : '\f12c'; }
+ &.d4::before { content : '\f198'; }
+ &.d4-1::before { content : '\f12d'; }
+ &.d4-2::before { content : '\f12e'; }
+ &.d4-3::before { content : '\f12f'; }
+ &.d4-4::before { content : '\f130'; }
+ &.d6::before { content : '\f199'; }
+ &.d6-1::before { content : '\f131'; }
+ &.d6-2::before { content : '\f132'; }
+ &.d6-3::before { content : '\f133'; }
+ &.d6-4::before { content : '\f134'; }
+ &.d6-5::before { content : '\f135'; }
+ &.d6-6::before { content : '\f136'; }
+ &.d8::before { content : '\f19a'; }
+ &.d8-1::before { content : '\f137'; }
+ &.d8-2::before { content : '\f138'; }
+ &.d8-3::before { content : '\f139'; }
+ &.d8-4::before { content : '\f13a'; }
+ &.d8-5::before { content : '\f13b'; }
+ &.d8-6::before { content : '\f13c'; }
+ &.d8-7::before { content : '\f13d'; }
+ &.d8-8::before { content : '\f13e'; }
+ &.dot-d6::before { content : '\f19b'; }
+ &.dot-d6-1::before { content : '\f13f'; }
+ &.dot-d6-2::before { content : '\f140'; }
+ &.dot-d6-3::before { content : '\f141'; }
+ &.dot-d6-4::before { content : '\f142'; }
+ &.dot-d6-5::before { content : '\f143'; }
+ &.dot-d6-6::before { content : '\f18f'; }
+ &.small-dot-d6-1::before { content : '\f183'; }
+ &.small-dot-d6-2::before { content : '\f184'; }
+ &.small-dot-d6-3::before { content : '\f185'; }
+ &.small-dot-d6-4::before { content : '\f186'; }
+ &.small-dot-d6-5::before { content : '\f187'; }
+ &.small-dot-d6-6::before { content : '\f188'; }
+ &.solid-small-dot-d6-1::before { content : '\f189'; }
+ &.solid-small-dot-d6-2::before { content : '\f18a'; }
+ &.solid-small-dot-d6-3::before { content : '\f18b'; }
+ &.solid-small-dot-d6-4::before { content : '\f18c'; }
+ &.solid-small-dot-d6-5::before { content : '\f18d'; }
+ &.solid-small-dot-d6-6::before { content : '\f18e'; }
+}
\ No newline at end of file
diff --git a/themes/fonts/5e/dicefont.woff2 b/themes/fonts/icon fonts/dicefont.woff2
similarity index 100%
rename from themes/fonts/5e/dicefont.woff2
rename to themes/fonts/icon fonts/dicefont.woff2
diff --git a/themes/fonts/5e/dicefont_license.md b/themes/fonts/icon fonts/dicefont_license.md
similarity index 100%
rename from themes/fonts/5e/dicefont_license.md
rename to themes/fonts/icon fonts/dicefont_license.md
diff --git a/themes/fonts/icon fonts/font-icons.less b/themes/fonts/icon fonts/font-icons.less
index f8eb19f11..be8efa734 100644
--- a/themes/fonts/icon fonts/font-icons.less
+++ b/themes/fonts/icon fonts/font-icons.less
@@ -1,6 +1,6 @@
-/* Main Font, serif */
+/* Icon Font: Elderberry Inn */
@font-face {
- font-family : 'Eldeberry-Inn';
+ font-family : 'Elderberry-Inn';
font-style : normal;
font-weight : normal;
src : url('../../../fonts/icon fonts/Elderberry-Inn-Icons.woff2');
@@ -10,7 +10,7 @@
span.ei {
display : inline-block;
margin-right : 3px;
- font-family : 'Eldeberry-Inn';
+ font-family : 'Elderberry-Inn';
line-height : 1;
vertical-align : baseline;
-moz-osx-font-smoothing : grayscale;