diff --git a/.circleci/config.yml b/.circleci/config.yml index d02402927..fb239ceb3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -64,9 +64,6 @@ 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 - Hard Breaks command: npm run test:hard-breaks diff --git a/client/homebrew/brewRenderer/brewRenderer.less b/client/homebrew/brewRenderer/brewRenderer.less index 128c419be..b0a3e9779 100644 --- a/client/homebrew/brewRenderer/brewRenderer.less +++ b/client/homebrew/brewRenderer/brewRenderer.less @@ -68,12 +68,16 @@ @media print { .toolBar { display : none; } .brewRenderer { - height : 100%; - padding-top : unset; - overflow-y : unset; + height : 100%; + padding : unset; + overflow-y : unset; + &:has(.facing, .flow) { + padding : unset; + } .pages { - margin : 0px; - zoom : 100% !important; + margin : 0px; + zoom : 100% !important; + display : block; & > .page { box-shadow : unset; } } } diff --git a/package-lock.json b/package-lock.json index 71d2693a9..ddf8d7138 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,6 +34,8 @@ "less": "^3.13.1", "lodash": "^4.17.21", "marked": "15.0.8", + "marked-alignment-paragraphs": "^1.0.0", + "marked-definition-lists": "^1.0.1", "marked-emoji": "^2.0.0", "marked-extended-tables": "^2.0.1", "marked-gfm-heading-id": "^4.0.1", @@ -44,7 +46,7 @@ "moment": "^2.30.1", "mongoose": "^8.13.2", "nanoid": "5.1.5", - "nconf": "^0.12.1", + "nconf": "^0.13.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-frame-component": "^4.1.3", @@ -392,12 +394,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", - "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.10" + "@babel/types": "^7.27.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -1689,14 +1691,14 @@ } }, "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" @@ -1730,9 +1732,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", - "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -3903,6 +3905,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, "node_modules/bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", @@ -6450,6 +6462,13 @@ "node": ">=16.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT", + "optional": true + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -9979,6 +9998,24 @@ "node": ">= 18" } }, + "node_modules/marked-alignment-paragraphs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/marked-alignment-paragraphs/-/marked-alignment-paragraphs-1.0.0.tgz", + "integrity": "sha512-e+X+vUt9oteG4I0+29ZvTjrNEvB2/uo/Dhczu+Nq0shITA3oqYmTercT25mq+2mfJQQn7xiJGXPmDmBSFgotIA==", + "license": "MIT", + "peerDependencies": { + "marked": ">=3 <16" + } + }, + "node_modules/marked-definition-lists": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/marked-definition-lists/-/marked-definition-lists-1.0.1.tgz", + "integrity": "sha512-+fBAbGbVnGNNkCLMTnieReZ+gq14iCF0rBc1BBMGDDqnKeImxdvoV9kB8xRTVW4ZH5BPp6XLVykUZW0icsvZTw==", + "license": "MIT", + "peerDependencies": { + "marked": ">=3 <16" + } + }, "node_modules/marked-emoji": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/marked-emoji/-/marked-emoji-2.0.0.tgz", @@ -10676,9 +10713,9 @@ "license": "MIT" }, "node_modules/nconf": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.12.1.tgz", - "integrity": "sha512-p2cfF+B3XXacQdswUYWZ0w6Vld0832A/tuqjLBu3H1sfUcby4N2oVbGhyuCkZv+t3iY3aiFEj7gZGqax9Q2c1w==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/nconf/-/nconf-0.13.0.tgz", + "integrity": "sha512-hJ/u2xCpA663h6xyOiztx3y+lg9eU0rdkwJ+c4FtiHo2g/gB0sjXtW31yTdMLWLOIj1gL2FcJMwfOqouuUK/Wg==", "license": "MIT", "dependencies": { "async": "^3.0.0", diff --git a/package.json b/package.json index a98cf59ff..8b817cfd3 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "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:hard-breaks": "jest tests/markdown/hard-breaks.test.js --verbose --noStackTrace", "test:non-breaking-spaces": "jest tests/markdown/non-breaking-spaces.test.js --verbose --noStackTrace", "test:emojis": "jest tests/markdown/emojis.test.js --verbose --noStackTrace", @@ -108,9 +107,11 @@ "less": "^3.13.1", "lodash": "^4.17.21", "marked": "15.0.8", + "marked-definition-lists": "^1.0.1", "marked-emoji": "^2.0.0", "marked-extended-tables": "^2.0.1", "marked-gfm-heading-id": "^4.0.1", + "marked-alignment-paragraphs": "^1.0.0", "marked-nonbreaking-spaces": "^1.0.1", "marked-smartypants-lite": "^1.0.3", "marked-subsuper-text": "^1.0.3", @@ -118,7 +119,7 @@ "moment": "^2.30.1", "mongoose": "^8.13.2", "nanoid": "5.1.5", - "nconf": "^0.12.1", + "nconf": "^0.13.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-frame-component": "^4.1.3", diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js index 6f5fa6fc6..90a9488dc 100644 --- a/shared/naturalcrit/markdown.js +++ b/shared/naturalcrit/markdown.js @@ -4,11 +4,13 @@ import _ from 'lodash'; import { Parser as MathParser } from 'expr-eval'; import { marked as Marked } from 'marked'; import MarkedExtendedTables from 'marked-extended-tables'; +import MarkedDefinitionLists from 'marked-definition-lists'; +import MarkedAlignedParagraphs from 'marked-alignment-paragraphs'; +import MarkedNonbreakingSpaces from 'marked-nonbreaking-spaces'; +import MarkedSubSuperText from 'marked-subsuper-text'; import { markedSmartypantsLite as MarkedSmartypantsLite } from 'marked-smartypants-lite'; import { gfmHeadingId as MarkedGFMHeadingId, resetHeadings as MarkedGFMResetHeadingIDs } from 'marked-gfm-heading-id'; import { markedEmoji as MarkedEmojis } from 'marked-emoji'; -import MarkedNonbreakingSpaces from 'marked-nonbreaking-spaces'; -import MarkedSubSuperText from 'marked-subsuper-text'; import { romanize } from 'romans'; import writtenNumber from 'written-number'; @@ -388,42 +390,6 @@ const mustacheInjectBlock = { } }; -const justifiedParagraphClasses = []; -justifiedParagraphClasses[2] = 'Left'; -justifiedParagraphClasses[4] = 'Right'; -justifiedParagraphClasses[6] = 'Center'; - -const justifiedParagraphs = { - name : 'justifiedParagraphs', - level : 'block', - start(src) { - return src.match(/\n(?:-:|:-|-:) {1}/m)?.index; - }, // Hint to Marked.js to stop and check for a match - tokenizer(src, tokens) { - const regex = /^(((:-))|((-:))|((:-:))) .+(\n(([^\n].*\n)*(\n|$))|$)/ygm; - const match = regex.exec(src); - if(match?.length) { - let whichJustify; - if(match[2]?.length) whichJustify = 2; - if(match[4]?.length) whichJustify = 4; - if(match[6]?.length) whichJustify = 6; - return { - type : 'justifiedParagraphs', // Should match "name" above - raw : match[0], // Text to consume from the source - length : match[whichJustify].length, - text : match[0].slice(match[whichJustify].length), - class : justifiedParagraphClasses[whichJustify], - tokens : this.lexer.inlineTokens(match[0].slice(match[whichJustify].length + 1)) - }; - } - }, - renderer(token) { - return `
${this.parser.parseInline(token.tokens)}
`; - } - -}; - - const forcedParagraphBreaks = { name : 'hardBreaks', level : 'block', @@ -445,93 +411,6 @@ const forcedParagraphBreaks = { } }; -const definitionListsSingleLine = { - name : 'definitionListsSingleLine', - level : 'block', - start(src) { return src.match(/\n[^\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; - let endIndex = 0; - const definitions = []; - while (match = regex.exec(src)) { - const originalLine = match[0]; // This line and below to handle conflict with emojis - let firstLine = originalLine; // Remove in V4 when definitionListsInline updated to - this.lexer.inlineTokens(firstLine.trim()) // require spaces around `::` - .filter((t)=>t.type == 'emoji') - .map((emoji)=>firstLine = firstLine.replace(emoji.raw, 'x'.repeat(emoji.raw.length))); - - const newMatch = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym.exec(firstLine); - if(newMatch) { - definitions.push({ - dt : this.lexer.inlineTokens(originalLine.slice(0, newMatch[1].length).trim()), - dd : this.lexer.inlineTokens(originalLine.slice(newMatch[1].length + 2).trim()) - }); - } // End of emoji hack. - endIndex = regex.lastIndex; - } - if(definitions.length) { - return { - type : 'definitionListsSingleLine', - raw : src.slice(0, endIndex), - definitions - }; - } - }, - renderer(token) { - return `- 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 = `