@miliastry/quasar 1.0.6 → 1.2.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.
Files changed (278) hide show
  1. package/README.md +1 -1
  2. package/dist/Visuals/lyne.css +374 -9
  3. package/dist/Visuals/osu.css +393 -11
  4. package/dist/index.js +10470 -3336
  5. package/dist/index.mjs +10336 -3337
  6. package/dist/types/Analysis/Contracts/AnalysisReport.d.ts +22 -0
  7. package/dist/types/Analysis/Contracts/Contribution.d.ts +68 -0
  8. package/dist/types/Analysis/Contracts/Pass.d.ts +59 -0
  9. package/dist/types/Analysis/Contracts/PipelineContext.d.ts +34 -0
  10. package/dist/types/Analysis/Passes/Analysis/ColorUsageAnalyzer.d.ts +56 -0
  11. package/dist/types/Analysis/Passes/Analysis/GradientAnalyzer.d.ts +111 -0
  12. package/dist/types/Analysis/Passes/Analysis/MergeableColorAnalyzer.d.ts +26 -0
  13. package/dist/types/Analysis/Passes/Analysis/RainbowAnalyzer.d.ts +47 -0
  14. package/dist/types/Analysis/Passes/Analysis/SymbolAnalyzer.d.ts +89 -0
  15. package/dist/types/Analysis/Passes/Analysis/WaveAnalyzer.d.ts +44 -0
  16. package/dist/types/Analysis/Passes/Analysis/__tests__/GradientAnalyzer.test.d.ts +4 -0
  17. package/dist/types/Analysis/Passes/Analysis/__tests__/MergeableColorAnalyzer.test.d.ts +4 -0
  18. package/dist/types/Analysis/Passes/Analysis/__tests__/RainbowAnalyzer.test.d.ts +4 -0
  19. package/dist/types/Analysis/Passes/Analysis/__tests__/WaveAnalyzer.test.d.ts +4 -0
  20. package/dist/types/Analysis/Passes/Decision/DefaultDecision.d.ts +30 -0
  21. package/dist/types/Analysis/Passes/Decision/PaletteRemapDecision.d.ts +169 -0
  22. package/dist/types/Analysis/Passes/Decision/__tests__/DefaultDecision.test.d.ts +4 -0
  23. package/dist/types/Analysis/Passes/Transform/CollapseGradientTransform.d.ts +37 -0
  24. package/dist/types/Analysis/Passes/Transform/MergeColorsTransform.d.ts +28 -0
  25. package/dist/types/Analysis/Passes/Transform/RainbowCollapseTransform.d.ts +19 -0
  26. package/dist/types/Analysis/Passes/Transform/WaveCollapseTransform.d.ts +20 -0
  27. package/dist/types/Analysis/Passes/Utility/CharacterCountAnalyzer.d.ts +19 -0
  28. package/dist/types/Analysis/Pipeline/Pipeline.d.ts +69 -0
  29. package/dist/types/Analysis/Pipeline/PipelineBuilder.d.ts +31 -0
  30. package/dist/types/Analysis/Pipeline/PipelineStage.d.ts +17 -0
  31. package/dist/types/Analysis/Utils/color-utils.d.ts +60 -0
  32. package/dist/types/Analysis/__tests__/Integration.test.d.ts +8 -0
  33. package/dist/types/Analysis/__tests__/PaletteRemap.test.d.ts +14 -0
  34. package/dist/types/Analysis/__tests__/Pipeline.test.d.ts +10 -0
  35. package/dist/types/Analysis/__tests__/RoundTrip.test.d.ts +30 -0
  36. package/dist/types/Analysis/__tests__/SymbolAnalyzer.test.d.ts +11 -0
  37. package/dist/types/Analysis/index.d.ts +40 -0
  38. package/dist/types/BBCode/BBCodeDocumentModel.d.ts +102 -0
  39. package/dist/types/BBCode/BBCodeToGreenNode.d.ts +111 -0
  40. package/dist/types/BBCode/Parser.d.ts +78 -0
  41. package/dist/types/BBCode/index.d.ts +4 -0
  42. package/dist/types/Collab/positions.d.ts +55 -0
  43. package/dist/types/Commands/Command.d.ts +37 -0
  44. package/dist/types/Commands/CommandRegistry.d.ts +48 -0
  45. package/dist/types/Commands/DeleteNode.d.ts +7 -0
  46. package/dist/types/Commands/InsertText.d.ts +8 -0
  47. package/dist/types/Commands/SplitMerge.d.ts +9 -0
  48. package/dist/types/Commands/WrapInTag.d.ts +8 -0
  49. package/dist/types/Commands/index.d.ts +6 -0
  50. package/dist/types/Diff/TreeDiffer.d.ts +52 -0
  51. package/dist/types/Diff/__tests__/TreeDiffer.test.d.ts +1 -0
  52. package/dist/types/Diff/index.d.ts +2 -0
  53. package/dist/types/Edits/EditPlan.d.ts +144 -0
  54. package/dist/types/Edits/Optimizer.d.ts +78 -0
  55. package/dist/types/Edits/Rules/Rule.d.ts +91 -0
  56. package/dist/types/Edits/Rules/dropEmptyTags.d.ts +40 -0
  57. package/dist/types/Edits/Rules/dropRedundantNesting.d.ts +59 -0
  58. package/dist/types/Edits/Rules/index.d.ts +9 -0
  59. package/dist/types/Edits/Rules/mergeAdjacent.d.ts +93 -0
  60. package/dist/types/Edits/Rules/reorderWrappers.d.ts +42 -0
  61. package/dist/types/Edits/Rules/shortenHex.d.ts +30 -0
  62. package/dist/types/Edits/Rules/tagValue.d.ts +56 -0
  63. package/dist/types/Edits/Rules/unwrapInvisibleColor.d.ts +47 -0
  64. package/dist/types/Edits/applyEdits.d.ts +36 -0
  65. package/dist/types/Edits/index.d.ts +6 -0
  66. package/dist/types/Events/EventBus.d.ts +103 -0
  67. package/dist/types/Events/index.d.ts +2 -0
  68. package/dist/types/Formatter/Formatter.d.ts +32 -0
  69. package/dist/types/Formatter/index.d.ts +2 -0
  70. package/dist/types/HTML/HTMLDocumentModel.d.ts +13 -0
  71. package/dist/types/HTML/HTMLToGreenNode.d.ts +4 -0
  72. package/dist/types/Incremental/BracketIndex.d.ts +101 -0
  73. package/dist/types/Incremental/ChangeTracker.d.ts +69 -0
  74. package/dist/types/Incremental/IncrementalParser.d.ts +246 -0
  75. package/dist/types/Incremental/__tests__/BracketIndex.test.d.ts +1 -0
  76. package/dist/types/Incremental/__tests__/IncrementalParser.test.d.ts +1 -0
  77. package/dist/types/Incremental/index.d.ts +4 -0
  78. package/dist/types/Lexer/BBCodeLexer.d.ts +74 -0
  79. package/dist/types/Lexer/Lexer.d.ts +54 -0
  80. package/dist/types/Lexer/index.d.ts +4 -0
  81. package/dist/types/Linter/Linter.d.ts +58 -0
  82. package/dist/types/Linter/index.d.ts +2 -0
  83. package/dist/types/Markdown/MarkdownAST.d.ts +122 -0
  84. package/dist/types/Markdown/MarkdownDocumentModel.d.ts +15 -0
  85. package/dist/types/Markdown/MarkdownLexer.d.ts +155 -0
  86. package/dist/types/Markdown/MarkdownParser.d.ts +42 -0
  87. package/dist/types/Markdown/MarkdownToGreenNode.d.ts +6 -0
  88. package/dist/types/MilHibri/MilHibriDocumentModel.d.ts +21 -0
  89. package/dist/types/Model/DocumentModel.d.ts +318 -0
  90. package/dist/types/Model/NodeFactory.d.ts +29 -0
  91. package/dist/types/Model/TagRegistry.d.ts +165 -0
  92. package/dist/types/Model/index.d.ts +5 -0
  93. package/dist/types/Plugins/PluginAPI.d.ts +44 -0
  94. package/dist/types/Plugins/PluginRegistry.d.ts +81 -0
  95. package/dist/types/Plugins/index.d.ts +3 -0
  96. package/dist/types/Queries/QueryEngine.d.ts +32 -0
  97. package/dist/types/Queries/index.d.ts +1 -0
  98. package/dist/types/Reconciler/SurgicalReconciler.d.ts +23 -0
  99. package/dist/types/RenderPipeline/RenderPipeline.d.ts +46 -0
  100. package/dist/types/RenderPipeline/RenderTree.d.ts +55 -0
  101. package/dist/types/RenderPipeline/index.d.ts +4 -0
  102. package/dist/types/Repair/NestingRepair.d.ts +48 -0
  103. package/dist/types/Repair/index.d.ts +1 -0
  104. package/dist/types/Semantic/SemanticAnalyzer.d.ts +321 -0
  105. package/dist/types/Semantic/index.d.ts +2 -0
  106. package/dist/types/Symbols/SymbolTable.d.ts +47 -0
  107. package/dist/types/Symbols/index.d.ts +1 -0
  108. package/dist/types/Syntax/GreenNode.d.ts +154 -0
  109. package/dist/types/Syntax/GreenNodePool.d.ts +134 -0
  110. package/dist/types/Syntax/NodeMatcher.d.ts +75 -0
  111. package/dist/types/Syntax/RedNode.d.ts +280 -0
  112. package/dist/types/Syntax/RedNodeStore.d.ts +116 -0
  113. package/dist/types/Syntax/TreeBuilder.d.ts +54 -0
  114. package/dist/types/Syntax/__tests__/GreenNode.test.d.ts +1 -0
  115. package/dist/types/Syntax/__tests__/RedNode.test.d.ts +1 -0
  116. package/dist/types/Syntax/__tests__/RedNodeStore.test.d.ts +1 -0
  117. package/dist/types/Syntax/greenEdit.d.ts +64 -0
  118. package/dist/types/Syntax/hash.d.ts +17 -0
  119. package/dist/types/Syntax/index.d.ts +8 -0
  120. package/dist/types/Syntax/nodeAttr.d.ts +63 -0
  121. package/dist/types/Syntax/partition.d.ts +67 -0
  122. package/dist/types/Syntax/preserveNodeIds.d.ts +47 -0
  123. package/dist/types/Tokens/TokenResolver.d.ts +25 -0
  124. package/dist/types/Tokens/index.d.ts +1 -0
  125. package/dist/types/Transactions/Transaction.d.ts +36 -0
  126. package/dist/types/Transactions/UndoManager.d.ts +63 -0
  127. package/dist/types/Transactions/index.d.ts +3 -0
  128. package/dist/types/Transformers/ASTOptimizer.d.ts +34 -0
  129. package/dist/types/Transformers/GradientTransformer.d.ts +18 -0
  130. package/dist/types/Transformers/GrowTransformer.d.ts +16 -0
  131. package/dist/types/Transformers/RainbowTransformer.d.ts +17 -0
  132. package/dist/types/Transformers/SineWaveTransformer.d.ts +17 -0
  133. package/dist/types/Transformers/Transformer.d.ts +17 -0
  134. package/dist/types/Types/core.d.ts +104 -0
  135. package/dist/types/Types/diagnostics.d.ts +85 -0
  136. package/dist/types/Types/index.d.ts +11 -0
  137. package/dist/types/Types/operations.d.ts +109 -0
  138. package/dist/types/Types/queries.d.ts +67 -0
  139. package/dist/types/Types/symbols.d.ts +42 -0
  140. package/dist/types/Types/tokens.d.ts +75 -0
  141. package/dist/types/Utils/BBCodeGenerator.d.ts +21 -0
  142. package/dist/types/Utils/ColorMath.d.ts +92 -0
  143. package/dist/types/Utils/EffectMath.d.ts +643 -0
  144. package/dist/types/Utils/TagVocabulary.d.ts +104 -0
  145. package/dist/types/Utils/TagVocabulary.test.d.ts +1 -0
  146. package/dist/types/Utils/color.d.ts +29 -0
  147. package/dist/types/Utils/dom-to-svg.d.ts +28 -0
  148. package/dist/types/Utils/dom-to-svg.test.d.ts +1 -0
  149. package/dist/types/Utils/treeTransformers.d.ts +160 -0
  150. package/dist/types/Visitors/BBBlocksExporter.d.ts +27 -0
  151. package/dist/types/Visitors/BBCodeExporter.d.ts +90 -0
  152. package/dist/types/Visitors/BlockPatcher.d.ts +91 -0
  153. package/dist/types/Visitors/DOMMorpher.d.ts +19 -0
  154. package/dist/types/Visitors/HTMLRenderer.d.ts +492 -0
  155. package/dist/types/Visitors/JSONExporter.d.ts +42 -0
  156. package/dist/types/Visitors/MarkdownExporter.d.ts +28 -0
  157. package/dist/types/Visitors/SVGRenderer.d.ts +16 -0
  158. package/dist/types/Visitors/TiptapExporter.d.ts +40 -0
  159. package/dist/types/Visitors/Visitor.d.ts +35 -0
  160. package/dist/types/Visitors/index.d.ts +9 -0
  161. package/dist/types/Visuals/BoxDrawer.d.ts +54 -0
  162. package/dist/types/Visuals/LyneAudio.d.ts +8 -0
  163. package/dist/types/Visuals/index.d.ts +39 -0
  164. package/dist/types/index.d.ts +155 -0
  165. package/package.json +14 -10
  166. package/src/Analysis/Passes/Analysis/ColorUsageAnalyzer.ts +103 -0
  167. package/src/Analysis/Passes/Analysis/GradientAnalyzer.ts +294 -20
  168. package/src/Analysis/Passes/Analysis/SymbolAnalyzer.ts +257 -0
  169. package/src/Analysis/Passes/Decision/PaletteRemapDecision.ts +425 -0
  170. package/src/Analysis/Pipeline/Pipeline.ts +15 -2
  171. package/src/Analysis/index.ts +8 -2
  172. package/src/BBCode/BBCodeDocumentModel.ts +39 -1
  173. package/src/BBCode/BBCodeToGreenNode.ts +18 -12
  174. package/src/BBCode/Parser.ts +38 -1
  175. package/src/Edits/EditPlan.ts +290 -0
  176. package/src/Edits/Optimizer.ts +185 -0
  177. package/src/Edits/Rules/Rule.ts +145 -0
  178. package/src/Edits/Rules/dropEmptyTags.ts +110 -0
  179. package/src/Edits/Rules/dropRedundantNesting.ts +120 -0
  180. package/src/Edits/Rules/index.ts +28 -0
  181. package/src/Edits/Rules/mergeAdjacent.ts +224 -0
  182. package/src/Edits/Rules/reorderWrappers.ts +154 -0
  183. package/src/Edits/Rules/shortenHex.ts +79 -0
  184. package/src/Edits/Rules/tagValue.ts +105 -0
  185. package/src/Edits/Rules/unwrapInvisibleColor.ts +93 -0
  186. package/src/Edits/applyEdits.ts +54 -0
  187. package/src/Edits/index.ts +22 -0
  188. package/src/Events/EventBus.ts +18 -0
  189. package/src/HTML/HTMLToGreenNode.ts +193 -37
  190. package/src/Incremental/BracketIndex.ts +249 -0
  191. package/src/Incremental/IncrementalParser.ts +464 -41
  192. package/src/Lexer/BBCodeLexer.ts +25 -3
  193. package/src/Lexer/index.ts +1 -1
  194. package/src/Markdown/MarkdownAST.ts +65 -2
  195. package/src/Markdown/MarkdownLexer.ts +69 -14
  196. package/src/Markdown/MarkdownParser.ts +749 -62
  197. package/src/Markdown/MarkdownToGreenNode.ts +148 -27
  198. package/src/MilHibri/MilHibriDocumentModel.ts +56 -0
  199. package/src/Model/DocumentModel.ts +138 -5
  200. package/src/Model/TagRegistry.ts +129 -134
  201. package/src/Reconciler/SurgicalReconciler.ts +460 -0
  202. package/src/Repair/NestingRepair.ts +116 -0
  203. package/src/Repair/index.ts +1 -0
  204. package/src/Semantic/SemanticAnalyzer.ts +1845 -65
  205. package/src/Syntax/GreenNodePool.ts +12 -12
  206. package/src/Syntax/RedNode.ts +17 -3
  207. package/src/Syntax/RedNodeStore.ts +9 -1
  208. package/src/Syntax/nodeAttr.ts +172 -0
  209. package/src/Tokens/TokenResolver.ts +55 -0
  210. package/src/Tokens/index.ts +1 -0
  211. package/src/Types/core.ts +15 -0
  212. package/src/Utils/ColorMath.ts +276 -55
  213. package/src/Utils/EffectMath.ts +2462 -0
  214. package/src/Utils/TagVocabulary.ts +141 -0
  215. package/src/Utils/dom-to-svg.ts +28 -12
  216. package/src/Visitors/BBBlocksExporter.ts +15 -1
  217. package/src/Visitors/BBCodeExporter.ts +365 -32
  218. package/src/Visitors/BlockPatcher.ts +123 -22
  219. package/src/Visitors/DOMMorpher.ts +44 -28
  220. package/src/Visitors/HTMLRenderer.ts +771 -205
  221. package/src/Visitors/MarkdownExporter.ts +98 -41
  222. package/src/Visitors/TiptapExporter.ts +1 -0
  223. package/src/Visitors/index.ts +1 -1
  224. package/src/Visuals/BoxDrawer.ts +56 -10
  225. package/src/Visuals/LyneAudio.ts +346 -0
  226. package/src/Visuals/index.ts +8 -4
  227. package/src/Visuals/lyne.css +374 -9
  228. package/src/Visuals/osu.css +393 -11
  229. package/src/index.ts +178 -8
  230. package/dist/index.d.mts +0 -4612
  231. package/dist/index.d.ts +0 -4612
  232. package/src/Analysis/Passes/Analysis/__tests__/GradientAnalyzer.test.ts +0 -135
  233. package/src/Analysis/Passes/Analysis/__tests__/MergeableColorAnalyzer.test.ts +0 -84
  234. package/src/Analysis/Passes/Analysis/__tests__/RainbowAnalyzer.test.ts +0 -99
  235. package/src/Analysis/Passes/Analysis/__tests__/WaveAnalyzer.test.ts +0 -119
  236. package/src/Analysis/Passes/Decision/__tests__/DefaultDecision.test.ts +0 -179
  237. package/src/Analysis/__tests__/Integration.test.ts +0 -162
  238. package/src/Analysis/__tests__/Pipeline.test.ts +0 -133
  239. package/src/Diff/__tests__/TreeDiffer.test.ts +0 -65
  240. package/src/Incremental/__tests__/IncrementalParser.test.ts +0 -164
  241. package/src/Syntax/__tests__/GreenNode.test.ts +0 -33
  242. package/src/Syntax/__tests__/RedNode.test.ts +0 -81
  243. package/src/Syntax/__tests__/RedNodeStore.test.ts +0 -104
  244. package/src/Tests/ASTOptimizerIdempotence.test.ts +0 -77
  245. package/src/Tests/BlockPatcher.test.ts +0 -437
  246. package/src/Tests/BlockPatcherWindowed.test.ts +0 -364
  247. package/src/Tests/BoxDrawer.test.ts +0 -217
  248. package/src/Tests/BoxRichTitle.test.ts +0 -105
  249. package/src/Tests/Chars500kBenchmark.test.ts +0 -151
  250. package/src/Tests/Chars500kEdits.test.ts +0 -321
  251. package/src/Tests/CollabPositions.test.ts +0 -146
  252. package/src/Tests/CompilerPathProfiling.test.ts +0 -186
  253. package/src/Tests/DOMMorpher.test.ts +0 -142
  254. package/src/Tests/DomPatchPerf.test.ts +0 -60
  255. package/src/Tests/EffectSegments.snapshot.json +0 -616
  256. package/src/Tests/EffectSegments.test.ts +0 -68
  257. package/src/Tests/FindNodeAtOffset.test.ts +0 -65
  258. package/src/Tests/Fuzzer.test.ts +0 -166
  259. package/src/Tests/GreenNodePool.test.ts +0 -153
  260. package/src/Tests/Lexer.test.ts +0 -238
  261. package/src/Tests/LyneMode.test.ts +0 -291
  262. package/src/Tests/ModelCoherence.test.ts +0 -180
  263. package/src/Tests/Partition.test.ts +0 -238
  264. package/src/Tests/PluginTags.test.ts +0 -150
  265. package/src/Tests/ProblematicSection.test.ts +0 -46
  266. package/src/Tests/ProblematicSectionHTML.test.ts +0 -58
  267. package/src/Tests/RedReuse.test.ts +0 -134
  268. package/src/Tests/ReproDelete20k.test.ts +0 -62
  269. package/src/Tests/SemanticValidators.test.ts +0 -136
  270. package/src/Tests/StableNodeIds.test.ts +0 -210
  271. package/src/Tests/StudioColorBloat.test.ts +0 -25
  272. package/src/Tests/StudioDebugText.test.ts +0 -27
  273. package/src/Tests/StudioTrailingChar.test.ts +0 -25
  274. package/src/Tests/StudioValidText.test.ts +0 -25
  275. package/src/Tests/UrlImgBug.test.ts +0 -23
  276. package/src/Tests/VisualBuilderFidelity.test.ts +0 -105
  277. package/src/Tests/referenceDocument.ts +0 -119
  278. package/src/Utils/dom-to-svg.test.ts +0 -86
