@miliastry/quasar 1.0.6 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Visuals/lyne.css +117 -6
- package/dist/Visuals/osu.css +36 -5
- package/dist/index.js +9664 -3282
- package/dist/index.mjs +9535 -3283
- package/dist/types/Analysis/Contracts/AnalysisReport.d.ts +22 -0
- package/dist/types/Analysis/Contracts/Contribution.d.ts +68 -0
- package/dist/types/Analysis/Contracts/Pass.d.ts +59 -0
- package/dist/types/Analysis/Contracts/PipelineContext.d.ts +34 -0
- package/dist/types/Analysis/Passes/Analysis/ColorUsageAnalyzer.d.ts +56 -0
- package/dist/types/Analysis/Passes/Analysis/GradientAnalyzer.d.ts +111 -0
- package/dist/types/Analysis/Passes/Analysis/MergeableColorAnalyzer.d.ts +26 -0
- package/dist/types/Analysis/Passes/Analysis/RainbowAnalyzer.d.ts +47 -0
- package/dist/types/Analysis/Passes/Analysis/SymbolAnalyzer.d.ts +89 -0
- package/dist/types/Analysis/Passes/Analysis/WaveAnalyzer.d.ts +44 -0
- package/dist/types/Analysis/Passes/Analysis/__tests__/GradientAnalyzer.test.d.ts +4 -0
- package/dist/types/Analysis/Passes/Analysis/__tests__/MergeableColorAnalyzer.test.d.ts +4 -0
- package/dist/types/Analysis/Passes/Analysis/__tests__/RainbowAnalyzer.test.d.ts +4 -0
- package/dist/types/Analysis/Passes/Analysis/__tests__/WaveAnalyzer.test.d.ts +4 -0
- package/dist/types/Analysis/Passes/Decision/DefaultDecision.d.ts +30 -0
- package/dist/types/Analysis/Passes/Decision/PaletteRemapDecision.d.ts +169 -0
- package/dist/types/Analysis/Passes/Decision/__tests__/DefaultDecision.test.d.ts +4 -0
- package/dist/types/Analysis/Passes/Transform/CollapseGradientTransform.d.ts +37 -0
- package/dist/types/Analysis/Passes/Transform/MergeColorsTransform.d.ts +28 -0
- package/dist/types/Analysis/Passes/Transform/RainbowCollapseTransform.d.ts +19 -0
- package/dist/types/Analysis/Passes/Transform/WaveCollapseTransform.d.ts +20 -0
- package/dist/types/Analysis/Passes/Utility/CharacterCountAnalyzer.d.ts +19 -0
- package/dist/types/Analysis/Pipeline/Pipeline.d.ts +69 -0
- package/dist/types/Analysis/Pipeline/PipelineBuilder.d.ts +31 -0
- package/dist/types/Analysis/Pipeline/PipelineStage.d.ts +17 -0
- package/dist/types/Analysis/Utils/color-utils.d.ts +60 -0
- package/dist/types/Analysis/__tests__/Integration.test.d.ts +8 -0
- package/dist/types/Analysis/__tests__/PaletteRemap.test.d.ts +14 -0
- package/dist/types/Analysis/__tests__/Pipeline.test.d.ts +10 -0
- package/dist/types/Analysis/__tests__/RoundTrip.test.d.ts +30 -0
- package/dist/types/Analysis/__tests__/SymbolAnalyzer.test.d.ts +11 -0
- package/dist/types/Analysis/index.d.ts +40 -0
- package/dist/types/BBCode/BBCodeDocumentModel.d.ts +92 -0
- package/dist/types/BBCode/BBCodeToGreenNode.d.ts +111 -0
- package/dist/types/BBCode/Parser.d.ts +78 -0
- package/dist/types/BBCode/index.d.ts +4 -0
- package/dist/types/Collab/positions.d.ts +55 -0
- package/dist/types/Commands/Command.d.ts +37 -0
- package/dist/types/Commands/CommandRegistry.d.ts +48 -0
- package/dist/types/Commands/DeleteNode.d.ts +7 -0
- package/dist/types/Commands/InsertText.d.ts +8 -0
- package/dist/types/Commands/SplitMerge.d.ts +9 -0
- package/dist/types/Commands/WrapInTag.d.ts +8 -0
- package/dist/types/Commands/index.d.ts +6 -0
- package/dist/types/Diff/TreeDiffer.d.ts +52 -0
- package/dist/types/Diff/__tests__/TreeDiffer.test.d.ts +1 -0
- package/dist/types/Diff/index.d.ts +2 -0
- package/dist/types/Edits/EditPlan.d.ts +144 -0
- package/dist/types/Edits/Optimizer.d.ts +78 -0
- package/dist/types/Edits/Rules/Rule.d.ts +91 -0
- package/dist/types/Edits/Rules/dropEmptyTags.d.ts +40 -0
- package/dist/types/Edits/Rules/dropRedundantNesting.d.ts +59 -0
- package/dist/types/Edits/Rules/index.d.ts +9 -0
- package/dist/types/Edits/Rules/mergeAdjacent.d.ts +93 -0
- package/dist/types/Edits/Rules/reorderWrappers.d.ts +42 -0
- package/dist/types/Edits/Rules/shortenHex.d.ts +30 -0
- package/dist/types/Edits/Rules/tagValue.d.ts +56 -0
- package/dist/types/Edits/Rules/unwrapInvisibleColor.d.ts +47 -0
- package/dist/types/Edits/applyEdits.d.ts +36 -0
- package/dist/types/Edits/index.d.ts +6 -0
- package/dist/types/Events/EventBus.d.ts +103 -0
- package/dist/types/Events/index.d.ts +2 -0
- package/dist/types/Formatter/Formatter.d.ts +32 -0
- package/dist/types/Formatter/index.d.ts +2 -0
- package/dist/types/HTML/HTMLDocumentModel.d.ts +13 -0
- package/dist/types/HTML/HTMLToGreenNode.d.ts +4 -0
- package/dist/types/Incremental/BracketIndex.d.ts +101 -0
- package/dist/types/Incremental/ChangeTracker.d.ts +69 -0
- package/dist/types/Incremental/IncrementalParser.d.ts +246 -0
- package/dist/types/Incremental/__tests__/BracketIndex.test.d.ts +1 -0
- package/dist/types/Incremental/__tests__/IncrementalParser.test.d.ts +1 -0
- package/dist/types/Incremental/index.d.ts +4 -0
- package/dist/types/Lexer/BBCodeLexer.d.ts +74 -0
- package/dist/types/Lexer/Lexer.d.ts +54 -0
- package/dist/types/Lexer/index.d.ts +4 -0
- package/dist/types/Linter/Linter.d.ts +58 -0
- package/dist/types/Linter/index.d.ts +2 -0
- package/dist/types/Markdown/MarkdownAST.d.ts +122 -0
- package/dist/types/Markdown/MarkdownDocumentModel.d.ts +15 -0
- package/dist/types/Markdown/MarkdownLexer.d.ts +155 -0
- package/dist/types/Markdown/MarkdownParser.d.ts +42 -0
- package/dist/types/Markdown/MarkdownToGreenNode.d.ts +6 -0
- package/dist/types/MilHibri/MilHibriDocumentModel.d.ts +21 -0
- package/dist/types/Model/DocumentModel.d.ts +318 -0
- package/dist/types/Model/NodeFactory.d.ts +29 -0
- package/dist/types/Model/TagRegistry.d.ts +165 -0
- package/dist/types/Model/index.d.ts +5 -0
- package/dist/types/Plugins/PluginAPI.d.ts +44 -0
- package/dist/types/Plugins/PluginRegistry.d.ts +81 -0
- package/dist/types/Plugins/index.d.ts +3 -0
- package/dist/types/Queries/QueryEngine.d.ts +32 -0
- package/dist/types/Queries/index.d.ts +1 -0
- package/dist/types/Reconciler/SurgicalReconciler.d.ts +23 -0
- package/dist/types/RenderPipeline/RenderPipeline.d.ts +46 -0
- package/dist/types/RenderPipeline/RenderTree.d.ts +55 -0
- package/dist/types/RenderPipeline/index.d.ts +4 -0
- package/dist/types/Repair/NestingRepair.d.ts +48 -0
- package/dist/types/Repair/index.d.ts +1 -0
- package/dist/types/Semantic/SemanticAnalyzer.d.ts +321 -0
- package/dist/types/Semantic/index.d.ts +2 -0
- package/dist/types/Symbols/SymbolTable.d.ts +47 -0
- package/dist/types/Symbols/index.d.ts +1 -0
- package/dist/types/Syntax/GreenNode.d.ts +154 -0
- package/dist/types/Syntax/GreenNodePool.d.ts +134 -0
- package/dist/types/Syntax/NodeMatcher.d.ts +75 -0
- package/dist/types/Syntax/RedNode.d.ts +280 -0
- package/dist/types/Syntax/RedNodeStore.d.ts +116 -0
- package/dist/types/Syntax/TreeBuilder.d.ts +54 -0
- package/dist/types/Syntax/__tests__/GreenNode.test.d.ts +1 -0
- package/dist/types/Syntax/__tests__/RedNode.test.d.ts +1 -0
- package/dist/types/Syntax/__tests__/RedNodeStore.test.d.ts +1 -0
- package/dist/types/Syntax/greenEdit.d.ts +64 -0
- package/dist/types/Syntax/hash.d.ts +17 -0
- package/dist/types/Syntax/index.d.ts +8 -0
- package/dist/types/Syntax/nodeAttr.d.ts +63 -0
- package/dist/types/Syntax/partition.d.ts +67 -0
- package/dist/types/Syntax/preserveNodeIds.d.ts +47 -0
- package/dist/types/Tokens/TokenResolver.d.ts +25 -0
- package/dist/types/Tokens/index.d.ts +1 -0
- package/dist/types/Transactions/Transaction.d.ts +36 -0
- package/dist/types/Transactions/UndoManager.d.ts +63 -0
- package/dist/types/Transactions/index.d.ts +3 -0
- package/dist/types/Transformers/ASTOptimizer.d.ts +34 -0
- package/dist/types/Transformers/GradientTransformer.d.ts +18 -0
- package/dist/types/Transformers/GrowTransformer.d.ts +16 -0
- package/dist/types/Transformers/RainbowTransformer.d.ts +17 -0
- package/dist/types/Transformers/SineWaveTransformer.d.ts +17 -0
- package/dist/types/Transformers/Transformer.d.ts +17 -0
- package/dist/types/Types/core.d.ts +104 -0
- package/dist/types/Types/diagnostics.d.ts +85 -0
- package/dist/types/Types/index.d.ts +11 -0
- package/dist/types/Types/operations.d.ts +109 -0
- package/dist/types/Types/queries.d.ts +67 -0
- package/dist/types/Types/symbols.d.ts +42 -0
- package/dist/types/Types/tokens.d.ts +75 -0
- package/dist/types/Utils/BBCodeGenerator.d.ts +21 -0
- package/dist/types/Utils/ColorMath.d.ts +92 -0
- package/dist/types/Utils/EffectMath.d.ts +643 -0
- package/dist/types/Utils/TagVocabulary.d.ts +104 -0
- package/dist/types/Utils/TagVocabulary.test.d.ts +1 -0
- package/dist/types/Utils/color.d.ts +29 -0
- package/dist/types/Utils/dom-to-svg.d.ts +28 -0
- package/dist/types/Utils/dom-to-svg.test.d.ts +1 -0
- package/dist/types/Utils/treeTransformers.d.ts +160 -0
- package/dist/types/Visitors/BBBlocksExporter.d.ts +27 -0
- package/dist/types/Visitors/BBCodeExporter.d.ts +90 -0
- package/dist/types/Visitors/BlockPatcher.d.ts +91 -0
- package/dist/types/Visitors/DOMMorpher.d.ts +13 -0
- package/dist/types/Visitors/HTMLRenderer.d.ts +380 -0
- package/dist/types/Visitors/JSONExporter.d.ts +42 -0
- package/dist/types/Visitors/MarkdownExporter.d.ts +28 -0
- package/dist/types/Visitors/SVGRenderer.d.ts +16 -0
- package/dist/types/Visitors/TiptapExporter.d.ts +40 -0
- package/dist/types/Visitors/Visitor.d.ts +35 -0
- package/dist/types/Visitors/index.d.ts +9 -0
- package/dist/types/Visuals/BoxDrawer.d.ts +49 -0
- package/dist/types/Visuals/index.d.ts +38 -0
- package/dist/types/index.d.ts +154 -0
- package/package.json +14 -10
- package/src/Analysis/Passes/Analysis/ColorUsageAnalyzer.ts +103 -0
- package/src/Analysis/Passes/Analysis/GradientAnalyzer.ts +294 -20
- package/src/Analysis/Passes/Analysis/SymbolAnalyzer.ts +257 -0
- package/src/Analysis/Passes/Decision/PaletteRemapDecision.ts +425 -0
- package/src/Analysis/Pipeline/Pipeline.ts +15 -2
- package/src/Analysis/index.ts +8 -2
- package/src/BBCode/BBCodeDocumentModel.ts +3 -0
- package/src/BBCode/BBCodeToGreenNode.ts +18 -12
- package/src/BBCode/Parser.ts +38 -1
- package/src/Edits/EditPlan.ts +290 -0
- package/src/Edits/Optimizer.ts +185 -0
- package/src/Edits/Rules/Rule.ts +145 -0
- package/src/Edits/Rules/dropEmptyTags.ts +110 -0
- package/src/Edits/Rules/dropRedundantNesting.ts +120 -0
- package/src/Edits/Rules/index.ts +28 -0
- package/src/Edits/Rules/mergeAdjacent.ts +224 -0
- package/src/Edits/Rules/reorderWrappers.ts +154 -0
- package/src/Edits/Rules/shortenHex.ts +79 -0
- package/src/Edits/Rules/tagValue.ts +105 -0
- package/src/Edits/Rules/unwrapInvisibleColor.ts +93 -0
- package/src/Edits/applyEdits.ts +54 -0
- package/src/Edits/index.ts +22 -0
- package/src/Events/EventBus.ts +18 -0
- package/src/HTML/HTMLToGreenNode.ts +185 -37
- package/src/Incremental/BracketIndex.ts +249 -0
- package/src/Incremental/IncrementalParser.ts +464 -41
- package/src/Lexer/BBCodeLexer.ts +25 -3
- package/src/Lexer/index.ts +1 -1
- package/src/Markdown/MarkdownAST.ts +65 -2
- package/src/Markdown/MarkdownLexer.ts +69 -14
- package/src/Markdown/MarkdownParser.ts +749 -62
- package/src/Markdown/MarkdownToGreenNode.ts +148 -27
- package/src/MilHibri/MilHibriDocumentModel.ts +56 -0
- package/src/Model/DocumentModel.ts +138 -5
- package/src/Model/TagRegistry.ts +129 -134
- package/src/Reconciler/SurgicalReconciler.ts +460 -0
- package/src/Repair/NestingRepair.ts +116 -0
- package/src/Repair/index.ts +1 -0
- package/src/Semantic/SemanticAnalyzer.ts +1802 -65
- package/src/Syntax/GreenNodePool.ts +12 -12
- package/src/Syntax/RedNode.ts +17 -3
- package/src/Syntax/RedNodeStore.ts +9 -1
- package/src/Syntax/nodeAttr.ts +172 -0
- package/src/Tokens/TokenResolver.ts +55 -0
- package/src/Tokens/index.ts +1 -0
- package/src/Types/core.ts +15 -0
- package/src/Utils/ColorMath.ts +276 -55
- package/src/Utils/EffectMath.ts +2462 -0
- package/src/Utils/TagVocabulary.ts +141 -0
- package/src/Utils/dom-to-svg.ts +28 -12
- package/src/Visitors/BBBlocksExporter.ts +15 -1
- package/src/Visitors/BBCodeExporter.ts +319 -32
- package/src/Visitors/BlockPatcher.ts +87 -11
- package/src/Visitors/DOMMorpher.ts +15 -5
- package/src/Visitors/HTMLRenderer.ts +374 -151
- package/src/Visitors/MarkdownExporter.ts +98 -41
- package/src/Visitors/TiptapExporter.ts +1 -0
- package/src/Visuals/index.ts +7 -4
- package/src/Visuals/lyne.css +117 -6
- package/src/Visuals/osu.css +36 -5
- package/src/index.ts +172 -5
- package/dist/index.d.mts +0 -4612
- package/dist/index.d.ts +0 -4612
- package/src/Analysis/Passes/Analysis/__tests__/GradientAnalyzer.test.ts +0 -135
- package/src/Analysis/Passes/Analysis/__tests__/MergeableColorAnalyzer.test.ts +0 -84
- package/src/Analysis/Passes/Analysis/__tests__/RainbowAnalyzer.test.ts +0 -99
- package/src/Analysis/Passes/Analysis/__tests__/WaveAnalyzer.test.ts +0 -119
- package/src/Analysis/Passes/Decision/__tests__/DefaultDecision.test.ts +0 -179
- package/src/Analysis/__tests__/Integration.test.ts +0 -162
- package/src/Analysis/__tests__/Pipeline.test.ts +0 -133
- package/src/Diff/__tests__/TreeDiffer.test.ts +0 -65
- package/src/Incremental/__tests__/IncrementalParser.test.ts +0 -164
- package/src/Syntax/__tests__/GreenNode.test.ts +0 -33
- package/src/Syntax/__tests__/RedNode.test.ts +0 -81
- package/src/Syntax/__tests__/RedNodeStore.test.ts +0 -104
- package/src/Tests/ASTOptimizerIdempotence.test.ts +0 -77
- package/src/Tests/BlockPatcher.test.ts +0 -437
- package/src/Tests/BlockPatcherWindowed.test.ts +0 -364
- package/src/Tests/BoxDrawer.test.ts +0 -217
- package/src/Tests/BoxRichTitle.test.ts +0 -105
- package/src/Tests/Chars500kBenchmark.test.ts +0 -151
- package/src/Tests/Chars500kEdits.test.ts +0 -321
- package/src/Tests/CollabPositions.test.ts +0 -146
- package/src/Tests/CompilerPathProfiling.test.ts +0 -186
- package/src/Tests/DOMMorpher.test.ts +0 -142
- package/src/Tests/DomPatchPerf.test.ts +0 -60
- package/src/Tests/EffectSegments.snapshot.json +0 -616
- package/src/Tests/EffectSegments.test.ts +0 -68
- package/src/Tests/FindNodeAtOffset.test.ts +0 -65
- package/src/Tests/Fuzzer.test.ts +0 -166
- package/src/Tests/GreenNodePool.test.ts +0 -153
- package/src/Tests/Lexer.test.ts +0 -238
- package/src/Tests/LyneMode.test.ts +0 -291
- package/src/Tests/ModelCoherence.test.ts +0 -180
- package/src/Tests/Partition.test.ts +0 -238
- package/src/Tests/PluginTags.test.ts +0 -150
- package/src/Tests/ProblematicSection.test.ts +0 -46
- package/src/Tests/ProblematicSectionHTML.test.ts +0 -58
- package/src/Tests/RedReuse.test.ts +0 -134
- package/src/Tests/ReproDelete20k.test.ts +0 -62
- package/src/Tests/SemanticValidators.test.ts +0 -136
- package/src/Tests/StableNodeIds.test.ts +0 -210
- package/src/Tests/StudioColorBloat.test.ts +0 -25
- package/src/Tests/StudioDebugText.test.ts +0 -27
- package/src/Tests/StudioTrailingChar.test.ts +0 -25
- package/src/Tests/StudioValidText.test.ts +0 -25
- package/src/Tests/UrlImgBug.test.ts +0 -23
- package/src/Tests/VisualBuilderFidelity.test.ts +0 -105
- package/src/Tests/referenceDocument.ts +0 -119
- package/src/Utils/dom-to-svg.test.ts +0 -86
|
@@ -1,10 +1,35 @@
|
|
|
1
|
-
import { MarkdownToken, MarkdownFenceToken } from './MarkdownLexer';
|
|
2
|
-
import {
|
|
1
|
+
import { MarkdownToken, MarkdownFenceToken, MarkdownContainerToken, scanMarkdown } from './MarkdownLexer';
|
|
2
|
+
import {
|
|
3
|
+
MarkdownNode,
|
|
4
|
+
MarkdownDocument,
|
|
5
|
+
MarkdownParagraph,
|
|
6
|
+
MarkdownText,
|
|
7
|
+
MarkdownHeading,
|
|
8
|
+
MarkdownStrikethrough,
|
|
9
|
+
MarkdownUnderline,
|
|
10
|
+
MarkdownColor,
|
|
11
|
+
MarkdownFontSize,
|
|
12
|
+
MarkdownFont,
|
|
13
|
+
MarkdownAlign,
|
|
14
|
+
MarkdownBox,
|
|
15
|
+
MarkdownSeparator,
|
|
16
|
+
} from './MarkdownAST';
|
|
17
|
+
|
|
18
|
+
interface BBCodeTagInfo {
|
|
19
|
+
tagName: string;
|
|
20
|
+
attrValue?: string;
|
|
21
|
+
tokenEndIndex: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const BLOCK_BBCODE_TAGS = new Set([
|
|
25
|
+
'box', 'boxw', 'spoilerbox', 'centre', 'center', 'right', 'left', 'align', 'quote', 'notice', 'wnotice', 'code',
|
|
26
|
+
'columns', 'tables', 'row', 'th', 'col', 'glass', 'card', 'neon-box', 'scroll', 'separator', 'list', 'group', 'svg', 'imagemap', 'image',
|
|
27
|
+
]);
|
|
3
28
|
|
|
4
29
|
/**
|
|
5
30
|
* DocumentEngine — MarkdownParser
|
|
6
31
|
*
|
|
7
|
-
*
|
|
32
|
+
* Parses Markdown tokens with hybrid BBCode embedding support into a typed AST.
|
|
8
33
|
*/
|
|
9
34
|
export class MarkdownParser {
|
|
10
35
|
private tokens: MarkdownToken[];
|
|
@@ -25,12 +50,8 @@ export class MarkdownParser {
|
|
|
25
50
|
while (this.match('newline')) newlines++;
|
|
26
51
|
|
|
27
52
|
if (newlines > 0) {
|
|
28
|
-
// Since blocks like parseParagraph consume their trailing newline,
|
|
29
|
-
// seeing 1 newline here means there were 2 newlines in the source (a blank line).
|
|
30
|
-
// 1 newline here -> 1 empty_line
|
|
31
|
-
// 2 newlines here -> 2 empty_lines
|
|
32
53
|
for (let i = 0; i < newlines; i++) {
|
|
33
|
-
doc.children.push({ type: 'empty_line' }
|
|
54
|
+
doc.children.push({ type: 'empty_line' });
|
|
34
55
|
}
|
|
35
56
|
}
|
|
36
57
|
|
|
@@ -50,6 +71,13 @@ export class MarkdownParser {
|
|
|
50
71
|
|
|
51
72
|
const token = this.peek();
|
|
52
73
|
|
|
74
|
+
// Horizontal Rule (---)
|
|
75
|
+
if (token.kind === 'hr' || (token.kind === 'dash' && ('value' in token && token.value === '---'))) {
|
|
76
|
+
this.advance();
|
|
77
|
+
this.match('newline');
|
|
78
|
+
return { type: 'separator' };
|
|
79
|
+
}
|
|
80
|
+
|
|
53
81
|
if (token.kind === 'hash') {
|
|
54
82
|
return this.parseHeading();
|
|
55
83
|
}
|
|
@@ -57,16 +85,14 @@ export class MarkdownParser {
|
|
|
57
85
|
if (token.kind === 'gt') {
|
|
58
86
|
const saved = this.current;
|
|
59
87
|
this.advance(); // consume gt
|
|
60
|
-
|
|
61
|
-
if (next1 && next1.kind === 'text' && ('value' in next1) && (next1.value as string).trim() === '') {
|
|
88
|
+
while (!this.isAtEnd() && this.peek()?.kind === 'text' && ('value' in this.peek()) && (this.peek() as any).value.trim() === '') {
|
|
62
89
|
this.advance(); // consume space
|
|
63
90
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return this.parseNotice();
|
|
91
|
+
if (this.check('bracket_open')) {
|
|
92
|
+
const nextToken = this.tokens[this.current + 1];
|
|
93
|
+
if (nextToken && (nextToken.kind === 'bang' || ('value' in nextToken && nextToken.value === '!'))) {
|
|
94
|
+
this.current = saved; // restore
|
|
95
|
+
return this.parseNotice();
|
|
70
96
|
}
|
|
71
97
|
}
|
|
72
98
|
this.current = saved; // restore
|
|
@@ -76,13 +102,23 @@ export class MarkdownParser {
|
|
|
76
102
|
if (token.kind === 'fence') {
|
|
77
103
|
return this.parseFence();
|
|
78
104
|
}
|
|
105
|
+
|
|
106
|
+
// Fenced Container Divs (::: details Title, ::: center, ::: warning)
|
|
107
|
+
if (token.kind === 'container') {
|
|
108
|
+
return this.parseContainer();
|
|
109
|
+
}
|
|
79
110
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
111
|
+
// Lists (unordered or ordered)
|
|
112
|
+
if (this.isListStart()) {
|
|
113
|
+
return this.parseList();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Embedded Block BBCode tags (e.g. [box=Title]...[/box], [centre]...[/centre])
|
|
117
|
+
if (token.kind === 'bracket_open') {
|
|
118
|
+
const bbTag = this.peekBBCodeOpenTag(this.current);
|
|
119
|
+
if (bbTag && BLOCK_BBCODE_TAGS.has(bbTag.tagName)) {
|
|
120
|
+
return this.parseBBCodeBlock(bbTag);
|
|
121
|
+
}
|
|
86
122
|
}
|
|
87
123
|
|
|
88
124
|
// Default to paragraph
|
|
@@ -93,20 +129,18 @@ export class MarkdownParser {
|
|
|
93
129
|
const hash = this.advance();
|
|
94
130
|
const level = hash.kind === 'hash' ? Math.min(hash.value.length, 6) : 1;
|
|
95
131
|
|
|
96
|
-
// We don't want to blindly consume a text token if it contains actual text.
|
|
97
|
-
// Let's just let parseInline handle it. The renderer can deal with leading spaces,
|
|
98
|
-
// or we can trim the first child if it's text.
|
|
99
132
|
const children: MarkdownNode[] = [];
|
|
100
133
|
while (!this.isAtEnd() && !this.check('newline')) {
|
|
101
134
|
const inlineNode = this.parseInline();
|
|
102
135
|
if (inlineNode) {
|
|
103
136
|
children.push(inlineNode);
|
|
137
|
+
} else {
|
|
138
|
+
break;
|
|
104
139
|
}
|
|
105
140
|
}
|
|
106
141
|
|
|
107
|
-
// Trim leading space from the first child if it's text
|
|
108
142
|
if (children.length > 0 && children[0].type === 'text') {
|
|
109
|
-
children[0].value = (children[0] as
|
|
143
|
+
children[0].value = (children[0] as MarkdownText).value.replace(/^\s+/, '');
|
|
110
144
|
}
|
|
111
145
|
|
|
112
146
|
this.match('newline');
|
|
@@ -120,26 +154,113 @@ export class MarkdownParser {
|
|
|
120
154
|
|
|
121
155
|
private parseBlockquote(): MarkdownNode {
|
|
122
156
|
const children: MarkdownNode[] = [];
|
|
123
|
-
|
|
124
|
-
while (!this.isAtEnd()) {
|
|
125
|
-
|
|
126
|
-
|
|
157
|
+
this.advance(); // consume initial gt
|
|
158
|
+
while (!this.isAtEnd() && this.peek()?.kind === 'text' && ('value' in this.peek()) && (this.peek() as any).value.trim() === '') {
|
|
159
|
+
this.advance();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Parse the first line's inline nodes to check for author attribution (e.g. **Author wrote:**, **Author:**, **Author**)
|
|
163
|
+
const firstLineNodes: MarkdownNode[] = [];
|
|
164
|
+
while (!this.isAtEnd() && !this.check('newline')) {
|
|
165
|
+
const inlineNode = this.parseInline();
|
|
166
|
+
if (inlineNode) firstLineNodes.push(inlineNode);
|
|
167
|
+
else break;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
let source: string | undefined;
|
|
171
|
+
|
|
172
|
+
const getPlainText = (n: MarkdownNode): string => {
|
|
173
|
+
if ('value' in n && typeof (n as any).value === 'string') return (n as any).value;
|
|
174
|
+
if ('children' in n && Array.isArray((n as any).children)) {
|
|
175
|
+
return (n as any).children.map(getPlainText).join('');
|
|
127
176
|
}
|
|
128
|
-
|
|
177
|
+
return '';
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// Trim trailing whitespace nodes from the first line
|
|
181
|
+
const trimmed = [...firstLineNodes];
|
|
182
|
+
while (
|
|
183
|
+
trimmed.length > 0 &&
|
|
184
|
+
trimmed[trimmed.length - 1].type === 'text' &&
|
|
185
|
+
(trimmed[trimmed.length - 1] as any).value.trim() === ''
|
|
186
|
+
) {
|
|
187
|
+
trimmed.pop();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (trimmed.length > 0) {
|
|
191
|
+
// 1. Single bold node: **Author**, **Author wrote:**, **Author:**
|
|
192
|
+
if (trimmed.length === 1 && trimmed[0].type === 'strong') {
|
|
193
|
+
const raw = getPlainText(trimmed[0]).trim();
|
|
194
|
+
const cleaned = raw.replace(/\s*wrote:\s*$/i, '').replace(/:\s*$/, '').trim();
|
|
195
|
+
if (cleaned) {
|
|
196
|
+
source = cleaned;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// 2. Bold node followed by "wrote:", "wrote", ":", etc.
|
|
200
|
+
else if (
|
|
201
|
+
trimmed.length === 2 &&
|
|
202
|
+
trimmed[0].type === 'strong' &&
|
|
203
|
+
trimmed[1].type === 'text' &&
|
|
204
|
+
/^\s*(wrote:|wrote|:)\s*$/i.test((trimmed[1] as any).value)
|
|
205
|
+
) {
|
|
206
|
+
const cleaned = getPlainText(trimmed[0]).trim();
|
|
207
|
+
if (cleaned) {
|
|
208
|
+
source = cleaned;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// 3. Plain text patterns: [quote=Author] or Author wrote:
|
|
212
|
+
else {
|
|
213
|
+
const fullLine = trimmed.map(getPlainText).join('').trim();
|
|
214
|
+
const quoteMatch = fullLine.match(/^\[quote=["']?([^\]"']+)["']?\]$/i);
|
|
215
|
+
if (quoteMatch) {
|
|
216
|
+
source = quoteMatch[1].trim();
|
|
217
|
+
} else {
|
|
218
|
+
const wroteMatch = fullLine.match(/^(\*\*)?([^*\n]+?)\1\s*wrote:\s*$/i);
|
|
219
|
+
if (wroteMatch) {
|
|
220
|
+
source = wroteMatch[2].trim();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (!source) {
|
|
227
|
+
children.push(...firstLineNodes);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
while (!this.isAtEnd()) {
|
|
129
231
|
if (this.check('newline')) {
|
|
130
232
|
this.advance();
|
|
131
233
|
if (this.check('newline') || this.isAtEnd()) {
|
|
132
234
|
break; // Empty line ends blockquote
|
|
133
235
|
}
|
|
134
|
-
children.
|
|
236
|
+
if (children.length > 0) {
|
|
237
|
+
children.push({ type: 'text', value: '\n' });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (this.match('gt')) {
|
|
242
|
+
// blockquote line prefix
|
|
243
|
+
while (!this.isAtEnd() && this.peek()?.kind === 'text' && ('value' in this.peek()) && (this.peek() as any).value.trim() === '') {
|
|
244
|
+
this.advance();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (this.check('newline')) {
|
|
249
|
+
// will be handled on next loop iteration
|
|
135
250
|
} else {
|
|
136
251
|
const inlineNode = this.parseInline();
|
|
137
252
|
if (inlineNode) children.push(inlineNode);
|
|
253
|
+
else break;
|
|
138
254
|
}
|
|
139
255
|
}
|
|
140
256
|
|
|
257
|
+
if (children.length > 0 && children[0].type === 'text') {
|
|
258
|
+
(children[0] as MarkdownText).value = (children[0] as MarkdownText).value.replace(/^\s+/, '');
|
|
259
|
+
}
|
|
260
|
+
|
|
141
261
|
return {
|
|
142
262
|
type: 'blockquote',
|
|
263
|
+
source,
|
|
143
264
|
children
|
|
144
265
|
};
|
|
145
266
|
}
|
|
@@ -147,16 +268,40 @@ export class MarkdownParser {
|
|
|
147
268
|
private parseNotice(): MarkdownNode {
|
|
148
269
|
const children: MarkdownNode[] = [];
|
|
149
270
|
this.advance(); // consume gt
|
|
271
|
+
while (!this.isAtEnd() && this.peek()?.kind === 'text' && ('value' in this.peek()) && (this.peek() as any).value.trim() === '') {
|
|
272
|
+
this.advance();
|
|
273
|
+
}
|
|
150
274
|
|
|
151
|
-
// consume the [!NOTE] part
|
|
275
|
+
// consume the [!NOTE] or [!NOTE]- Title part
|
|
276
|
+
let headerText = '';
|
|
277
|
+
let isCollapsible = false;
|
|
278
|
+
let title = '';
|
|
279
|
+
|
|
152
280
|
while (!this.isAtEnd() && !this.check('newline')) {
|
|
153
|
-
if (this.match('bracket_close'))
|
|
154
|
-
|
|
281
|
+
if (this.match('bracket_close')) {
|
|
282
|
+
// Check if immediately followed by - or + (Obsidian collapsible syntax)
|
|
283
|
+
if (this.match('dash')) isCollapsible = true;
|
|
284
|
+
else if (this.match('plus')) isCollapsible = true;
|
|
285
|
+
|
|
286
|
+
// Read rest of title line
|
|
287
|
+
while (!this.isAtEnd() && !this.check('newline')) {
|
|
288
|
+
const t = this.advance();
|
|
289
|
+
title += this.tokenValue(t);
|
|
290
|
+
}
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
const t = this.advance();
|
|
294
|
+
headerText += this.tokenValue(t);
|
|
155
295
|
}
|
|
156
296
|
|
|
297
|
+
this.match('newline');
|
|
298
|
+
|
|
157
299
|
while (!this.isAtEnd()) {
|
|
158
300
|
if (this.match('gt')) {
|
|
159
301
|
// blockquote line prefix
|
|
302
|
+
while (!this.isAtEnd() && this.peek()?.kind === 'text' && ('value' in this.peek()) && (this.peek() as any).value.trim() === '') {
|
|
303
|
+
this.advance();
|
|
304
|
+
}
|
|
160
305
|
}
|
|
161
306
|
if (this.check('newline')) {
|
|
162
307
|
this.advance();
|
|
@@ -165,22 +310,87 @@ export class MarkdownParser {
|
|
|
165
310
|
} else {
|
|
166
311
|
const inlineNode = this.parseInline();
|
|
167
312
|
if (inlineNode) children.push(inlineNode);
|
|
313
|
+
else break;
|
|
168
314
|
}
|
|
169
315
|
}
|
|
170
316
|
|
|
317
|
+
if (isCollapsible) {
|
|
318
|
+
const cleanTitle = title.trim() || 'Details';
|
|
319
|
+
return { type: 'box', title: cleanTitle, rawTitle: cleanTitle, children };
|
|
320
|
+
}
|
|
321
|
+
|
|
171
322
|
return { type: 'notice', children };
|
|
172
323
|
}
|
|
324
|
+
|
|
325
|
+
private parseContainer(): MarkdownNode {
|
|
326
|
+
const token = this.advance() as MarkdownContainerToken;
|
|
327
|
+
const info = token.info ? token.info.trim() : '';
|
|
328
|
+
const firstSpace = info.indexOf(' ');
|
|
329
|
+
const tag = (firstSpace === -1 ? info : info.slice(0, firstSpace)).toLowerCase();
|
|
330
|
+
const rest = firstSpace === -1 ? '' : info.slice(firstSpace + 1).trim();
|
|
331
|
+
|
|
332
|
+
this.match('newline');
|
|
333
|
+
const children: MarkdownNode[] = [];
|
|
334
|
+
|
|
335
|
+
while (!this.isAtEnd()) {
|
|
336
|
+
if (this.check('container')) {
|
|
337
|
+
this.advance(); // closing :::
|
|
338
|
+
this.match('newline');
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
if (this.check('newline')) {
|
|
342
|
+
this.advance();
|
|
343
|
+
while (this.match('newline')) {
|
|
344
|
+
children.push({ type: 'empty_line' });
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
const block = this.parseBlock();
|
|
348
|
+
if (block) children.push(block);
|
|
349
|
+
else break;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
switch (tag) {
|
|
354
|
+
case 'details':
|
|
355
|
+
case 'box':
|
|
356
|
+
case 'boxw':
|
|
357
|
+
return { type: 'box', title: rest || 'Details', rawTitle: rest, children };
|
|
358
|
+
case 'spoilerbox':
|
|
359
|
+
return { type: 'spoilerbox', title: rest || 'Spoiler', rawTitle: rest, children };
|
|
360
|
+
case 'center':
|
|
361
|
+
case 'centre':
|
|
362
|
+
return { type: 'center', children };
|
|
363
|
+
case 'right':
|
|
364
|
+
return { type: 'right', children };
|
|
365
|
+
case 'left':
|
|
366
|
+
return { type: 'left', children };
|
|
367
|
+
case 'info':
|
|
368
|
+
case 'note':
|
|
369
|
+
case 'warning':
|
|
370
|
+
case 'tip':
|
|
371
|
+
case 'caution':
|
|
372
|
+
case 'notice':
|
|
373
|
+
case 'wnotice':
|
|
374
|
+
return { type: 'notice', color: rest ? rest : undefined, children };
|
|
375
|
+
case 'quote':
|
|
376
|
+
return { type: 'blockquote', source: rest || undefined, children };
|
|
377
|
+
default:
|
|
378
|
+
return { type: 'box', title: info || 'Box', rawTitle: info, children };
|
|
379
|
+
}
|
|
380
|
+
}
|
|
173
381
|
|
|
174
382
|
private tokenValue(token: MarkdownToken): string {
|
|
383
|
+
if (!token) return '';
|
|
175
384
|
if ('value' in token) return (token as any).value;
|
|
176
385
|
switch (token.kind) {
|
|
177
386
|
case 'bracket_open': return '[';
|
|
178
387
|
case 'bracket_close': return ']';
|
|
388
|
+
case 'brace_open': return '{';
|
|
389
|
+
case 'brace_close': return '}';
|
|
179
390
|
case 'paren_open': return '(';
|
|
180
391
|
case 'paren_close': return ')';
|
|
181
392
|
case 'bang': return '!';
|
|
182
393
|
case 'gt': return '>';
|
|
183
|
-
case 'plus': return '+';
|
|
184
394
|
case 'dot': return '.';
|
|
185
395
|
case 'hr': return '---';
|
|
186
396
|
default: return '';
|
|
@@ -194,33 +404,76 @@ export class MarkdownParser {
|
|
|
194
404
|
while (!this.isAtEnd()) {
|
|
195
405
|
if (this.check('fence')) {
|
|
196
406
|
this.advance(); // close fence
|
|
407
|
+
this.match('newline');
|
|
197
408
|
break;
|
|
198
409
|
}
|
|
199
410
|
const p = this.advance();
|
|
200
411
|
code += this.tokenValue(p);
|
|
201
412
|
}
|
|
202
|
-
|
|
413
|
+
if (code.startsWith('\r\n')) code = code.slice(2);
|
|
414
|
+
else if (code.startsWith('\n')) code = code.slice(1);
|
|
415
|
+
if (code.endsWith('\r\n')) code = code.slice(0, -2);
|
|
416
|
+
else if (code.endsWith('\n')) code = code.slice(0, -1);
|
|
417
|
+
|
|
418
|
+
return { type: 'code_block', lang: value, value: code };
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private isListStart(): boolean {
|
|
422
|
+
if (this.isAtEnd()) return false;
|
|
423
|
+
const token = this.peek();
|
|
424
|
+
if (token.kind === 'dash' || token.kind === 'star') {
|
|
425
|
+
const next = this.tokens[this.current + 1];
|
|
426
|
+
if (next && next.kind === 'text' && ('value' in next) && (next.value as string).startsWith(' ')) {
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return this.isOrderedListStart();
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
private isOrderedListStart(): boolean {
|
|
434
|
+
if (this.isAtEnd()) return false;
|
|
435
|
+
const token = this.peek();
|
|
436
|
+
if (token.kind === 'text' && /^\d+$/.test(('value' in token ? (token as any).value : '').trim())) {
|
|
437
|
+
const next1 = this.tokens[this.current + 1];
|
|
438
|
+
if (next1 && next1.kind === 'dot') {
|
|
439
|
+
const next2 = this.tokens[this.current + 2];
|
|
440
|
+
if (next2 && next2.kind === 'text' && ('value' in next2) && (next2.value as string).startsWith(' ')) {
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return false;
|
|
203
446
|
}
|
|
204
447
|
|
|
205
448
|
private parseList(): MarkdownNode {
|
|
206
449
|
const items: any[] = [];
|
|
207
|
-
|
|
208
|
-
|
|
450
|
+
const ordered = this.isOrderedListStart();
|
|
451
|
+
|
|
452
|
+
while (!this.isAtEnd()) {
|
|
453
|
+
if (ordered) {
|
|
454
|
+
if (!this.isOrderedListStart()) break;
|
|
455
|
+
this.advance(); // number
|
|
456
|
+
this.advance(); // dot
|
|
457
|
+
} else {
|
|
458
|
+
if (!this.check('dash') && !this.check('star')) break;
|
|
459
|
+
this.advance(); // dash or star
|
|
460
|
+
}
|
|
461
|
+
|
|
209
462
|
const children: MarkdownNode[] = [];
|
|
210
463
|
while (!this.isAtEnd() && !this.check('newline')) {
|
|
211
464
|
const inlineNode = this.parseInline();
|
|
212
465
|
if (inlineNode) children.push(inlineNode);
|
|
466
|
+
else break;
|
|
213
467
|
}
|
|
214
|
-
// Trim leading space from the first child if it's text
|
|
215
468
|
if (children.length > 0 && children[0].type === 'text') {
|
|
216
|
-
children[0].value = (children[0] as
|
|
469
|
+
children[0].value = (children[0] as MarkdownText).value.replace(/^\s+/, '');
|
|
217
470
|
}
|
|
218
471
|
items.push({ type: 'list_item', children });
|
|
219
472
|
if (this.match('newline')) {
|
|
220
473
|
if (this.check('newline')) break; // End of list on double newline
|
|
221
474
|
}
|
|
222
475
|
}
|
|
223
|
-
return { type: 'list', ordered
|
|
476
|
+
return { type: 'list', ordered, children: items };
|
|
224
477
|
}
|
|
225
478
|
|
|
226
479
|
private parseParagraph(): MarkdownParagraph {
|
|
@@ -229,21 +482,30 @@ export class MarkdownParser {
|
|
|
229
482
|
while (!this.isAtEnd()) {
|
|
230
483
|
if (this.check('newline')) {
|
|
231
484
|
this.advance();
|
|
232
|
-
// Two newlines mean end of paragraph
|
|
233
485
|
if (this.check('newline') || this.isAtEnd()) {
|
|
234
486
|
break;
|
|
235
487
|
} else {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
488
|
+
if (this.check('hash') || this.check('gt') || this.check('fence') || this.check('hr') || this.check('container')) {
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
if (this.isListStart()) {
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
// Also break if block BBCode starts
|
|
495
|
+
if (this.check('bracket_open')) {
|
|
496
|
+
const bbTag = this.peekBBCodeOpenTag(this.current);
|
|
497
|
+
if (bbTag && BLOCK_BBCODE_TAGS.has(bbTag.tagName)) {
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
239
500
|
}
|
|
240
|
-
// Single newline treated as space in paragraph
|
|
241
501
|
children.push({ type: 'text', value: '\n' });
|
|
242
502
|
}
|
|
243
503
|
} else {
|
|
244
504
|
const inlineNode = this.parseInline();
|
|
245
505
|
if (inlineNode) {
|
|
246
506
|
children.push(inlineNode);
|
|
507
|
+
} else {
|
|
508
|
+
break;
|
|
247
509
|
}
|
|
248
510
|
}
|
|
249
511
|
}
|
|
@@ -254,7 +516,7 @@ export class MarkdownParser {
|
|
|
254
516
|
};
|
|
255
517
|
}
|
|
256
518
|
|
|
257
|
-
|
|
519
|
+
public parseInline(terminator?: { kind: string, value?: string }): MarkdownNode | null {
|
|
258
520
|
if (this.isAtEnd()) return null;
|
|
259
521
|
|
|
260
522
|
if (terminator) {
|
|
@@ -264,9 +526,54 @@ export class MarkdownParser {
|
|
|
264
526
|
}
|
|
265
527
|
}
|
|
266
528
|
|
|
529
|
+
// Check for BBCode close tag matching an active parent
|
|
530
|
+
if (this.check('bracket_open')) {
|
|
531
|
+
const peekClose = this.peekBBCodeCloseTag(this.current);
|
|
532
|
+
if (peekClose) {
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Check for BBCode open tag
|
|
537
|
+
const openTag = this.peekBBCodeOpenTag(this.current);
|
|
538
|
+
if (openTag) {
|
|
539
|
+
return this.parseBBCodeInline(openTag);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
267
543
|
const token = this.advance();
|
|
544
|
+
|
|
545
|
+
// Inline Code (`code`)
|
|
546
|
+
if (token.kind === 'backtick') {
|
|
547
|
+
const val = ('value' in token) ? token.value as string : '`';
|
|
548
|
+
if (val === '`') {
|
|
549
|
+
const savedCurrent = this.current;
|
|
550
|
+
let code = '';
|
|
551
|
+
let closed = false;
|
|
552
|
+
|
|
553
|
+
while (!this.isAtEnd()) {
|
|
554
|
+
const next = this.peek();
|
|
555
|
+
if (next.kind === 'newline') break;
|
|
556
|
+
|
|
557
|
+
if (next.kind === 'backtick' && ('value' in next && next.value === '`')) {
|
|
558
|
+
this.advance();
|
|
559
|
+
closed = true;
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const p = this.advance();
|
|
564
|
+
code += this.tokenValue(p);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (closed) {
|
|
568
|
+
return { type: 'code_inline', value: code };
|
|
569
|
+
} else {
|
|
570
|
+
this.current = savedCurrent;
|
|
571
|
+
return { type: 'text', value: '`' };
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
268
575
|
|
|
269
|
-
// Bold and Italic
|
|
576
|
+
// Bold and Italic (*, **, _, __)
|
|
270
577
|
if (token.kind === 'star' || token.kind === 'underscore') {
|
|
271
578
|
const val = ('value' in token) ? token.value as string : '';
|
|
272
579
|
if (val === '**' || val === '__' || val === '*' || val === '_') {
|
|
@@ -278,10 +585,10 @@ export class MarkdownParser {
|
|
|
278
585
|
|
|
279
586
|
while (!this.isAtEnd()) {
|
|
280
587
|
const next = this.peek();
|
|
281
|
-
if (next.kind === 'newline') break;
|
|
588
|
+
if (next.kind === 'newline') break;
|
|
282
589
|
|
|
283
590
|
if (next.kind === token.kind && ('value' in next && next.value === val)) {
|
|
284
|
-
this.advance();
|
|
591
|
+
this.advance();
|
|
285
592
|
closed = true;
|
|
286
593
|
break;
|
|
287
594
|
}
|
|
@@ -297,14 +604,13 @@ export class MarkdownParser {
|
|
|
297
604
|
if (closed) {
|
|
298
605
|
return { type, children };
|
|
299
606
|
} else {
|
|
300
|
-
// Backtrack and treat as text
|
|
301
607
|
this.current = savedCurrent;
|
|
302
608
|
return { type: 'text', value: val };
|
|
303
609
|
}
|
|
304
610
|
}
|
|
305
611
|
}
|
|
306
612
|
|
|
307
|
-
// Strikethrough
|
|
613
|
+
// Strikethrough (~~)
|
|
308
614
|
if (token.kind === 'tilde') {
|
|
309
615
|
const val = ('value' in token) ? token.value as string : '';
|
|
310
616
|
if (val === '~~') {
|
|
@@ -328,7 +634,7 @@ export class MarkdownParser {
|
|
|
328
634
|
}
|
|
329
635
|
|
|
330
636
|
if (closed) {
|
|
331
|
-
return { type: 'strikethrough', children }
|
|
637
|
+
return { type: 'strikethrough', children };
|
|
332
638
|
} else {
|
|
333
639
|
this.current = savedCurrent;
|
|
334
640
|
return { type: 'text', value: val };
|
|
@@ -336,6 +642,68 @@ export class MarkdownParser {
|
|
|
336
642
|
}
|
|
337
643
|
}
|
|
338
644
|
|
|
645
|
+
// Underline (++) (CriticMarkup / markdown-it-ins)
|
|
646
|
+
if (token.kind === 'plus' && ('value' in token && token.value === '++')) {
|
|
647
|
+
const savedCurrent = this.current;
|
|
648
|
+
const children: MarkdownNode[] = [];
|
|
649
|
+
let closed = false;
|
|
650
|
+
while (!this.isAtEnd()) {
|
|
651
|
+
const next = this.peek();
|
|
652
|
+
if (next.kind === 'newline') break;
|
|
653
|
+
if (next.kind === 'plus' && ('value' in next && next.value === '++')) {
|
|
654
|
+
this.advance();
|
|
655
|
+
closed = true;
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
658
|
+
const child = this.parseInline({ kind: 'plus', value: '++' });
|
|
659
|
+
if (child) children.push(child);
|
|
660
|
+
else break;
|
|
661
|
+
}
|
|
662
|
+
if (closed) {
|
|
663
|
+
return { type: 'underline', children };
|
|
664
|
+
} else {
|
|
665
|
+
this.current = savedCurrent;
|
|
666
|
+
return { type: 'text', value: '++' };
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// Alignment arrow (-> text <- or -> text ->)
|
|
671
|
+
if (token.kind === 'arrow_right') {
|
|
672
|
+
const savedCurrent = this.current;
|
|
673
|
+
const children: MarkdownNode[] = [];
|
|
674
|
+
let alignType: 'center' | 'right' | null = null;
|
|
675
|
+
while (!this.isAtEnd()) {
|
|
676
|
+
const next = this.peek();
|
|
677
|
+
if (next.kind === 'newline') break;
|
|
678
|
+
if (next.kind === 'arrow_left') {
|
|
679
|
+
this.advance();
|
|
680
|
+
alignType = 'center';
|
|
681
|
+
break;
|
|
682
|
+
}
|
|
683
|
+
if (next.kind === 'arrow_right') {
|
|
684
|
+
this.advance();
|
|
685
|
+
alignType = 'right';
|
|
686
|
+
break;
|
|
687
|
+
}
|
|
688
|
+
const child = this.parseInline();
|
|
689
|
+
if (child) children.push(child);
|
|
690
|
+
else break;
|
|
691
|
+
}
|
|
692
|
+
if (alignType) {
|
|
693
|
+
if (children.length > 0 && children[0].type === 'text') {
|
|
694
|
+
children[0].value = (children[0] as MarkdownText).value.replace(/^\s+/, '');
|
|
695
|
+
}
|
|
696
|
+
if (children.length > 0 && children[children.length - 1].type === 'text') {
|
|
697
|
+
const last = children[children.length - 1] as MarkdownText;
|
|
698
|
+
last.value = last.value.replace(/\s+$/, '');
|
|
699
|
+
}
|
|
700
|
+
return { type: alignType, children };
|
|
701
|
+
} else {
|
|
702
|
+
this.current = savedCurrent;
|
|
703
|
+
return { type: 'text', value: '->' };
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
339
707
|
// Spoiler (||)
|
|
340
708
|
if (token.kind === 'text' && token.value === '||') {
|
|
341
709
|
const savedCurrent = this.current;
|
|
@@ -361,8 +729,9 @@ export class MarkdownParser {
|
|
|
361
729
|
}
|
|
362
730
|
}
|
|
363
731
|
|
|
364
|
-
// Link or
|
|
732
|
+
// Link, Image, or Generic Attribute Span [text]{attributes}
|
|
365
733
|
if (token.kind === 'bracket_open' || token.kind === 'bang') {
|
|
734
|
+
const savedCurrent = this.current;
|
|
366
735
|
const isImage = token.kind === 'bang';
|
|
367
736
|
if (isImage) {
|
|
368
737
|
if (!this.match('bracket_open')) {
|
|
@@ -370,7 +739,6 @@ export class MarkdownParser {
|
|
|
370
739
|
}
|
|
371
740
|
}
|
|
372
741
|
|
|
373
|
-
const savedCurrent = this.current;
|
|
374
742
|
let text = '';
|
|
375
743
|
let closedBracket = false;
|
|
376
744
|
|
|
@@ -380,7 +748,7 @@ export class MarkdownParser {
|
|
|
380
748
|
break;
|
|
381
749
|
}
|
|
382
750
|
const p = this.advance();
|
|
383
|
-
text += (
|
|
751
|
+
text += this.tokenValue(p);
|
|
384
752
|
}
|
|
385
753
|
|
|
386
754
|
if (closedBracket && this.match('paren_open')) {
|
|
@@ -392,7 +760,7 @@ export class MarkdownParser {
|
|
|
392
760
|
break;
|
|
393
761
|
}
|
|
394
762
|
const p = this.advance();
|
|
395
|
-
url += (
|
|
763
|
+
url += this.tokenValue(p);
|
|
396
764
|
}
|
|
397
765
|
|
|
398
766
|
if (closedParen) {
|
|
@@ -403,10 +771,28 @@ export class MarkdownParser {
|
|
|
403
771
|
}
|
|
404
772
|
}
|
|
405
773
|
}
|
|
774
|
+
|
|
775
|
+
// Generic Attribute Span [text]{.underline} or [text]{color="#ff0055"} or [text]{#ff0055}
|
|
776
|
+
if (closedBracket && this.match('brace_open')) {
|
|
777
|
+
let attrString = '';
|
|
778
|
+
let closedBrace = false;
|
|
779
|
+
while (!this.isAtEnd() && !this.check('newline')) {
|
|
780
|
+
if (this.match('brace_close')) {
|
|
781
|
+
closedBrace = true;
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
const p = this.advance();
|
|
785
|
+
attrString += this.tokenValue(p);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (closedBrace) {
|
|
789
|
+
return this.createAttributeSpanNode(text, attrString.trim());
|
|
790
|
+
}
|
|
791
|
+
}
|
|
406
792
|
|
|
407
|
-
// Fallback
|
|
793
|
+
// Fallback: restore cursor to before bracket was consumed
|
|
408
794
|
this.current = savedCurrent;
|
|
409
|
-
return { type: 'text', value: isImage ? '!
|
|
795
|
+
return { type: 'text', value: isImage ? '!' : '[' };
|
|
410
796
|
}
|
|
411
797
|
|
|
412
798
|
return {
|
|
@@ -415,6 +801,308 @@ export class MarkdownParser {
|
|
|
415
801
|
};
|
|
416
802
|
}
|
|
417
803
|
|
|
804
|
+
private createAttributeSpanNode(rawText: string, attrString: string): MarkdownNode {
|
|
805
|
+
const innerTokens = scanMarkdown(rawText);
|
|
806
|
+
const innerDoc = new MarkdownParser(innerTokens).parseInline();
|
|
807
|
+
const children: MarkdownNode[] = innerDoc ? [innerDoc] : [{ type: 'text', value: rawText }];
|
|
808
|
+
|
|
809
|
+
// 1. Shorthand class: .underline
|
|
810
|
+
if (attrString === '.underline' || attrString.includes('.underline')) {
|
|
811
|
+
return { type: 'underline', children };
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// 2. Shorthand hex color: #ff0055 or #f00
|
|
815
|
+
if (/^#[0-9a-fA-F]{3,8}$/.test(attrString)) {
|
|
816
|
+
return { type: 'color', color: attrString, children };
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// 3. Key-value attributes: color="...", size="...", font="..."
|
|
820
|
+
let resultNode: MarkdownNode = { type: 'paragraph', children };
|
|
821
|
+
let wrapped = false;
|
|
822
|
+
|
|
823
|
+
// Match color=... or style="color:..."
|
|
824
|
+
const colorMatch = attrString.match(/color=["']?([^"'\s}]+)["']?/) || attrString.match(/style=["'][^"']*color:\s*([^;"'\s]+)/);
|
|
825
|
+
if (colorMatch) {
|
|
826
|
+
resultNode = { type: 'color', color: colorMatch[1], children };
|
|
827
|
+
wrapped = true;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// Match size=... or style="font-size:..."
|
|
831
|
+
const sizeMatch = attrString.match(/size=["']?([^"'\s}]+)["']?/) || attrString.match(/style=["'][^"']*font-size:\s*([^;"'\s]+)/);
|
|
832
|
+
if (sizeMatch) {
|
|
833
|
+
const currentChildren = wrapped ? [resultNode] : children;
|
|
834
|
+
resultNode = { type: 'font_size', size: sizeMatch[1], children: currentChildren };
|
|
835
|
+
wrapped = true;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// Match font=... or style="font-family:..."
|
|
839
|
+
const fontMatch = attrString.match(/font(?:-family)?=["']?([^"'}]+)["']?/);
|
|
840
|
+
if (fontMatch) {
|
|
841
|
+
const currentChildren = wrapped ? [resultNode] : children;
|
|
842
|
+
resultNode = { type: 'font', font: fontMatch[1].trim(), children: currentChildren };
|
|
843
|
+
wrapped = true;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
if (wrapped) {
|
|
847
|
+
return resultNode;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
return { type: 'text', value: `[${rawText}]{${attrString}}` };
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// --- BBCode Embedding Support ---
|
|
854
|
+
|
|
855
|
+
private peekBBCodeOpenTag(startIndex: number): BBCodeTagInfo | null {
|
|
856
|
+
if (this.tokens[startIndex]?.kind !== 'bracket_open') return null;
|
|
857
|
+
let idx = startIndex + 1;
|
|
858
|
+
let tagContent = '';
|
|
859
|
+
|
|
860
|
+
while (idx < this.tokens.length) {
|
|
861
|
+
const t = this.tokens[idx];
|
|
862
|
+
if (t.kind === 'newline') return null;
|
|
863
|
+
if (t.kind === 'bracket_close') {
|
|
864
|
+
// Evaluate tagContent
|
|
865
|
+
const trimmed = tagContent.trim();
|
|
866
|
+
if (trimmed.startsWith('/')) return null; // closing tag
|
|
867
|
+
|
|
868
|
+
const eqIdx = trimmed.indexOf('=');
|
|
869
|
+
const tagName = (eqIdx >= 0 ? trimmed.slice(0, eqIdx) : trimmed).trim().toLowerCase();
|
|
870
|
+
let attrValue = eqIdx >= 0 ? trimmed.slice(eqIdx + 1).trim() : undefined;
|
|
871
|
+
if (attrValue && ((attrValue.startsWith('"') && attrValue.endsWith('"')) || (attrValue.startsWith("'") && attrValue.endsWith("'")))) {
|
|
872
|
+
attrValue = attrValue.slice(1, -1);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// If immediately followed by (url) or {attributes}, it is a Markdown construct, not BBCode
|
|
876
|
+
if (idx + 1 < this.tokens.length) {
|
|
877
|
+
const nextToken = this.tokens[idx + 1];
|
|
878
|
+
if (nextToken.kind === 'paren_open' || nextToken.kind === 'brace_open') {
|
|
879
|
+
return null;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
if (/^[a-z0-9_-]+$/i.test(tagName)) {
|
|
884
|
+
return { tagName, attrValue, tokenEndIndex: idx };
|
|
885
|
+
}
|
|
886
|
+
return null;
|
|
887
|
+
}
|
|
888
|
+
tagContent += this.tokenValue(t);
|
|
889
|
+
idx++;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
return null;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
private peekBBCodeCloseTag(startIndex: number): { tagName: string; tokenEndIndex: number } | null {
|
|
896
|
+
if (this.tokens[startIndex]?.kind !== 'bracket_open') return null;
|
|
897
|
+
let idx = startIndex + 1;
|
|
898
|
+
let tagContent = '';
|
|
899
|
+
|
|
900
|
+
while (idx < this.tokens.length) {
|
|
901
|
+
const t = this.tokens[idx];
|
|
902
|
+
if (t.kind === 'newline') return null;
|
|
903
|
+
if (t.kind === 'bracket_close') {
|
|
904
|
+
const trimmed = tagContent.trim();
|
|
905
|
+
if (trimmed.startsWith('/')) {
|
|
906
|
+
const tagName = trimmed.slice(1).trim().toLowerCase();
|
|
907
|
+
if (/^[a-z0-9_-]+$/i.test(tagName)) {
|
|
908
|
+
return { tagName, tokenEndIndex: idx };
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
return null;
|
|
912
|
+
}
|
|
913
|
+
tagContent += this.tokenValue(t);
|
|
914
|
+
idx++;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
return null;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
private isMatchingCloseTag(closeTagName: string, openTagName: string): boolean {
|
|
921
|
+
const c = closeTagName.toLowerCase();
|
|
922
|
+
const o = openTagName.toLowerCase();
|
|
923
|
+
if (c === o) return true;
|
|
924
|
+
if ((c === 'centre' || c === 'center') && (o === 'centre' || o === 'center')) return true;
|
|
925
|
+
if ((c === 'u' || c === 'underline') && (o === 'u' || o === 'underline')) return true;
|
|
926
|
+
if ((c === 's' || c === 'strike' || c === 'strikethrough') && (o === 's' || o === 'strike' || o === 'strikethrough')) return true;
|
|
927
|
+
if ((c === 'b' || c === 'bold') && (o === 'b' || o === 'bold')) return true;
|
|
928
|
+
if ((c === 'i' || c === 'italic') && (o === 'i' || o === 'italic')) return true;
|
|
929
|
+
if ((c === 'box' || c === 'boxw') && (o === 'box' || o === 'boxw')) return true;
|
|
930
|
+
return false;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
private parseBBCodeBlock(openTag: BBCodeTagInfo): MarkdownNode {
|
|
934
|
+
const t = openTag.tagName.toLowerCase();
|
|
935
|
+
if (t === 'code') {
|
|
936
|
+
this.current = openTag.tokenEndIndex + 1;
|
|
937
|
+
let raw = '';
|
|
938
|
+
while (!this.isAtEnd()) {
|
|
939
|
+
if (this.check('bracket_open')) {
|
|
940
|
+
const closeTag = this.peekBBCodeCloseTag(this.current);
|
|
941
|
+
if (closeTag && closeTag.tagName.toLowerCase() === 'code') {
|
|
942
|
+
this.current = closeTag.tokenEndIndex + 1;
|
|
943
|
+
this.match('newline');
|
|
944
|
+
break;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
const p = this.advance();
|
|
948
|
+
raw += this.tokenValue(p);
|
|
949
|
+
}
|
|
950
|
+
if (raw.startsWith('\r\n')) raw = raw.slice(2);
|
|
951
|
+
else if (raw.startsWith('\n')) raw = raw.slice(1);
|
|
952
|
+
if (raw.endsWith('\r\n')) raw = raw.slice(0, -2);
|
|
953
|
+
else if (raw.endsWith('\n')) raw = raw.slice(0, -1);
|
|
954
|
+
return { type: 'code_block', lang: '', value: raw };
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
this.current = openTag.tokenEndIndex + 1;
|
|
958
|
+
this.match('newline');
|
|
959
|
+
|
|
960
|
+
const children: MarkdownNode[] = [];
|
|
961
|
+
|
|
962
|
+
while (!this.isAtEnd()) {
|
|
963
|
+
if (this.check('bracket_open')) {
|
|
964
|
+
const closeTag = this.peekBBCodeCloseTag(this.current);
|
|
965
|
+
if (closeTag && this.isMatchingCloseTag(closeTag.tagName, openTag.tagName)) {
|
|
966
|
+
this.current = closeTag.tokenEndIndex + 1;
|
|
967
|
+
this.match('newline');
|
|
968
|
+
break;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
if (this.check('newline')) {
|
|
973
|
+
this.advance();
|
|
974
|
+
while (this.match('newline')) {
|
|
975
|
+
children.push({ type: 'empty_line' });
|
|
976
|
+
}
|
|
977
|
+
} else {
|
|
978
|
+
const block = this.parseBlock();
|
|
979
|
+
if (block) children.push(block);
|
|
980
|
+
else break;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
return this.createBBCodeNode(openTag.tagName, openTag.attrValue, children);
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
private parseBBCodeInline(openTag: BBCodeTagInfo): MarkdownNode {
|
|
988
|
+
const t = openTag.tagName.toLowerCase();
|
|
989
|
+
if (t === 'c') {
|
|
990
|
+
this.current = openTag.tokenEndIndex + 1;
|
|
991
|
+
let raw = '';
|
|
992
|
+
while (!this.isAtEnd()) {
|
|
993
|
+
if (this.check('bracket_open')) {
|
|
994
|
+
const closeTag = this.peekBBCodeCloseTag(this.current);
|
|
995
|
+
if (closeTag && closeTag.tagName.toLowerCase() === 'c') {
|
|
996
|
+
this.current = closeTag.tokenEndIndex + 1;
|
|
997
|
+
break;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
if (this.check('newline')) break;
|
|
1001
|
+
const p = this.advance();
|
|
1002
|
+
raw += this.tokenValue(p);
|
|
1003
|
+
}
|
|
1004
|
+
return { type: 'code_inline', value: raw };
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
if (t === 'raw' || t === 'noparse' || t === 'plain') {
|
|
1008
|
+
this.current = openTag.tokenEndIndex + 1;
|
|
1009
|
+
let raw = '';
|
|
1010
|
+
while (!this.isAtEnd()) {
|
|
1011
|
+
if (this.check('bracket_open')) {
|
|
1012
|
+
const closeTag = this.peekBBCodeCloseTag(this.current);
|
|
1013
|
+
if (closeTag && this.isMatchingCloseTag(closeTag.tagName, openTag.tagName)) {
|
|
1014
|
+
this.current = closeTag.tokenEndIndex + 1;
|
|
1015
|
+
break;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
if (this.check('newline')) {
|
|
1019
|
+
this.advance();
|
|
1020
|
+
raw += '\n';
|
|
1021
|
+
} else {
|
|
1022
|
+
const p = this.advance();
|
|
1023
|
+
raw += this.tokenValue(p);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
return { type: 'bbcode_tag', tagName: t === 'noparse' ? 'raw' : t, attrValue: openTag.attrValue, children: [{ type: 'text', value: raw }] };
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
this.current = openTag.tokenEndIndex + 1;
|
|
1030
|
+
const children: MarkdownNode[] = [];
|
|
1031
|
+
|
|
1032
|
+
while (!this.isAtEnd()) {
|
|
1033
|
+
if (this.check('bracket_open')) {
|
|
1034
|
+
const closeTag = this.peekBBCodeCloseTag(this.current);
|
|
1035
|
+
if (closeTag && this.isMatchingCloseTag(closeTag.tagName, openTag.tagName)) {
|
|
1036
|
+
this.current = closeTag.tokenEndIndex + 1;
|
|
1037
|
+
break;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
if (this.check('newline')) {
|
|
1042
|
+
this.advance();
|
|
1043
|
+
children.push({ type: 'text', value: '\n' });
|
|
1044
|
+
} else {
|
|
1045
|
+
const child = this.parseInline();
|
|
1046
|
+
if (child) children.push(child);
|
|
1047
|
+
else break;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
return this.createBBCodeNode(openTag.tagName, openTag.attrValue, children);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
private createBBCodeNode(tagName: string, attrValue: string | undefined, children: MarkdownNode[]): MarkdownNode {
|
|
1055
|
+
const t = tagName.toLowerCase();
|
|
1056
|
+
switch (t) {
|
|
1057
|
+
case 'b':
|
|
1058
|
+
case 'bold':
|
|
1059
|
+
return { type: 'strong', children };
|
|
1060
|
+
case 'i':
|
|
1061
|
+
case 'italic':
|
|
1062
|
+
return { type: 'emphasis', children };
|
|
1063
|
+
case 'u':
|
|
1064
|
+
case 'underline':
|
|
1065
|
+
return { type: 'underline', children };
|
|
1066
|
+
case 's':
|
|
1067
|
+
case 'strike':
|
|
1068
|
+
case 'strikethrough':
|
|
1069
|
+
return { type: 'strikethrough', children };
|
|
1070
|
+
case 'c':
|
|
1071
|
+
return { type: 'code_inline', value: children.map(c => ('value' in c ? (c as any).value : '')).join('') };
|
|
1072
|
+
case 'color':
|
|
1073
|
+
case 'colour':
|
|
1074
|
+
return { type: 'color', color: attrValue || '#ffffff', children };
|
|
1075
|
+
case 'size':
|
|
1076
|
+
return { type: 'font_size', size: attrValue || '100', children };
|
|
1077
|
+
case 'font':
|
|
1078
|
+
return { type: 'font', font: attrValue || 'sans-serif', children };
|
|
1079
|
+
case 'centre':
|
|
1080
|
+
case 'center':
|
|
1081
|
+
return { type: 'center', children };
|
|
1082
|
+
case 'right':
|
|
1083
|
+
return { type: 'right', children };
|
|
1084
|
+
case 'left':
|
|
1085
|
+
return { type: 'left', children };
|
|
1086
|
+
case 'align':
|
|
1087
|
+
return { type: (attrValue === 'center' || attrValue === 'right' || attrValue === 'left' ? attrValue : 'center') as any, children };
|
|
1088
|
+
case 'box':
|
|
1089
|
+
case 'boxw':
|
|
1090
|
+
return { type: 'box', title: attrValue || 'Box', rawTitle: attrValue, children };
|
|
1091
|
+
case 'spoilerbox':
|
|
1092
|
+
return { type: 'spoilerbox', title: attrValue || 'Spoiler', rawTitle: attrValue, children };
|
|
1093
|
+
case 'spoiler':
|
|
1094
|
+
return { type: 'spoiler', children };
|
|
1095
|
+
case 'quote':
|
|
1096
|
+
return { type: 'blockquote', source: attrValue, children };
|
|
1097
|
+
case 'notice':
|
|
1098
|
+
return { type: 'notice', color: attrValue, children };
|
|
1099
|
+
case 'wnotice':
|
|
1100
|
+
return { type: 'wnotice', color: attrValue, children };
|
|
1101
|
+
default:
|
|
1102
|
+
return { type: 'bbcode_tag', tagName: t, attrValue, children };
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
|
|
418
1106
|
// --- Helpers ---
|
|
419
1107
|
|
|
420
1108
|
private peek(): MarkdownToken {
|
|
@@ -445,7 +1133,6 @@ export class MarkdownParser {
|
|
|
445
1133
|
return true;
|
|
446
1134
|
}
|
|
447
1135
|
return false;
|
|
448
|
-
|
|
449
1136
|
}
|
|
450
1137
|
}
|
|
451
1138
|
|