@@ -0,0 +1,492 @@
1
+ /**
2
+ * DocumentEngine — HTMLRenderer
3
+ *
4
+ * Renders the Document Model to HTML for preview.
5
+ * This is the main renderer for the visual BBCode preview.
6
+ *
7
+ * Uses the TagRegistry for custom rendering.
8
+ * Plugins can register custom renderers for preview components.
9
+ */
10
+ import { RedNode } from '../Syntax/RedNode';
11
+ import { Visitor } from './Visitor';
12
+ import type { TagRegistry } from '../Model/TagRegistry';
13
+ import type { BBCodeDialect } from '../BBCode/BBCodeToGreenNode';
14
+ import { type TokenResolverFn, type TokenSource } from '../Tokens';
15
+ export interface HTMLRendererOptions {
16
+ /**
17
+ * Replicate osu! forum BBCode spacing quirks.
18
+ *
19
+ * NO LO LEE NADIE desde que el motor implementa las reglas reales de
20
+ * consumo de saltos (ver {@link HTMLRenderer.NEWLINE_RULES}). Sólo gobernaba
21
+ * dos heurísticas sobre `[code]` que resultaron ser falsas — osu no se come
22
+ * ningún salto ANTES de un bloque, y el resto de las reglas nunca estuvo
23
+ * condicionado. El espaciado ya no es opcional: Miliastry es "osu con
24
+ * esteroides" y rompe líneas igual, así que apagarlo no tendría a qué
25
+ * volver.
26
+ *
27
+ * Se conserva porque es API pública del renderer y quitarlo rompería a quien
28
+ * lo pase. Es candidato a borrarse en la próxima ruptura de versión.
29
+ *
30
+ * @deprecated Sin efecto. El espaciado de osu es ahora incondicional.
31
+ */
32
+ osuBehaviour?: boolean;
33
+ /** Registry for resolving custom tags */
34
+ registry?: TagRegistry;
35
+ /** BBCode dialect to render for ('osu' | 'miliastry' | 'lyne') */
36
+ dialect?: BBCodeDialect;
37
+ /** Visual theme for markup classes ('osu' | 'lyne' | 'miliastry') */
38
+ theme?: 'osu' | 'lyne' | 'miliastry';
39
+ /** Safe media proxy callback to rewrite image and media URLs */
40
+ mediaProxy?: (url: string) => string;
41
+ /** Resolver for entity links like profile, guild, map */
42
+ entityLinkResolver?: (kind: string, value: string) => {
43
+ href: string;
44
+ external?: boolean;
45
+ } | null;
46
+ /**
47
+ * Forum-style `@mention` linkifier. Called with the bare name (no `@`);
48
+ * return null to leave the mention as plain text. Used by forum hosts that
49
+ * linkify `@username` to a profile route.
50
+ */
51
+ mentionResolver?: (name: string) => {
52
+ href: string;
53
+ external?: boolean;
54
+ } | null;
55
+ /**
56
+ * Timestamp chip linkifier (`1:23`, `01:23.456`, `01:23:456`). Called with
57
+ * the millisecond offset and the display label; return null to leave the
58
+ * timestamp as plain text. Used by map hosts that deep-link into an editor.
59
+ */
60
+ timestampResolver?: (ms: number, label: string) => {
61
+ href: string;
62
+ external?: boolean;
63
+ } | null;
64
+ /** Design tokens or resolver function */
65
+ tokens?: TokenSource;
66
+ }
67
+ export declare class HTMLRenderer extends Visitor<string> {
68
+ private tokenResolver?;
69
+ private options;
70
+ constructor(options?: HTMLRendererOptions);
71
+ setTokens(tokens?: TokenSource): void;
72
+ getTokenResolver(): TokenResolverFn | undefined;
73
+ private readonly BLOCK_TAGS;
74
+ private readonly INLINE_TAGS;
75
+ /**
76
+ * The ` data-node-id="…"` attribute, or `''` for nodes nobody looks up.
77
+ *
78
+ * ─── Why this is not emitted on everything ──────────────────────────────
79
+ *
80
+ * It used to be, carrying `node.id` — a process-global counter minted per
81
+ * RedNode, so a reparse renames every node in the document. Measured on a
82
+ * one-character edit: zero ids survive.
83
+ *
84
+ * `DOMMorpher` already noted that this rules the ids out as morph KEYS. The
85
+ * larger cost is that it also breaks the morpher's prefix/suffix trim, which
86
+ * uses `isEqualNode` — a comparison that includes attributes. An attribute
87
+ * that always differs makes every element compare unequal, so the trim never
88
+ * fires and the morpher walks the whole document. Measured on one keystroke
89
+ * in the reference document: **1665 `setAttribute` calls across 1725
90
+ * elements**, to write new numbers meaning the same thing, when the actual
91
+ * change was 10 insertions and 9 removals.
92
+ *
93
+ * ─── Why BLOCKS, and not stable ids ─────────────────────────────────────
94
+ *
95
+ * Making the id stable was the obvious repair and it does not work. Keying on
96
+ * the node's SPAN was measured: an edit at the END drops the writes to 6, but
97
+ * an insertion shifts every offset after it, so an edit near the START still
98
+ * cost 1637 — and spans are longer strings than `nN`, so the emitted HTML grew
99
+ * 10%. Stability under insertion is not something a position can have.
100
+ *
101
+ * The real observation is that no consumer ever wanted these on inline nodes.
102
+ * Both readers in the app are block-level: `usePreviewClick` walks up with
103
+ * `closest('[data-node-id]')` for *block selection*, and `useBlockHighlight`
104
+ * highlights and scrolls to a *block*. Emitting ids on the per-character
105
+ * spans of a gradient did not just cost — it made `closest()` stop at a
106
+ * character instead of the block the click meant.
107
+ *
108
+ * So the attribute goes where it is read. Inline nodes carry no id, compare
109
+ * equal, and let `isEqualNode` skip their subtrees natively; the handful of
110
+ * block containers that do carry one are few enough that their churn is
111
+ * noise.
112
+ */
113
+ /**
114
+ * Text leaves emit their content directly, with no wrapper element.
115
+ *
116
+ * They used to come wrapped in `<span class="bb-text">` — one extra DOM
117
+ * element per text leaf, which is HALF the preview's elements (measured:
118
+ * 1726 → 851 on a 19.6 KB post, 17251 → 8510 on a 196 KB one). The class
119
+ * earned none of it: it has no CSS rule anywhere in the repo, carries no
120
+ * `data-node-id` (text is not an id-bearing kind, so click mapping and
121
+ * highlighting never looked at it), and every style a text leaf can have
122
+ * still emits its own `<span style="…">` below.
123
+ *
124
+ * A/B in a production build, steady state: flush 41.4 → 39.7 ms at 19.6 KB
125
+ * and 55.4 → 46.4 ms at 196 KB, plus ~27% less HTML to serialize and parse.
126
+ */
127
+ private textWrap;
128
+ /**
129
+ * A qué elementos se les pone `data-node-id`.
130
+ *
131
+ * `'all'` (por defecto) — a todos. Es lo que permite que un clic en
132
+ * CUALQUIER punto del preview señale ese nodo exacto en el editor: en un
133
+ * degradado cada carácter es su propio nodo, y sin id no hay nada a lo que
134
+ * `closest()` pueda agarrarse.
135
+ *
136
+ * Esto estuvo desactivado por una buena razón que ya no aplica. Los ids se
137
+ * regeneraban en cada parseo, así que el atributo cambiaba en TODOS los
138
+ * elementos por pulsación: `isEqualNode` no casaba nunca, el morpher no
139
+ * podía saltarse ningún subárbol y se medían 1665 `setAttribute` sobre 1725
140
+ * elementos para escribir números nuevos que significaban lo mismo. Con la
141
+ * identidad estable entre reparseos eso desapareció: un nodo que no cambia
142
+ * conserva su id, su HTML es idéntico y el camino rápido del morpher sigue
143
+ * funcionando.
144
+ *
145
+ * El coste que queda es el tamaño: ~10 bytes por elemento, que hay que
146
+ * serializar y parsear. Medido con A/B en la misma sesión sobre el post con
147
+ * degradados (19,6 KB, 852 elementos):
148
+ *
149
+ * solo bloques (9 con id): latencia 48 ms · p95 72 ms
150
+ * todos (808 con id): latencia 56 ms · p95 104 ms
151
+ *
152
+ * Se paga a propósito: la precisión del clic es una función que se pidió, y
153
+ * 56 ms sigue holgadamente dentro de lo que se percibe como inmediato.
154
+ * `'blocks'` queda disponible para quien priorice la latencia, y `'none'`
155
+ * para los consumidores de solo lectura (foros, render estático): sin
156
+ * `data-node-id` en absoluto, el HTML es más pequeño y no hay nada que
157
+ * mantenga vivos los nodos del árbol.
158
+ */
159
+ static idMode: 'blocks' | 'all' | 'none';
160
+ /**
161
+ * Nesting depth of `[tables]` while rendering. `[col]`/`[row]`/`[th]` are
162
+ * table cells only INSIDE a `[tables]`; orphaned ones (e.g. `[col]` inside
163
+ * `[columns]`, or stray cells) render as plain inline spans — the same rule
164
+ * LYNE's forum renderer applies.
165
+ */
166
+ private tableDepth;
167
+ /**
168
+ * ¿Estamos emitiendo el vocabulario de clases de osu!?
169
+ *
170
+ * osu estiliza box, spoilerbox, notice, imagemap, youtube, los alineados y
171
+ * los perfiles POR NOMBRE DE CLASE, no por estilo inline. Sobre una userpage
172
+ * real el HTML de Quasar salía sin estilo porque emitía su propio
173
+ * vocabulario (`<details>`, `.notice`, `.imagemap-container`…). Bajo
174
+ * `dialect: 'osu'` se emiten las clases y la estructura de osu; el resto de
175
+ * dialectos conserva la suya, que es la que sus hojas de estilo esperan.
176
+ */
177
+ private isOsu;
178
+ /** osu recorta los saltos pegados a la apertura y al cierre de box/notice. */
179
+ private static trimOsuEdges;
180
+ private idAttr;
181
+ /**
182
+ * Kinds that carry `data-node-id`.
183
+ *
184
+ * The block containers a user can select or be scrolled to, plus the media
185
+ * nodes, which are atomic and clickable in their own right. Deliberately NOT
186
+ * `text` or any inline formatting kind — see {@link idAttr}.
187
+ */
188
+ private static readonly ID_BEARING_KINDS;
189
+ visit(node: RedNode): string;
190
+ /**
191
+ * Ancho de cada tabla abierta, apilado igual que `tableDepth`.
192
+ *
193
+ * `getTableContext` corre una vez POR CELDA y recorría todas las filas de
194
+ * la tabla en cada llamada, con un `.filter()` —y su array— por fila: una
195
+ * tabla de 20×20 hacía 8.000 arrays intermedios por renderizado para
196
+ * llegar 400 veces al mismo número. Ahora se calcula una vez al entrar en
197
+ * la tabla. Va en una pila y no en un caché por nodo porque los hijos de un
198
+ * `RedNode` SON mutables (`appendChild`/`removeChild`), así que la
199
+ * identidad del nodo no garantiza que su estructura siga siendo la misma
200
+ * entre renderizados.
201
+ */
202
+ private maxColsStack;
203
+ render(root: RedNode): string;
204
+ /**
205
+ * Render every child and concatenate.
206
+ *
207
+ * Replaces the `children.map(c => this.renderNode(c)).join('')` idiom, which
208
+ * was repeated at 16 call sites and allocated a closure plus an intermediate
209
+ * array of N strings at every level of the tree. Appending to one string lets
210
+ * the engine use its rope representation instead.
211
+ */
212
+ /**
213
+ * Render the direct children of `node` to HTML (no wrapper element).
214
+ *
215
+ * Public so the incremental preview (`BlockPatcher`) can morph a block
216
+ * element's inner content without re-rendering the whole document.
217
+ */
218
+ renderChildren(node: RedNode): string;
219
+ private renderNode;
220
+ /** How many newlines a construct swallows around its own tags. */
221
+ private static readonly NEWLINE_RULES;
222
+ /**
223
+ * What a Miliastry-only block does. This is what the old
224
+ * `isPrevBlockBoundary` / `isTrailingBlockBoundary` pair did for every block:
225
+ * eat the first newline after the open, every newline before the close, and
226
+ * the first newline after the close.
227
+ */
228
+ private static readonly LEGACY_BLOCK_RULE;
229
+ /**
230
+ * Containers whose opening tag occupies no source text, so a backwards scan
231
+ * has to walk straight through them.
232
+ */
233
+ private static readonly WIDTHLESS_OPEN;
234
+ /**
235
+ * Same for the closing side. `list_item` is here because `[/*]` is never
236
+ * written: an item ends where the next `[*]` or the `[/list]` begins, so
237
+ * `\s*\[/list\]` sees the newline that Quasar stores inside the item.
238
+ */
239
+ private static readonly WIDTHLESS_CLOSE;
240
+ private newlineRule;
241
+ private static isNewlineNode;
242
+ private static isBlankText;
243
+ /**
244
+ * Whether this `spacing` / `empty_line` leaf is eaten by a neighbouring tag
245
+ * and therefore renders nothing.
246
+ *
247
+ * Each leaf is exactly ONE source newline (the parser splits a run into one
248
+ * node per `\n`), so the four scans below can be read straight off the
249
+ * regexes they mirror. A newline eaten by any of them is eaten: osu's passes
250
+ * run in a fixed order, but since a consumed newline is consumed whichever
251
+ * pass claimed it, the union is enough — the per-pass order only matters for
252
+ * a budget that could be spent elsewhere, and budgets here are counted from
253
+ * the tag outwards, exactly as `\n?\n?` counts.
254
+ */
255
+ private isNewlineSwallowed;
256
+ /** `\[box\]\n*`, `\[quote\]\s*`, `[centre]\n`. */
257
+ private eatenByOpeningTag;
258
+ /** `\n*\[/box\]`, `\s*\[/quote\]`, `\s*\[/list\]`. */
259
+ private eatenByClosingTag;
260
+ /** `\[/box\]\n?`, `\[/list\]\n?\n?`. */
261
+ private eatenAfterClosingTag;
262
+ /** `\s*\[\*\]` — the only pass that eats whitespace BEFORE an opening tag. */
263
+ private eatenBeforeOpeningTag;
264
+ private renderError;
265
+ private wrapInline;
266
+ private wrapBlock;
267
+ /** Characters `escapeHtml` has to rewrite. Non-global on purpose: `test` must not carry `lastIndex`. */
268
+ private static readonly HTML_ESCAPE_RE;
269
+ private static readonly NAMED_COLORS;
270
+ /** Keyword-or-number CSS values (font-weight, font-style, text-decoration). */
271
+ private isCssKeyword;
272
+ /** Read a metadata field, falling back to the raw tag attribute. */
273
+ /**
274
+ * Lo único que osu! acepta en `[color=…]`.
275
+ *
276
+ * Su `BBCodeForDB::parseColour` sella el tag con un uid sólo si el valor
277
+ * matchea `#[[:xdigit:]]{6}` o `[[:alpha:]]+` — nada más. No valida que el
278
+ * nombre sea un color CSS de verdad (`banana` pasa), pero `#fff`, `#ffffffff`,
279
+ * `rgb(...)`, `$token` o un hex sin `#` no pasan. Sin uid, la segunda pasada
280
+ * no ve el tag y el opener *y* el closer quedan como texto en la página.
281
+ */
282
+ private static readonly OSU_COLOR_RE;
283
+ /**
284
+ * `[color]` con el vocabulario de cada dialecto.
285
+ *
286
+ * Miliastry (y Lyne) aceptan a propósito más que osu: `#RGB`, `#RGBA`, un
287
+ * `$token` de diseño, nombres propios. Bajo `dialect: 'osu'` eso es una
288
+ * mentira: el editor pintaría color donde la página publicada muestra el
289
+ * BBCode crudo. Así que replicamos lo que hace osu — literal el opener,
290
+ * literal el closer, y los hijos renderizados normalmente en el medio.
291
+ *
292
+ * El chequeo mira el texto crudo del atributo, no el valor saneado: osu
293
+ * matchea sobre la fuente, así que `[color="#ffffff"]` (con comillas) también
294
+ * se le escapa.
295
+ */
296
+ private renderColor;
297
+ private colorStyle;
298
+ private fontSizeStyle;
299
+ private fontStyle;
300
+ private renderLink;
301
+ private renderProfile;
302
+ private renderEntity;
303
+ /**
304
+ * El aviso que ocupa el sitio de un medio que no se puede pintar.
305
+ *
306
+ * Lleva `data-bb-empty` con la etiqueta de origen porque el camino
307
+ * HTML→BBCode lee de vuelta lo que hay en el lienzo: sin la marca, el texto
308
+ * del aviso entraba al documento como contenido y se comía la etiqueta. En
309
+ * `docs/ai/hxovc.bbcode`, con cinco `[img][/img]` vacíos, cada edición del
310
+ * bloque convertía otro en la frase «[img] missing source URL».
311
+ */
312
+ private mediaError;
313
+ private renderImage;
314
+ private renderVideo;
315
+ private renderAudio;
316
+ private hexToRgba;
317
+ private renderNotice;
318
+ private renderTables;
319
+ /** Celdas directas de una fila, sin materializar un array intermedio. */
320
+ private static countCells;
321
+ /**
322
+ * Columnas de la tabla más ancha bajo `tableNode`, memoizado por tabla.
323
+ *
324
+ * `getTableContext` se llama una vez POR CELDA, y recorría todas las filas
325
+ * de la tabla en cada llamada, con un `.filter()` —y su array— por fila.
326
+ * Una tabla de 20×20 hacía 8.000 arrays intermedios en cada renderizado
327
+ * para llegar 400 veces al mismo número. El caché se vacía al empezar cada
328
+ * render, así que no retiene nodos entre documentos.
329
+ */
330
+ private static widestRow;
331
+ private getTableContext;
332
+ private renderGallery;
333
+ private renderColumns;
334
+ private renderSeparator;
335
+ private renderScroll;
336
+ private renderAbbr;
337
+ private renderTooltip;
338
+ private renderRaw;
339
+ private renderPlain;
340
+ private renderAlign;
341
+ /**
342
+ * `[centre]` / `[left]` / `[right]` (y `[align=…]`).
343
+ *
344
+ * osu! no usa `text-align` inline: estiliza el bloque por nombre de clase,
345
+ * con la grafía británica `centre`. Fuera del dialecto osu el estilo inline
346
+ * se mantiene, porque ni Miliastry ni Lyne traen esas reglas.
347
+ */
348
+ private renderAlignAs;
349
+ private renderEffect;
350
+ private renderAnim;
351
+ private renderContainer;
352
+ private static readonly STYLE_PROP_WHITELIST;
353
+ private renderStyleTag;
354
+ private renderQuote;
355
+ /**
356
+ * La estructura exacta que `bbcode-spoilerbox` de osu-web espera.
357
+ *
358
+ * El toggle de osu es JS: `js-spoilerbox__link` es el gancho del click y
359
+ * `js-spoilerbox__body` el panel que abre. Si falta cualquiera de las dos
360
+ * clases el box queda mudo, así que la estructura no es decorativa.
361
+ */
362
+ private renderOsuSpoilerbox;
363
+ /** El rótulo de un box bajo osu: el del autor, o `SPOILER` en mayúsculas. */
364
+ private osuBoxTitle;
365
+ private renderSpoilerbox;
366
+ private renderBox;
367
+ /**
368
+ * Marca un `[box]`/`[spoilerbox]` sin título propio.
369
+ *
370
+ * El `<summary>` siempre lleva texto ("Box", "Spoiler"), así que sin esta
371
+ * marca el camino HTML→BBCode leía ese relleno como si el autor lo hubiera
372
+ * escrito y devolvía `[box=Box]`.
373
+ */
374
+ private bareTitleAttr;
375
+ /**
376
+ * ¿El título del box lo escribió el autor, o es el relleno del parser?
377
+ *
378
+ * `BBCodeToGreenNode` ya deja `metadata.title = 'Box'`/`'Spoiler'` para un
379
+ * tag pelado, así que el `fallback` de `renderTitle` nunca llega a usarse:
380
+ * quien quiera otro rótulo por defecto tiene que preguntar por aquí.
381
+ */
382
+ private hasOwnTitle;
383
+ private renderTitle;
384
+ /**
385
+ * `[box=Title:#hex]`, `[tables=striped:#hex]`, `[columns=2:#hex]` → un
386
+ * ` style="--<suffix>-accent:#hex;"` del que el CSS deriva la paleta
387
+ * (título + chevron y borde en boxw; gradientes/header/hover en tables;
388
+ * borde + fondo en columns). Devuelve '' si no hay color válido.
389
+ */
390
+ private boxAccentStyle;
391
+ private renderList;
392
+ private renderListItem;
393
+ private renderCode;
394
+ private renderSVG;
395
+ /**
396
+ * Render an osu! BBCode imagemap.
397
+ *
398
+ * Structure:
399
+ * [imagemap]
400
+ * https://example.com/image.png ← first line = image URL
401
+ * 10 20 50 60 https://... Label ← subsequent lines = clickable areas
402
+ * [/imagemap]
403
+ *
404
+ * Each area line format: x y width height url [label]
405
+ * All values are PERCENTAGES (0–100) relative to the image dimensions.
406
+ * Uses CSS absolute positioning with percentage coordinates.
407
+ */
408
+ private renderImagemap;
409
+ private collectNodeText;
410
+ /**
411
+ * Effect parameters off a node, with every colour re-validated.
412
+ *
413
+ * `startsWith('#')` was NOT a filter: `#a" onmouseover="alert(1)` passes
414
+ * it. Every stop has to survive the full colour allowlist or be dropped,
415
+ * because these values end up inside a `style` attribute.
416
+ */
417
+ private effectParams;
418
+ /** Text inside an effect node, ignoring layout-only children. */
419
+ /**
420
+ * The plain text an effect modulates, line breaks included.
421
+ *
422
+ * A break is a childless `spacing` leaf carrying no text, so collecting
423
+ * it naively contributes nothing and the effect sees one long line.
424
+ * Every two-dimensional axis then collapses: `line` reports zero for
425
+ * the whole block, `column` stretches one row across the paragraph, and
426
+ * `radial` measures a rectangle one line tall. The break was being
427
+ * dropped from the OUTPUT as well, so a multi-line effect previewed as
428
+ * a single run-on line.
429
+ *
430
+ * The document's own plain text spells a break `\n` — that is what
431
+ * `stripColorWrappers` and the studio's sampler both count — so this
432
+ * agrees with them rather than inventing a third answer.
433
+ */
434
+ private effectText;
435
+ private effectChildText;
436
+ /**
437
+ * Paint an effect's computed segments as spans.
438
+ *
439
+ * Shared by rainbow, grow and any modulated gradient, so the preview
440
+ * shows exactly the colours and sizes the BBCode export will contain
441
+ * rather than an approximation of them.
442
+ */
443
+ private renderEffectSegments;
444
+ /**
445
+ * A gradient previews as a real CSS gradient when it is a plain
446
+ * left-to-right ramp, and as computed per-character spans otherwise.
447
+ *
448
+ * The CSS path is genuinely smoother — it interpolates per pixel rather
449
+ * than per glyph — but it can only express a linear sweep. A gradient
450
+ * with a waveform, a non-index axis or quantisation steps looked
451
+ * nothing like its own export while this was the only path.
452
+ */
453
+ private renderGradient;
454
+ /**
455
+ * Escape for both text content and double-quoted attribute values.
456
+ *
457
+ * `'` is included because attribute values elsewhere in the codebase (and in
458
+ * consumer-supplied tag handlers) may be single-quoted; leaving it raw makes
459
+ * the escaping context-dependent, which is how injections get reintroduced.
460
+ */
461
+ /**
462
+ * Escape the five HTML-significant characters.
463
+ *
464
+ * Was five chained `.replace(/x/g, …)` calls: five full scans of the string
465
+ * and up to five intermediate allocations for *every* text node, even though
466
+ * ordinary prose contains none of these characters. This tests once and
467
+ * returns the input untouched in that common case, then does a single pass
468
+ * when there is actually something to escape.
469
+ */
470
+ /** Resolver configured at construction (read through a getter for brevity). */
471
+ private get mentionResolver();
472
+ /**
473
+ * `@mention` shape used by forum hosts: 3-15 word chars, boundaries that
474
+ * exclude longer usernames (same rule LYNE's forum uses). Non-global so
475
+ * `lastIndex` never leaks between calls.
476
+ */
477
+ private static readonly MENTION_RE;
478
+ /**
479
+ * Linkify a plain-text leaf: `@mention` runs and (optionally) timestamp
480
+ * chips. Both run through their resolver; null leaves the run as text.
481
+ * Every interpolated string is HTML-escaped, and hrefs with dangerous
482
+ * protocols (javascript:, data:, vbscript:) are rejected (defence in depth;
483
+ * the resolvers themselves should never produce one).
484
+ */
485
+ private linkifyText;
486
+ /** `1:23`, `01:23.456`, `01:23:456` — the editor deep-link shape. */
487
+ private static readonly TS_RE;
488
+ private linkifyTimestamps;
489
+ /** Root-relative, http(s), mailto and line:// hrefs are the only safe shapes. */
490
+ private isSafeHref;
491
+ private escapeHtml;
492
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * DocumentEngine — JSONExporter
3
+ *
4
+ * Exports the Document Model to JSON.
5
+ * Useful for:
6
+ * - Saving/loading documents in a structured format
7
+ * - Sending over the wire (collaboration)
8
+ * - Debugging and inspecting the document tree
9
+ * - Import/export between different tools
10
+ */
11
+ import { RedNode } from '../Syntax/RedNode';
12
+ import { Visitor } from './Visitor';
13
+ export interface JSONDocument {
14
+ version: number;
15
+ kind: string;
16
+ nodes: JSONNode[];
17
+ metadata?: Record<string, unknown>;
18
+ }
19
+ export interface JSONNode {
20
+ id: string;
21
+ kind: string;
22
+ text: string;
23
+ version: number;
24
+ children: JSONNode[];
25
+ attributes?: Record<string, unknown>;
26
+ metadata?: Record<string, unknown>;
27
+ range?: {
28
+ start: number;
29
+ end: number;
30
+ };
31
+ }
32
+ export declare class JSONExporter extends Visitor<string> {
33
+ /**
34
+ * Export a RedNode tree to a JSON string.
35
+ */
36
+ visit(node: RedNode): string;
37
+ /**
38
+ * Export to a JSON object.
39
+ */
40
+ toJSON(node: RedNode): JSONDocument;
41
+ private serializeNode;
42
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * DocumentEngine — MarkdownExporter
3
+ *
4
+ * Exports the Document Model to Markdown with hybrid BBCode embedding
5
+ * for features not natively supported in standard CommonMark (color, size,
6
+ * underline, alignment, collapsible boxes).
7
+ */
8
+ import { RedNode } from '../Syntax/RedNode';
9
+ import { Visitor } from './Visitor';
10
+ export declare class MarkdownExporter extends Visitor<string> {
11
+ visit(node: RedNode): string;
12
+ export(node: RedNode): string;
13
+ private exportNode;
14
+ private renderTitleMarkdown;
15
+ /**
16
+ * Read a BBCode tag's `=VALUE` attribute.
17
+ */
18
+ /**
19
+ * El atributo del nodo, o `undefined` si no lleva.
20
+ *
21
+ * El recorte lo hace `nodeAttrValue`, el mismo lector del `HTMLRenderer`;
22
+ * aquí sólo se cambia el contrato del caso vacío. Este exportador encadena
23
+ * `metadata ?? atributo ?? contenido`, así que necesita un `undefined`
24
+ * explícito donde `nodeAttrValue` devuelve el texto crudo — es la única
25
+ * diferencia, y por eso no se llama directamente.
26
+ */
27
+ private extractValue;
28
+ }
@@ -0,0 +1,16 @@
1
+ import { RedNode } from '../Syntax/RedNode';
2
+ import { Visitor } from './Visitor';
3
+ import type { VisitorContext } from './Visitor';
4
+ /**
5
+ * SVGRenderer (Runtime ForeignObject Strategy)
6
+ *
7
+ * Instead of attempting to calculate CSS layouts manually,
8
+ * this runtime renderer wraps the exact HTML output in an SVG <foreignObject>.
9
+ *
10
+ * This gives 100% precision with zero DOM scanning overhead for real-time previews.
11
+ */
12
+ export declare class SVGRenderer extends Visitor<string> {
13
+ private htmlRenderer;
14
+ visit(node: RedNode, context?: VisitorContext): string;
15
+ render(root: RedNode): string;
16
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * DocumentEngine — TiptapExporter
3
+ *
4
+ * Exports the Document Model to Tiptap (ProseMirror) JSON format.
5
+ * This proves how easily Quasar's AST bridges to visual WYSIWYG editors.
6
+ *
7
+ * Tiptap uses a JSON format structured like:
8
+ * {
9
+ * type: "doc",
10
+ * content: [
11
+ * {
12
+ * type: "paragraph",
13
+ * content: [
14
+ * { type: "text", text: "Hello ", marks: [{ type: "bold" }] }
15
+ * ]
16
+ * }
17
+ * ]
18
+ * }
19
+ */
20
+ import { RedNode } from '../Syntax/RedNode';
21
+ import { Visitor } from './Visitor';
22
+ export interface TiptapNode {
23
+ type: string;
24
+ attrs?: Record<string, any>;
25
+ content?: TiptapNode[];
26
+ marks?: TiptapMark[];
27
+ text?: string;
28
+ }
29
+ export interface TiptapMark {
30
+ type: string;
31
+ attrs?: Record<string, any>;
32
+ }
33
+ export declare class TiptapExporter extends Visitor<string> {
34
+ visit(node: RedNode): string;
35
+ toTiptap(node: RedNode): TiptapNode;
36
+ private processChildren;
37
+ private processNode;
38
+ private kindToMark;
39
+ private kindToBlockType;
40
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * DocumentEngine — Visitor
3
+ *
4
+ * Base visitor pattern for traversing the Red Tree.
5
+ * All exporters and renderers extend this.
6
+ *
7
+ * Instead of switch statements on tag names, use the visitor pattern.
8
+ * New languages/exporters don't modify existing code.
9
+ *
10
+ * Inspired by SwiftSyntax's Visitor pattern.
11
+ */
12
+ import { RedNode } from '../Syntax/RedNode';
13
+ export interface VisitorContext {
14
+ source: string;
15
+ options: Record<string, unknown>;
16
+ }
17
+ export declare abstract class Visitor<T = string> {
18
+ protected context: VisitorContext;
19
+ /**
20
+ * Visit a node and produce an output value.
21
+ */
22
+ abstract visit(node: RedNode, context?: VisitorContext): T;
23
+ /**
24
+ * Visit all children and combine results.
25
+ */
26
+ visitChildren(node: RedNode, separator?: string): T[];
27
+ /**
28
+ * Get the text content of a node.
29
+ */
30
+ getText(node: RedNode): string;
31
+ /**
32
+ * Set the visitor context.
33
+ */
34
+ setContext(context: VisitorContext): void;
35
+ }
@@ -0,0 +1,9 @@
1
+ export { Visitor } from './Visitor';
2
+ export type { VisitorContext } from './Visitor';
3
+ export { BBCodeExporter } from './BBCodeExporter';
4
+ export { HTMLRenderer } from './HTMLRenderer';
5
+ export { morphHTML, morphElement } from './DOMMorpher';
6
+ export { MarkdownExporter } from './MarkdownExporter';
7
+ export { JSONExporter } from './JSONExporter';
8
+ export { SVGRenderer } from './SVGRenderer';
9
+ export type { JSONDocument, JSONNode } from './JSONExporter';