@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.
Files changed (273) hide show
  1. package/dist/Visuals/lyne.css +117 -6
  2. package/dist/Visuals/osu.css +36 -5
  3. package/dist/index.js +9664 -3282
  4. package/dist/index.mjs +9535 -3283
  5. package/dist/types/Analysis/Contracts/AnalysisReport.d.ts +22 -0
  6. package/dist/types/Analysis/Contracts/Contribution.d.ts +68 -0
  7. package/dist/types/Analysis/Contracts/Pass.d.ts +59 -0
  8. package/dist/types/Analysis/Contracts/PipelineContext.d.ts +34 -0
  9. package/dist/types/Analysis/Passes/Analysis/ColorUsageAnalyzer.d.ts +56 -0
  10. package/dist/types/Analysis/Passes/Analysis/GradientAnalyzer.d.ts +111 -0
  11. package/dist/types/Analysis/Passes/Analysis/MergeableColorAnalyzer.d.ts +26 -0
  12. package/dist/types/Analysis/Passes/Analysis/RainbowAnalyzer.d.ts +47 -0
  13. package/dist/types/Analysis/Passes/Analysis/SymbolAnalyzer.d.ts +89 -0
  14. package/dist/types/Analysis/Passes/Analysis/WaveAnalyzer.d.ts +44 -0
  15. package/dist/types/Analysis/Passes/Analysis/__tests__/GradientAnalyzer.test.d.ts +4 -0
  16. package/dist/types/Analysis/Passes/Analysis/__tests__/MergeableColorAnalyzer.test.d.ts +4 -0
  17. package/dist/types/Analysis/Passes/Analysis/__tests__/RainbowAnalyzer.test.d.ts +4 -0
  18. package/dist/types/Analysis/Passes/Analysis/__tests__/WaveAnalyzer.test.d.ts +4 -0
  19. package/dist/types/Analysis/Passes/Decision/DefaultDecision.d.ts +30 -0
  20. package/dist/types/Analysis/Passes/Decision/PaletteRemapDecision.d.ts +169 -0
  21. package/dist/types/Analysis/Passes/Decision/__tests__/DefaultDecision.test.d.ts +4 -0
  22. package/dist/types/Analysis/Passes/Transform/CollapseGradientTransform.d.ts +37 -0
  23. package/dist/types/Analysis/Passes/Transform/MergeColorsTransform.d.ts +28 -0
  24. package/dist/types/Analysis/Passes/Transform/RainbowCollapseTransform.d.ts +19 -0
  25. package/dist/types/Analysis/Passes/Transform/WaveCollapseTransform.d.ts +20 -0
  26. package/dist/types/Analysis/Passes/Utility/CharacterCountAnalyzer.d.ts +19 -0
  27. package/dist/types/Analysis/Pipeline/Pipeline.d.ts +69 -0
  28. package/dist/types/Analysis/Pipeline/PipelineBuilder.d.ts +31 -0
  29. package/dist/types/Analysis/Pipeline/PipelineStage.d.ts +17 -0
  30. package/dist/types/Analysis/Utils/color-utils.d.ts +60 -0
  31. package/dist/types/Analysis/__tests__/Integration.test.d.ts +8 -0
  32. package/dist/types/Analysis/__tests__/PaletteRemap.test.d.ts +14 -0
  33. package/dist/types/Analysis/__tests__/Pipeline.test.d.ts +10 -0
  34. package/dist/types/Analysis/__tests__/RoundTrip.test.d.ts +30 -0
  35. package/dist/types/Analysis/__tests__/SymbolAnalyzer.test.d.ts +11 -0
  36. package/dist/types/Analysis/index.d.ts +40 -0
  37. package/dist/types/BBCode/BBCodeDocumentModel.d.ts +92 -0
  38. package/dist/types/BBCode/BBCodeToGreenNode.d.ts +111 -0
  39. package/dist/types/BBCode/Parser.d.ts +78 -0
  40. package/dist/types/BBCode/index.d.ts +4 -0
  41. package/dist/types/Collab/positions.d.ts +55 -0
  42. package/dist/types/Commands/Command.d.ts +37 -0
  43. package/dist/types/Commands/CommandRegistry.d.ts +48 -0
  44. package/dist/types/Commands/DeleteNode.d.ts +7 -0
  45. package/dist/types/Commands/InsertText.d.ts +8 -0
  46. package/dist/types/Commands/SplitMerge.d.ts +9 -0
  47. package/dist/types/Commands/WrapInTag.d.ts +8 -0
  48. package/dist/types/Commands/index.d.ts +6 -0
  49. package/dist/types/Diff/TreeDiffer.d.ts +52 -0
  50. package/dist/types/Diff/__tests__/TreeDiffer.test.d.ts +1 -0
  51. package/dist/types/Diff/index.d.ts +2 -0
  52. package/dist/types/Edits/EditPlan.d.ts +144 -0
  53. package/dist/types/Edits/Optimizer.d.ts +78 -0
  54. package/dist/types/Edits/Rules/Rule.d.ts +91 -0
  55. package/dist/types/Edits/Rules/dropEmptyTags.d.ts +40 -0
  56. package/dist/types/Edits/Rules/dropRedundantNesting.d.ts +59 -0
  57. package/dist/types/Edits/Rules/index.d.ts +9 -0
  58. package/dist/types/Edits/Rules/mergeAdjacent.d.ts +93 -0
  59. package/dist/types/Edits/Rules/reorderWrappers.d.ts +42 -0
  60. package/dist/types/Edits/Rules/shortenHex.d.ts +30 -0
  61. package/dist/types/Edits/Rules/tagValue.d.ts +56 -0
  62. package/dist/types/Edits/Rules/unwrapInvisibleColor.d.ts +47 -0
  63. package/dist/types/Edits/applyEdits.d.ts +36 -0
  64. package/dist/types/Edits/index.d.ts +6 -0
  65. package/dist/types/Events/EventBus.d.ts +103 -0
  66. package/dist/types/Events/index.d.ts +2 -0
  67. package/dist/types/Formatter/Formatter.d.ts +32 -0
  68. package/dist/types/Formatter/index.d.ts +2 -0
  69. package/dist/types/HTML/HTMLDocumentModel.d.ts +13 -0
  70. package/dist/types/HTML/HTMLToGreenNode.d.ts +4 -0
  71. package/dist/types/Incremental/BracketIndex.d.ts +101 -0
  72. package/dist/types/Incremental/ChangeTracker.d.ts +69 -0
  73. package/dist/types/Incremental/IncrementalParser.d.ts +246 -0
  74. package/dist/types/Incremental/__tests__/BracketIndex.test.d.ts +1 -0
  75. package/dist/types/Incremental/__tests__/IncrementalParser.test.d.ts +1 -0
  76. package/dist/types/Incremental/index.d.ts +4 -0
  77. package/dist/types/Lexer/BBCodeLexer.d.ts +74 -0
  78. package/dist/types/Lexer/Lexer.d.ts +54 -0
  79. package/dist/types/Lexer/index.d.ts +4 -0
  80. package/dist/types/Linter/Linter.d.ts +58 -0
  81. package/dist/types/Linter/index.d.ts +2 -0
  82. package/dist/types/Markdown/MarkdownAST.d.ts +122 -0
  83. package/dist/types/Markdown/MarkdownDocumentModel.d.ts +15 -0
  84. package/dist/types/Markdown/MarkdownLexer.d.ts +155 -0
  85. package/dist/types/Markdown/MarkdownParser.d.ts +42 -0
  86. package/dist/types/Markdown/MarkdownToGreenNode.d.ts +6 -0
  87. package/dist/types/MilHibri/MilHibriDocumentModel.d.ts +21 -0
  88. package/dist/types/Model/DocumentModel.d.ts +318 -0
  89. package/dist/types/Model/NodeFactory.d.ts +29 -0
  90. package/dist/types/Model/TagRegistry.d.ts +165 -0
  91. package/dist/types/Model/index.d.ts +5 -0
  92. package/dist/types/Plugins/PluginAPI.d.ts +44 -0
  93. package/dist/types/Plugins/PluginRegistry.d.ts +81 -0
  94. package/dist/types/Plugins/index.d.ts +3 -0
  95. package/dist/types/Queries/QueryEngine.d.ts +32 -0
  96. package/dist/types/Queries/index.d.ts +1 -0
  97. package/dist/types/Reconciler/SurgicalReconciler.d.ts +23 -0
  98. package/dist/types/RenderPipeline/RenderPipeline.d.ts +46 -0
  99. package/dist/types/RenderPipeline/RenderTree.d.ts +55 -0
  100. package/dist/types/RenderPipeline/index.d.ts +4 -0
  101. package/dist/types/Repair/NestingRepair.d.ts +48 -0
  102. package/dist/types/Repair/index.d.ts +1 -0
  103. package/dist/types/Semantic/SemanticAnalyzer.d.ts +321 -0
  104. package/dist/types/Semantic/index.d.ts +2 -0
  105. package/dist/types/Symbols/SymbolTable.d.ts +47 -0
  106. package/dist/types/Symbols/index.d.ts +1 -0
  107. package/dist/types/Syntax/GreenNode.d.ts +154 -0
  108. package/dist/types/Syntax/GreenNodePool.d.ts +134 -0
  109. package/dist/types/Syntax/NodeMatcher.d.ts +75 -0
  110. package/dist/types/Syntax/RedNode.d.ts +280 -0
  111. package/dist/types/Syntax/RedNodeStore.d.ts +116 -0
  112. package/dist/types/Syntax/TreeBuilder.d.ts +54 -0
  113. package/dist/types/Syntax/__tests__/GreenNode.test.d.ts +1 -0
  114. package/dist/types/Syntax/__tests__/RedNode.test.d.ts +1 -0
  115. package/dist/types/Syntax/__tests__/RedNodeStore.test.d.ts +1 -0
  116. package/dist/types/Syntax/greenEdit.d.ts +64 -0
  117. package/dist/types/Syntax/hash.d.ts +17 -0
  118. package/dist/types/Syntax/index.d.ts +8 -0
  119. package/dist/types/Syntax/nodeAttr.d.ts +63 -0
  120. package/dist/types/Syntax/partition.d.ts +67 -0
  121. package/dist/types/Syntax/preserveNodeIds.d.ts +47 -0
  122. package/dist/types/Tokens/TokenResolver.d.ts +25 -0
  123. package/dist/types/Tokens/index.d.ts +1 -0
  124. package/dist/types/Transactions/Transaction.d.ts +36 -0
  125. package/dist/types/Transactions/UndoManager.d.ts +63 -0
  126. package/dist/types/Transactions/index.d.ts +3 -0
  127. package/dist/types/Transformers/ASTOptimizer.d.ts +34 -0
  128. package/dist/types/Transformers/GradientTransformer.d.ts +18 -0
  129. package/dist/types/Transformers/GrowTransformer.d.ts +16 -0
  130. package/dist/types/Transformers/RainbowTransformer.d.ts +17 -0
  131. package/dist/types/Transformers/SineWaveTransformer.d.ts +17 -0
  132. package/dist/types/Transformers/Transformer.d.ts +17 -0
  133. package/dist/types/Types/core.d.ts +104 -0
  134. package/dist/types/Types/diagnostics.d.ts +85 -0
  135. package/dist/types/Types/index.d.ts +11 -0
  136. package/dist/types/Types/operations.d.ts +109 -0
  137. package/dist/types/Types/queries.d.ts +67 -0
  138. package/dist/types/Types/symbols.d.ts +42 -0
  139. package/dist/types/Types/tokens.d.ts +75 -0
  140. package/dist/types/Utils/BBCodeGenerator.d.ts +21 -0
  141. package/dist/types/Utils/ColorMath.d.ts +92 -0
  142. package/dist/types/Utils/EffectMath.d.ts +643 -0
  143. package/dist/types/Utils/TagVocabulary.d.ts +104 -0
  144. package/dist/types/Utils/TagVocabulary.test.d.ts +1 -0
  145. package/dist/types/Utils/color.d.ts +29 -0
  146. package/dist/types/Utils/dom-to-svg.d.ts +28 -0
  147. package/dist/types/Utils/dom-to-svg.test.d.ts +1 -0
  148. package/dist/types/Utils/treeTransformers.d.ts +160 -0
  149. package/dist/types/Visitors/BBBlocksExporter.d.ts +27 -0
  150. package/dist/types/Visitors/BBCodeExporter.d.ts +90 -0
  151. package/dist/types/Visitors/BlockPatcher.d.ts +91 -0
  152. package/dist/types/Visitors/DOMMorpher.d.ts +13 -0
  153. package/dist/types/Visitors/HTMLRenderer.d.ts +380 -0
  154. package/dist/types/Visitors/JSONExporter.d.ts +42 -0
  155. package/dist/types/Visitors/MarkdownExporter.d.ts +28 -0
  156. package/dist/types/Visitors/SVGRenderer.d.ts +16 -0
  157. package/dist/types/Visitors/TiptapExporter.d.ts +40 -0
  158. package/dist/types/Visitors/Visitor.d.ts +35 -0
  159. package/dist/types/Visitors/index.d.ts +9 -0
  160. package/dist/types/Visuals/BoxDrawer.d.ts +49 -0
  161. package/dist/types/Visuals/index.d.ts +38 -0
  162. package/dist/types/index.d.ts +154 -0
  163. package/package.json +14 -10
  164. package/src/Analysis/Passes/Analysis/ColorUsageAnalyzer.ts +103 -0
  165. package/src/Analysis/Passes/Analysis/GradientAnalyzer.ts +294 -20
  166. package/src/Analysis/Passes/Analysis/SymbolAnalyzer.ts +257 -0
  167. package/src/Analysis/Passes/Decision/PaletteRemapDecision.ts +425 -0
  168. package/src/Analysis/Pipeline/Pipeline.ts +15 -2
  169. package/src/Analysis/index.ts +8 -2
  170. package/src/BBCode/BBCodeDocumentModel.ts +3 -0
  171. package/src/BBCode/BBCodeToGreenNode.ts +18 -12
  172. package/src/BBCode/Parser.ts +38 -1
  173. package/src/Edits/EditPlan.ts +290 -0
  174. package/src/Edits/Optimizer.ts +185 -0
  175. package/src/Edits/Rules/Rule.ts +145 -0
  176. package/src/Edits/Rules/dropEmptyTags.ts +110 -0
  177. package/src/Edits/Rules/dropRedundantNesting.ts +120 -0
  178. package/src/Edits/Rules/index.ts +28 -0
  179. package/src/Edits/Rules/mergeAdjacent.ts +224 -0
  180. package/src/Edits/Rules/reorderWrappers.ts +154 -0
  181. package/src/Edits/Rules/shortenHex.ts +79 -0
  182. package/src/Edits/Rules/tagValue.ts +105 -0
  183. package/src/Edits/Rules/unwrapInvisibleColor.ts +93 -0
  184. package/src/Edits/applyEdits.ts +54 -0
  185. package/src/Edits/index.ts +22 -0
  186. package/src/Events/EventBus.ts +18 -0
  187. package/src/HTML/HTMLToGreenNode.ts +185 -37
  188. package/src/Incremental/BracketIndex.ts +249 -0
  189. package/src/Incremental/IncrementalParser.ts +464 -41
  190. package/src/Lexer/BBCodeLexer.ts +25 -3
  191. package/src/Lexer/index.ts +1 -1
  192. package/src/Markdown/MarkdownAST.ts +65 -2
  193. package/src/Markdown/MarkdownLexer.ts +69 -14
  194. package/src/Markdown/MarkdownParser.ts +749 -62
  195. package/src/Markdown/MarkdownToGreenNode.ts +148 -27
  196. package/src/MilHibri/MilHibriDocumentModel.ts +56 -0
  197. package/src/Model/DocumentModel.ts +138 -5
  198. package/src/Model/TagRegistry.ts +129 -134
  199. package/src/Reconciler/SurgicalReconciler.ts +460 -0
  200. package/src/Repair/NestingRepair.ts +116 -0
  201. package/src/Repair/index.ts +1 -0
  202. package/src/Semantic/SemanticAnalyzer.ts +1802 -65
  203. package/src/Syntax/GreenNodePool.ts +12 -12
  204. package/src/Syntax/RedNode.ts +17 -3
  205. package/src/Syntax/RedNodeStore.ts +9 -1
  206. package/src/Syntax/nodeAttr.ts +172 -0
  207. package/src/Tokens/TokenResolver.ts +55 -0
  208. package/src/Tokens/index.ts +1 -0
  209. package/src/Types/core.ts +15 -0
  210. package/src/Utils/ColorMath.ts +276 -55
  211. package/src/Utils/EffectMath.ts +2462 -0
  212. package/src/Utils/TagVocabulary.ts +141 -0
  213. package/src/Utils/dom-to-svg.ts +28 -12
  214. package/src/Visitors/BBBlocksExporter.ts +15 -1
  215. package/src/Visitors/BBCodeExporter.ts +319 -32
  216. package/src/Visitors/BlockPatcher.ts +87 -11
  217. package/src/Visitors/DOMMorpher.ts +15 -5
  218. package/src/Visitors/HTMLRenderer.ts +374 -151
  219. package/src/Visitors/MarkdownExporter.ts +98 -41
  220. package/src/Visitors/TiptapExporter.ts +1 -0
  221. package/src/Visuals/index.ts +7 -4
  222. package/src/Visuals/lyne.css +117 -6
  223. package/src/Visuals/osu.css +36 -5
  224. package/src/index.ts +172 -5
  225. package/dist/index.d.mts +0 -4612
  226. package/dist/index.d.ts +0 -4612
  227. package/src/Analysis/Passes/Analysis/__tests__/GradientAnalyzer.test.ts +0 -135
  228. package/src/Analysis/Passes/Analysis/__tests__/MergeableColorAnalyzer.test.ts +0 -84
  229. package/src/Analysis/Passes/Analysis/__tests__/RainbowAnalyzer.test.ts +0 -99
  230. package/src/Analysis/Passes/Analysis/__tests__/WaveAnalyzer.test.ts +0 -119
  231. package/src/Analysis/Passes/Decision/__tests__/DefaultDecision.test.ts +0 -179
  232. package/src/Analysis/__tests__/Integration.test.ts +0 -162
  233. package/src/Analysis/__tests__/Pipeline.test.ts +0 -133
  234. package/src/Diff/__tests__/TreeDiffer.test.ts +0 -65
  235. package/src/Incremental/__tests__/IncrementalParser.test.ts +0 -164
  236. package/src/Syntax/__tests__/GreenNode.test.ts +0 -33
  237. package/src/Syntax/__tests__/RedNode.test.ts +0 -81
  238. package/src/Syntax/__tests__/RedNodeStore.test.ts +0 -104
  239. package/src/Tests/ASTOptimizerIdempotence.test.ts +0 -77
  240. package/src/Tests/BlockPatcher.test.ts +0 -437
  241. package/src/Tests/BlockPatcherWindowed.test.ts +0 -364
  242. package/src/Tests/BoxDrawer.test.ts +0 -217
  243. package/src/Tests/BoxRichTitle.test.ts +0 -105
  244. package/src/Tests/Chars500kBenchmark.test.ts +0 -151
  245. package/src/Tests/Chars500kEdits.test.ts +0 -321
  246. package/src/Tests/CollabPositions.test.ts +0 -146
  247. package/src/Tests/CompilerPathProfiling.test.ts +0 -186
  248. package/src/Tests/DOMMorpher.test.ts +0 -142
  249. package/src/Tests/DomPatchPerf.test.ts +0 -60
  250. package/src/Tests/EffectSegments.snapshot.json +0 -616
  251. package/src/Tests/EffectSegments.test.ts +0 -68
  252. package/src/Tests/FindNodeAtOffset.test.ts +0 -65
  253. package/src/Tests/Fuzzer.test.ts +0 -166
  254. package/src/Tests/GreenNodePool.test.ts +0 -153
  255. package/src/Tests/Lexer.test.ts +0 -238
  256. package/src/Tests/LyneMode.test.ts +0 -291
  257. package/src/Tests/ModelCoherence.test.ts +0 -180
  258. package/src/Tests/Partition.test.ts +0 -238
  259. package/src/Tests/PluginTags.test.ts +0 -150
  260. package/src/Tests/ProblematicSection.test.ts +0 -46
  261. package/src/Tests/ProblematicSectionHTML.test.ts +0 -58
  262. package/src/Tests/RedReuse.test.ts +0 -134
  263. package/src/Tests/ReproDelete20k.test.ts +0 -62
  264. package/src/Tests/SemanticValidators.test.ts +0 -136
  265. package/src/Tests/StableNodeIds.test.ts +0 -210
  266. package/src/Tests/StudioColorBloat.test.ts +0 -25
  267. package/src/Tests/StudioDebugText.test.ts +0 -27
  268. package/src/Tests/StudioTrailingChar.test.ts +0 -25
  269. package/src/Tests/StudioValidText.test.ts +0 -25
  270. package/src/Tests/UrlImgBug.test.ts +0 -23
  271. package/src/Tests/VisualBuilderFidelity.test.ts +0 -105
  272. package/src/Tests/referenceDocument.ts +0 -119
  273. package/src/Utils/dom-to-svg.test.ts +0 -86
@@ -1,14 +1,13 @@
1
1
  /**
2
2
  * DocumentEngine — MarkdownExporter
3
3
  *
4
- * Exports the Document Model to Markdown.
5
- * Demonstrates the format-agnostic power of the engine:
6
- * BBCode in DocumentModel → Markdown out.
7
- *
8
- * A single visitor, no parser changes needed.
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).
9
7
  */
10
8
 
11
9
  import { RedNode } from '../Syntax/RedNode'
10
+ import { nodeAttrValue } from '../Syntax/nodeAttr'
12
11
  import { Visitor } from './Visitor'
13
12
 
14
13
  export class MarkdownExporter extends Visitor<string> {
@@ -34,35 +33,99 @@ export class MarkdownExporter extends Visitor<string> {
34
33
  switch (node.kind) {
35
34
  case 'bold': return `**${content}**`
36
35
  case 'italic': return `*${content}*`
37
- case 'underline': return content
36
+ case 'underline': return `++${content}++`
38
37
  case 'strikethrough': return `~~${content}~~`
39
38
  case 'inline_code': return `\`${content}\``
40
39
  case 'code': return `\`\`\`\n${content}\n\`\`\``
41
40
  case 'spoiler': return `||${content}||`
42
- case 'color': return content
43
- case 'font_size': return content
44
- case 'font': return content
45
- case 'heading': return `${'#'.repeat(Math.min(depth + 1, 6))} ${content}`
46
- case 'center': return content
47
- case 'right': return content
48
- case 'left': return content
49
- case 'url': return `[${content}](${this.extractValue(node) || content})`
50
- case 'email': return `[${content}](mailto:${this.extractValue(node) || content})`
51
- case 'profile': return `[${content}](https://osu.ppy.sh/users/${this.extractValue(node) || content})`
52
- case 'image': return `![Image](${node.text || ''})`
41
+ case 'color': {
42
+ const c = (node.metadata?.color as string) || this.extractValue(node) || '#ffffff'
43
+ return `[${content}]{${c}}`
44
+ }
45
+ case 'font_size': {
46
+ const s = (node.metadata?.size as string) || this.extractValue(node) || '100'
47
+ return `[${content}]{size="${s}"}`
48
+ }
49
+ case 'font': {
50
+ const f = (node.metadata?.font as string) || this.extractValue(node) || 'sans-serif'
51
+ return `[${content}]{font="${f}"}`
52
+ }
53
+ case 'heading': {
54
+ const level = (node.metadata?.level as number) || depth + 1
55
+ return `${'#'.repeat(Math.min(level, 6))} ${content}`
56
+ }
57
+ case 'center': return `-> ${content.trim()} <-`
58
+ case 'right': return `-> ${content.trim()} ->`
59
+ case 'left': return `[left]${content}[/left]`
60
+ case 'align': {
61
+ const a = (node.metadata?.align as string) || this.extractValue(node) || 'center'
62
+ if (a === 'center') return `-> ${content.trim()} <-`
63
+ if (a === 'right') return `-> ${content.trim()} ->`
64
+ return `[align=${a}]${content}[/align]`
65
+ }
66
+ case 'url': return `[${content}](${node.metadata?.href || this.extractValue(node) || content})`
67
+ case 'email': {
68
+ const addr = (node.metadata?.href as string)?.replace('mailto:', '') || this.extractValue(node) || content
69
+ return `[${content}](mailto:${addr})`
70
+ }
71
+ case 'profile': return `[${content}](https://osu.ppy.sh/users/${node.metadata?.username || this.extractValue(node) || content})`
72
+ case 'image': {
73
+ const src = (node.metadata?.src as string) || (node.text && !node.text.startsWith('=') ? node.text : '') || content
74
+ const alt = (node.metadata?.alt as string) || (content && content !== src ? content : 'Image')
75
+ return `![${alt}](${src})`
76
+ }
53
77
  case 'video': return `[🎥 YouTube Video](https://youtube.com/watch?v=${node.text || ''})`
54
78
  case 'audio': return `[🎵 Audio](${node.text || ''})`
55
- case 'quote': return `> **${this.extractValue(node) || 'Quote'}**\n> ${content.replace(/\\n/g, '\n> ')}`
56
- case 'notice': return `> [!NOTE]\n> ${content.replace(/\\n/g, '\n> ')}`
57
- case 'spoilerbox': return `**${this.renderTitleMarkdown(node) || 'Spoiler'}**\n${content}`
79
+ case 'quote': {
80
+ const source = (node.metadata?.source as string) || this.extractValue(node)
81
+ const header = source ? `**${source}**\n> ` : ''
82
+ return `> ${header}${content.replace(/\n/g, '\n> ')}`
83
+ }
84
+ case 'notice': {
85
+ const color = (node.metadata?.color as string) || this.extractValue(node)
86
+ const colorAttr = color ? `=${color}` : ''
87
+ return `[notice${colorAttr}]\n${content}\n[/notice]`
88
+ }
89
+ case 'wnotice': {
90
+ const color = (node.metadata?.color as string) || this.extractValue(node)
91
+ const colorAttr = color ? `=${color}` : ''
92
+ return `[wnotice${colorAttr}]\n${content}\n[/wnotice]`
93
+ }
94
+ case 'spoilerbox':
58
95
  case 'box':
59
- case 'boxw': return `**${this.renderTitleMarkdown(node) || 'Box'}**\n${content}`
60
- case 'list': return content
61
- case 'list_item': return `${' '.repeat(Math.max(0, depth - 1))}- ${content.trim()}`
96
+ case 'boxw': {
97
+ const title = this.renderTitleMarkdown(node)
98
+ const header = title ? `::: details ${title}` : '::: details'
99
+ return `${header}\n${content}\n:::`
100
+ }
101
+ case 'list': {
102
+ const lines: string[] = []
103
+ let orderIdx = 1
104
+ for (const child of node.children) {
105
+ if (child.kind === 'list_item') {
106
+ const itemContent = child.children.map(c => this.exportNode(c, depth)).join('')
107
+ const isOrdered = node.metadata?.ordered
108
+ const prefix = isOrdered ? `${orderIdx++}. ` : '- '
109
+ lines.push(`${' '.repeat(Math.max(0, depth))}${prefix}${itemContent.trim()}`)
110
+ }
111
+ }
112
+ return lines.length > 0 ? lines.join('\n') + '\n' : content
113
+ }
114
+ case 'list_item': {
115
+ const isOrdered = node.parent?.metadata?.ordered
116
+ const prefix = isOrdered ? '1. ' : '- '
117
+ return `${' '.repeat(Math.max(0, depth - 1))}${prefix}${content.trim()}`
118
+ }
119
+ case 'separator': return '---'
62
120
  case 'spacing': return '\n'
63
121
  case 'empty_line': return '\n'
64
- case 'text':
65
- default: return content || node.text || ''
122
+ case 'paragraph': return content
123
+ case 'text': return content || node.text || ''
124
+ default: {
125
+ const val = this.extractValue(node) || (node.metadata?.value as string) || (node.metadata?.raw as string)
126
+ const attr = val !== undefined && val !== null && String(val) !== '' ? `=${val}` : ''
127
+ return `[${node.kind}${attr}]${content}[/${node.kind}]`
128
+ }
66
129
  }
67
130
  }
68
131
 
@@ -76,24 +139,18 @@ export class MarkdownExporter extends Visitor<string> {
76
139
 
77
140
  /**
78
141
  * Read a BBCode tag's `=VALUE` attribute.
142
+ */
143
+ /**
144
+ * El atributo del nodo, o `undefined` si no lleva.
79
145
  *
80
- * This previously read `node.properties` / `node.attributes`, neither of
81
- * which exists on RedNode so it silently returned `undefined` for every
82
- * node and every call site fell through to its placeholder. That is why
83
- * Markdown export dropped link targets and quote/box titles.
146
+ * El recorte lo hace `nodeAttrValue`, el mismo lector del `HTMLRenderer`;
147
+ * aquí sólo se cambia el contrato del caso vacío. Este exportador encadena
148
+ * `metadata ?? atributo ?? contenido`, así que necesita un `undefined`
149
+ * explícito donde `nodeAttrValue` devuelve el texto crudo — es la única
150
+ * diferencia, y por eso no se llama directamente.
84
151
  */
85
152
  private extractValue(node: RedNode): string | undefined {
86
- const text = node.text || ''
87
- const eqIdx = text.indexOf('=')
88
- if (eqIdx < 0) return undefined
89
-
90
- let value = text.slice(eqIdx + 1)
91
- if (
92
- (value.startsWith('"') && value.endsWith('"')) ||
93
- (value.startsWith("'") && value.endsWith("'"))
94
- ) {
95
- value = value.slice(1, -1)
96
- }
97
- return value || undefined
153
+ if (!(node.text || '').includes('=')) return undefined
154
+ return nodeAttrValue(node) || undefined
98
155
  }
99
156
  }
@@ -121,6 +121,7 @@ export class TiptapExporter extends Visitor<string> {
121
121
  case 'rainbow':
122
122
  case 'grow':
123
123
  case 'sinewave':
124
+ case 'paint':
124
125
  return { type: 'miliastryEffect', attrs: { effect: node.kind, ...node.metadata } }
125
126
  }
126
127
  return null
@@ -6,11 +6,14 @@
6
6
  * modificar el renderer HTML.
7
7
  *
8
8
  * Uso:
9
- * // Importar como CSS directo (soportado por Next.js, Vite, Webpack):
10
- * import 'MiliastryPlatform/DocumentEngine/Visuals/osu.css'
9
+ * // Desde la FUENTE, que es como lo consume el monorepo (Next.js, Vite,
10
+ * // Webpack): esta ruta funciona sin haber compilado nada.
11
+ * import '@miliastry/quasar/src/Visuals/osu.css'
12
+ *
13
+ * // Desde el paquete PUBLICADO. Solo resuelve tras `npm run build`, que es
14
+ * // lo que copia los CSS a dist/Visuals (ver scripts/copy-css.mjs).
15
+ * import '@miliastry/quasar/Visuals/osu.css'
11
16
  *
12
- * // O desde un componente React:
13
- * import 'MiliastryPlatform/DocumentEngine/Visuals/osu.css'
14
17
  * // Luego en el JSX: <div className="bbcode-preview" ...
15
18
  *
16
19
  * Para cambiar de tema visual, solo cambia el import del CSS.
@@ -6,11 +6,28 @@
6
6
  *
7
7
  * Scoped under `.bbcode-preview-lyne` and `.theme-lyne`.
8
8
  * All rules strictly override default/osu.css styles to ensure 0% pink bleed.
9
+ *
10
+ * No `@import` of Google Fonts here — the host supplies the faces.
11
+ *
12
+ * This file used to open with
13
+ * @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono…&family=Saira+Condensed…&family=Saira…')
14
+ * and that line broke the desktop app. Chromium reports a stylesheet as FAILED
15
+ * when any of its @imports fails to load, so under a CSP whose `style-src` does
16
+ * not list fonts.googleapis.com (Tauri's) — or simply with no network — the CSS
17
+ * chunk carrying this file rejected, the dynamic import of the whole BBCode
18
+ * feature rejected with it, and the kernel booted without an editor.
19
+ *
20
+ * Miliastry self-hosts these families with next/font and publishes them as
21
+ * `--font-syne-display` / `--font-syne-body` / `--font-syne-mono` (app/layout.tsx;
22
+ * `[data-theme='syne']` in globals.css reads the same variables). They are
23
+ * used when present; the plain family names remain as the fallback for a host
24
+ * that installs the fonts by other means.
9
25
  */
10
26
 
11
- @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=Saira+Condensed:wght@600;700&family=Saira:wght@400;600&display=swap');
12
-
13
- :root {
27
+ .theme-lyne,
28
+ .bbcode-preview-lyne,
29
+ [data-theme='syne'],
30
+ [data-dialect='lyne'] {
14
31
  --color-accent: #2EE6E2;
15
32
  --color-accent-bright: #68FFF8;
16
33
  --color-warning: #FFC34D;
@@ -18,9 +35,9 @@
18
35
  --color-text-mid: #9EACD4;
19
36
  --color-text-dim: #606C8F;
20
37
  --color-hairline: rgba(255, 255, 255, 0.12);
21
- --font-display: "Saira Condensed", sans-serif;
22
- --font-body: "Saira", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
23
- --font-numeric: "IBM Plex Mono", monospace;
38
+ --font-display: var(--font-syne-display, "Saira Condensed"), "Saira Condensed", sans-serif;
39
+ --font-body: var(--font-syne-body, "Saira"), "Saira", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
40
+ --font-numeric: var(--font-syne-mono, "IBM Plex Mono"), "IBM Plex Mono", monospace;
24
41
  --lyne-cut: 8px;
25
42
  }
26
43
 
@@ -52,6 +69,41 @@
52
69
  border-bottom-color: var(--color-accent, #2EE6E2) !important;
53
70
  }
54
71
 
72
+ .bbcode-preview-lyne a:has(img),
73
+ .bbcode-preview-lyne a:has(.bb-img),
74
+ .bbcode-preview-lyne a:has(svg),
75
+ .bbcode-preview-lyne a.bb-link-img,
76
+ .bbcode-preview.theme-lyne a:has(img),
77
+ .bbcode-preview.theme-lyne a:has(.bb-img),
78
+ .bbcode-preview.theme-lyne a:has(svg),
79
+ .bbcode-preview.theme-lyne a.bb-link-img {
80
+ border-bottom: none !important;
81
+ text-decoration: none !important;
82
+ display: inline-block;
83
+ vertical-align: middle;
84
+ }
85
+
86
+ .bbcode-preview-lyne a:has(img):hover,
87
+ .bbcode-preview-lyne a:has(.bb-img):hover,
88
+ .bbcode-preview-lyne a:has(svg):hover,
89
+ .bbcode-preview-lyne a.bb-link-img:hover,
90
+ .bbcode-preview.theme-lyne a:has(img):hover,
91
+ .bbcode-preview.theme-lyne a:has(.bb-img):hover,
92
+ .bbcode-preview.theme-lyne a:has(svg):hover,
93
+ .bbcode-preview.theme-lyne a.bb-link-img:hover {
94
+ border-bottom: none !important;
95
+ text-decoration: none !important;
96
+ }
97
+
98
+ .bbcode-preview-lyne a img,
99
+ .bbcode-preview-lyne a .bb-img,
100
+ .bbcode-preview.theme-lyne a img,
101
+ .bbcode-preview.theme-lyne a .bb-img {
102
+ vertical-align: middle;
103
+ border: none;
104
+ text-decoration: none;
105
+ }
106
+
55
107
  /* Forum @mentions — inherit the accent link look, slightly bolder so they
56
108
  read as handles rather than plain links. */
57
109
  .bbcode-preview-lyne a.bb-mention,
@@ -1121,3 +1173,62 @@
1121
1173
  background-clip: text !important;
1122
1174
  color: transparent !important;
1123
1175
  }
1176
+
1177
+ /* ─── Lyne (SYNE) Preview Highlight Overrides ────────────────────────────── */
1178
+
1179
+ .theme-lyne [data-bbid]:hover:not(:has([data-bbid]:hover, [data-node-id]:hover)),
1180
+ .bbcode-preview-lyne [data-bbid]:hover:not(:has([data-bbid]:hover, [data-node-id]:hover)) {
1181
+ outline: 1.5px solid #2EE6E2 !important;
1182
+ background-color: rgba(46, 230, 226, 0.1) !important;
1183
+ box-shadow: 0 0 8px rgba(46, 230, 226, 0.25) !important;
1184
+ }
1185
+
1186
+ .theme-lyne[data-hover-highlight="on"] [data-bbid]:hover:not(:has([data-bbid]:hover, [data-node-id]:hover)),
1187
+ .bbcode-preview-lyne[data-hover-highlight="on"] [data-bbid]:hover:not(:has([data-bbid]:hover, [data-node-id]:hover)),
1188
+ .theme-lyne[data-hover-highlight="on"] [data-node-id]:hover:not(:has([data-bbid]:hover, [data-node-id]:hover)),
1189
+ .bbcode-preview-lyne[data-hover-highlight="on"] [data-node-id]:hover:not(:has([data-bbid]:hover, [data-node-id]:hover)) {
1190
+ outline: 1px dashed rgba(46, 230, 226, 0.55) !important;
1191
+ box-shadow: 0 0 6px rgba(46, 230, 226, 0.2) !important;
1192
+ }
1193
+
1194
+ .theme-lyne [data-node-id].cursor-highlight,
1195
+ .bbcode-preview-lyne [data-node-id].cursor-highlight,
1196
+ .theme-lyne .cursor-highlight,
1197
+ .bbcode-preview-lyne .cursor-highlight {
1198
+ outline: 2px solid #2EE6E2 !important;
1199
+ background-color: rgba(46, 230, 226, 0.18) !important;
1200
+ box-shadow: 0 0 12px rgba(46, 230, 226, 0.35) !important;
1201
+ }
1202
+
1203
+ .theme-lyne .bb-empty-line.cursor-highlight,
1204
+ .bbcode-preview-lyne .bb-empty-line.cursor-highlight {
1205
+ background-color: rgba(46, 230, 226, 0.22) !important;
1206
+ outline: 2px solid #2EE6E2 !important;
1207
+ box-shadow: 0 0 8px rgba(46, 230, 226, 0.3) !important;
1208
+ }
1209
+
1210
+ .theme-lyne[data-hover-highlight="on"] .bb-empty-line:hover,
1211
+ .bbcode-preview-lyne[data-hover-highlight="on"] .bb-empty-line:hover {
1212
+ outline: 1px dashed rgba(46, 230, 226, 0.55) !important;
1213
+ background-color: rgba(46, 230, 226, 0.1) !important;
1214
+ }
1215
+
1216
+ .theme-lyne div.bb-block.cursor-highlight::after,
1217
+ .bbcode-preview-lyne div.bb-block.cursor-highlight::after {
1218
+ box-shadow: inset 0 0 0 2px #2EE6E2, 0 0 10px rgba(46, 230, 226, 0.35) !important;
1219
+ background-color: rgba(46, 230, 226, 0.18) !important;
1220
+ }
1221
+
1222
+ .theme-lyne span.bb-block.cursor-highlight,
1223
+ .bbcode-preview-lyne span.bb-block.cursor-highlight {
1224
+ outline: 2px solid #2EE6E2 !important;
1225
+ background-color: rgba(46, 230, 226, 0.18) !important;
1226
+ box-shadow: 0 0 8px rgba(46, 230, 226, 0.3) !important;
1227
+ }
1228
+
1229
+ .theme-lyne ::selection,
1230
+ .bbcode-preview-lyne ::selection {
1231
+ background-color: rgba(46, 230, 226, 0.3) !important;
1232
+ color: #ffffff !important;
1233
+ }
1234
+
@@ -33,6 +33,28 @@
33
33
  color: #94C8EA;
34
34
  border-bottom: 1px solid rgba(126,184,224,0.7);
35
35
  }
36
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a:has(img),
37
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a:has(.bb-img),
38
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a:has(svg),
39
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a.bb-link-img {
40
+ border-bottom: none !important;
41
+ text-decoration: none !important;
42
+ display: inline-block;
43
+ vertical-align: middle;
44
+ }
45
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a:has(img):hover,
46
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a:has(.bb-img):hover,
47
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a:has(svg):hover,
48
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a.bb-link-img:hover {
49
+ border-bottom: none !important;
50
+ text-decoration: none !important;
51
+ }
52
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a img,
53
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a .bb-img {
54
+ vertical-align: middle;
55
+ border: none;
56
+ text-decoration: none;
57
+ }
36
58
 
37
59
  /* ─── Headings ───────────────────────────────────────── */
38
60
 
@@ -99,13 +121,22 @@
99
121
  /* ─── Spoiler ────────────────────────────────────────── */
100
122
 
101
123
  .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) .spoiler {
102
- background: #000;
103
- color: #000;
104
- cursor: text;
124
+ background: #111111;
125
+ color: #111111;
126
+ cursor: pointer;
127
+ padding: 0 4px;
128
+ border-radius: 3px;
129
+ user-select: text;
130
+ transition: background 0.15s ease, color 0.15s ease;
131
+ }
132
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) .spoiler:hover,
133
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) .spoiler:active {
134
+ color: #ffffff;
135
+ background: #333333;
105
136
  }
106
137
  .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) .spoiler::selection {
107
- color: #fff;
108
- background: #555;
138
+ color: #ffffff;
139
+ background: #555555;
109
140
  }
110
141
 
111
142
  /* ─── Box / Spoilerbox ───────────────────────────────── */
package/src/index.ts CHANGED
@@ -23,6 +23,9 @@ export type {
23
23
 
24
24
  // ── Lexer ──
25
25
  export { Lexer, type LexerOptions } from './Lexer/Lexer'
26
+ // Which tags carry literal content. The lexer is the only authority on it;
27
+ // exported so consumers stop keeping their own copy.
28
+ export { BBCODE_RAW_TAGS } from './Lexer/BBCodeLexer'
26
29
  export type { Token, TokenKind, Trivia, TriviaKind } from './Types/tokens'
27
30
 
28
31
  // ── Green/Red Tree ──
@@ -33,27 +36,89 @@ export { TreeBuilder, type BuildResult } from './Syntax/TreeBuilder'
33
36
  export { NodeMatcher, type MatchResult } from './Syntax/NodeMatcher'
34
37
 
35
38
  // ── Semantic ──
36
- export { SemanticAnalyzer, type AnalyzeResult } from './Semantic/SemanticAnalyzer'
37
- export type { Diagnostic, DiagnosticSeverity, DiagnosticTag } from './Types/diagnostics'
39
+ export { SemanticAnalyzer, type AnalyzeResult, type SemanticAnalyzerOptions, type CrossedTags, type UnknownTag } from './Semantic/SemanticAnalyzer'
40
+ /**
41
+ * Whether an analysis walked the document or only the edit — see
42
+ * {@link AnalyzeResult.scope}.
43
+ *
44
+ * `'full'` every node was validated afresh; `'window'` only the nodes an edit
45
+ * could have changed were, and every other node kept the verdict it already
46
+ * had. The DIAGNOSTICS ARE THE SAME EITHER WAY: this says how they were
47
+ * arrived at, not what they are.
48
+ *
49
+ * Worth reading if you are timing the engine, deciding whether a slow frame
50
+ * was the analysis, or writing a test that must not silently lose the
51
+ * incremental path. NOT worth branching your rendering on — a consumer that
52
+ * treats `'window'` results as partial and merges them into what it already
53
+ * had will double-report, because `AnalyzeResult.diagnostics` is always the
54
+ * whole document's.
55
+ *
56
+ * `AnalyzeResult.window` carries the span that was re-validated on a
57
+ * `'window'` pass, and is `null` on a full one.
58
+ */
59
+ export type { AnalyzeScope } from './Semantic/SemanticAnalyzer'
60
+ /**
61
+ * `AnalyzeResult` plus the id→node index — see
62
+ * {@link SemanticAnalyzer.analyze}. `allNodes` is a getter that builds the
63
+ * index on first read, so a caller that never asks never pays for the walk.
64
+ */
65
+ export type { IndexedAnalyzeResult } from './Semantic/SemanticAnalyzer'
66
+ export type { Diagnostic, DiagnosticSeverity, DiagnosticTag, DiagnosticFix, FixOperation } from './Types/diagnostics'
38
67
 
39
68
  // ── Tag Registry ──
40
69
  export { TagRegistry, type TagDefinition, type TagHandler } from './Model/TagRegistry'
70
+ // El texto plano de un subárbol. Se exporta porque fuera de Quasar nadie puede
71
+ // reimplementarlo bien: `[code]` es literal y los saltos de línea son hojas
72
+ // `spacing` sin texto propio, así que un recorrido ingenuo los pierde.
73
+ export { extractTextContent } from './Model/TagRegistry'
41
74
  export { NodeFactory } from './Model/NodeFactory'
42
75
 
43
76
  // ── Incremental ──
44
77
  export { IncrementalParser, type EditOperation } from './Incremental/IncrementalParser'
45
78
  export { ChangeTracker, type TextChange } from './Incremental/ChangeTracker'
79
+ /**
80
+ * A half-open span of the source, `[start, end)`, in the coordinates of the
81
+ * text whose result carries it.
82
+ *
83
+ * The same shape everywhere it appears — {@link ReparseResult.window},
84
+ * `AnalyzeResult.window`, `DocumentModel.lastReparseWindow` — because it
85
+ * always means the same thing: the part of the CURRENT text that the engine
86
+ * actually looked at.
87
+ */
88
+ export type { SourceSpan } from './Incremental/IncrementalParser'
89
+ /**
90
+ * What a reparse did, including {@link ReparseResult.window} — the span of the
91
+ * new text that went through the parser, or `null` after a full rebuild,
92
+ * where the answer is "all of it".
93
+ *
94
+ * Everything outside that span came across from the previous tree by
95
+ * reference, so a consumer holding per-node state (a rendered block, a
96
+ * measured height, a decoration) can keep it for every node the span does not
97
+ * touch. `DocumentModel.lastReparseWindow` is the same value for the model's
98
+ * last edit, which is usually the easier place to read it.
99
+ */
100
+ export type { ReparseResult } from './Incremental/IncrementalParser'
101
+ /**
102
+ * Why a reparse rebuilt the whole document instead of splicing a window.
103
+ *
104
+ * Useful when a document is unexpectedly slow to edit: a reason that repeats
105
+ * on every keystroke is a document shape the window guards will not accept —
106
+ * an unclosed `[` before the caret (`open-bracket-before`), a stray closing
107
+ * tag inside the window (`region-not-isolated`), an edit that changes what the
108
+ * parser leaves pending for the text after it (`pending-auto-close`).
109
+ */
110
+ export type { FallbackReason } from './Incremental/IncrementalParser'
46
111
 
47
112
  // ── Diff ──
48
113
  export { TreeDiffer, type DiffOperation, type DiffKind } from './Diff/TreeDiffer'
49
114
 
50
115
  // ── Visitors ──
51
116
  export { Visitor, type VisitorContext } from './Visitors/Visitor'
52
- export { BBCodeExporter, MILIASTRY_ONLY_TAGS, LYNE_ONLY_TAGS, type ExportTarget } from './Visitors/BBCodeExporter'
117
+ export { BBCodeExporter, MILIASTRY_ONLY_TAGS, LYNE_ONLY_TAGS, type ExportTarget, type BBCodeExporterOptions, type BBCodeExportOptions } from './Visitors/BBCodeExporter'
53
118
  export { BBBlocksExporter, type UIBBBlock } from './Visitors/BBBlocksExporter'
54
119
  export { HTMLRenderer, type HTMLRendererOptions } from './Visitors/HTMLRenderer'
55
120
  export { morphHTML } from './Visitors/DOMMorpher'
56
- export { patchBlocksInto } from './Visitors/BlockPatcher'
121
+ export { patchBlocksInto, clearPatchCache, type PatchBlocksOptions, type PatchBlocksStats } from './Visitors/BlockPatcher'
57
122
  export { SVGRenderer } from './Visitors/SVGRenderer'
58
123
  export { MarkdownExporter } from './Visitors/MarkdownExporter'
59
124
  export { JSONExporter } from './Visitors/JSONExporter'
@@ -94,6 +159,15 @@ export { RenderTree, type RenderNode, type RenderVariant } from './RenderPipelin
94
159
  export { PluginAPI } from './Plugins/PluginAPI'
95
160
  export { PluginRegistry, type PluginManifest, type PluginContribution } from './Plugins/PluginRegistry'
96
161
 
162
+ // ── Repair ──
163
+ export { repairNesting, type NestingRepair, type OrphanCloser, type UnclosedOpener } from './Repair/NestingRepair'
164
+
165
+ // ── Reconciler ──
166
+ export { reconcileVisualDOMToBBCode, computeTextDelta, type SurgicalEdit as QuasarSurgicalEdit, type ReconcileResult } from './Reconciler/SurgicalReconciler'
167
+
168
+ // ── BBCode Optimizer (edit plan, conflict contract, rules) ──
169
+ export * from './Edits'
170
+
97
171
  // ── BBCode Bridge ──
98
172
  export { BBCodeDocumentModel } from './BBCode/BBCodeDocumentModel'
99
173
  export type { BBCodeDocumentModelOptions } from './BBCode/BBCodeDocumentModel'
@@ -139,6 +213,9 @@ export { RainbowTransformer, type RainbowOptions } from './Transformers/RainbowT
139
213
  export { GradientTransformer, type GradientOptions } from './Transformers/GradientTransformer'
140
214
  export { GrowTransformer, type GrowOptions } from './Transformers/GrowTransformer'
141
215
 
216
+ // ── MilHibri Unified Hybrid Language ──
217
+ export { MilHibriDocumentModel, type MilHibriDocumentModelOptions } from './MilHibri/MilHibriDocumentModel'
218
+
142
219
  // ── Markdown Bridge ──
143
220
  export { MarkdownDocumentModel } from './Markdown/MarkdownDocumentModel'
144
221
  export { markdownAstToGreenTree, markdownAstToRedTree } from './Markdown/MarkdownToGreenNode'
@@ -147,6 +224,61 @@ export { markdownAstToGreenTree, markdownAstToRedTree } from './Markdown/Markdow
147
224
  export { HTMLDocumentModel } from './HTML/HTMLDocumentModel'
148
225
  export { htmlStringToGreenTree } from './HTML/HTMLToGreenNode'
149
226
 
227
+ // ── Colour Maths ──
228
+ // Perceptual colour utilities. Previously reachable only through the `./src/*`
229
+ // subpath, which resolves against source rather than `dist` and so breaks for
230
+ // any consumer of the built package.
231
+ export {
232
+ ease,
233
+ solveCubicBezierY,
234
+ hslToHex,
235
+ hexToRgb,
236
+ hexToHsl,
237
+ hexToOklab,
238
+ mixHex,
239
+ mixHexOklab,
240
+ mixMultiple,
241
+ mixMultipleStops,
242
+ perceptualDistance,
243
+ } from './Utils/ColorMath'
244
+ export type { Easing, ColorStop } from './Utils/ColorMath'
245
+ export { isHexColor } from './Utils/ColorMath'
246
+
247
+ // ── Effect math: the kernel shared by the tag handlers, the HTML
248
+ // renderer and @miliastry/quasar-studio. ──
249
+ export {
250
+ hashSeed, randAt, valueNoise, fbm,
251
+ validateExpression, compileExpression, EXPRESSION_VARS,
252
+ waveform, WAVE_KINDS, DEFAULT_WAVE_OPTIONS, clamp01,
253
+ blendHex, BLEND_MODES, rgbToHex, adjustHsl, posterizeHex, clampRange,
254
+ buildSampleTable, buildRangeScope, documentScope, axisValue, expressionVars, AXES,
255
+ effectiveAxis, mergeStyledSegments, normalizeHex,
256
+ parseColorStops, stringifyColorStops, parseEffectParams, stringifyEffectParams,
257
+ EFFECT_PARAM_KEYS, EFFECT_UNITS,
258
+ evaluateEffect, GRADIENT_DEFAULTS, RAINBOW_DEFAULTS, GROW_DEFAULTS, NEUTRAL_SIZE,
259
+ PAINT_DEFAULTS, SPATIAL_DEFAULTS,
260
+ // ── Placeable geometry, masks and paint grids ──
261
+ SPATIAL_AXES, DEFAULT_CELL_ASPECT, DEFAULT_SPATIAL, spatialPoint,
262
+ MASK_SHAPES, DEFAULT_MASK, maskValue, maskDistance,
263
+ spatialFromParams, maskFromParams, gridFromParams,
264
+ PAINT_MAX_COLORS, parsePaintGrid, samplePaintGrid,
265
+ stringifyPaintCells, stringifyPaintPalette,
266
+ } from './Utils/EffectMath'
267
+ export type {
268
+ WaveKind, WaveOptions, BlendMode, ExpressionVars, CompiledExpression,
269
+ CharSample, SampleTable, RangeScope, SampleContext, Axis,
270
+ EffectParams, EffectUnit, EffectKind, EffectSpan, StyledSegment,
271
+ SpatialOptions, SpatialPoint, MaskShape, MaskOptions, PaintGrid,
272
+ } from './Utils/EffectMath'
273
+
274
+ // ── Tag Vocabulary ──
275
+ export {
276
+ attributeVocabularyFor, allAttributeVocabularies,
277
+ EFFECT_TYPES, ANIM_TYPES, CONTAINER_TYPES,
278
+ SEPARATOR_VARIANTS, TABLE_FLAGS, IMG_MODIFIERS,
279
+ } from './Utils/TagVocabulary'
280
+ export type { AttributeVocabulary, VocabularySyntax } from './Utils/TagVocabulary'
281
+
150
282
  // ── Analysis Framework ──
151
283
  export {
152
284
  Pipeline,
@@ -157,15 +289,21 @@ export {
157
289
  CharacterCountAnalyzer,
158
290
  MergeableColorAnalyzer,
159
291
  GradientAnalyzer,
292
+ findCollapsibleGradients,
293
+ formatGradientTag,
160
294
  RainbowAnalyzer,
161
295
  WaveAnalyzer,
296
+ SymbolAnalyzer,
297
+ ColorUsageAnalyzer,
162
298
  DefaultDecision,
299
+ PaletteRemapDecision,
163
300
  CollapseGradientTransform,
164
301
  RainbowCollapseTransform,
165
302
  WaveCollapseTransform,
166
303
  MergeColorsTransform,
167
304
  } from './Analysis/index'
168
305
  export type {
306
+ CollapsibleGradient,
169
307
  Pass,
170
308
  AnalyzerPass,
171
309
  DecisionPass,
@@ -187,11 +325,40 @@ export type {
187
325
  RainbowDiagnostics,
188
326
  WaveModel,
189
327
  WaveDiagnostics,
328
+ SymbolGlyph,
329
+ SymbolRunModel,
330
+ ColorUsageModel,
331
+ Palette,
332
+ PaletteRemapOptions,
333
+ RemapAction,
190
334
  } from './Analysis/index'
191
335
 
192
336
  // NOTE: `ExportTarget` from Analysis is intentionally omitted to avoid
193
337
  // naming collision with the existing `ExportTarget` from Visitors/BBCodeExporter.
194
338
  // Consumers who need the Analysis export target can import directly:
195
- // `import { ExportTarget } from '@miliastry/quasar/Analysis/Contracts/PipelineContext'`
339
+ // `import { ExportTarget } from '@miliastry/quasar/src/Analysis/Contracts/PipelineContext'`
340
+ // The `src/` segment is not optional: `./src/*` is the subpath the exports map
341
+ // actually publishes, and the deep specifier without it resolves to nothing.
196
342
 
343
+ // Semántica de atributos de etiqueta, compartida con quien pinte BBCode
344
+ // fuera del HTMLRenderer (los presets del lienzo, por ejemplo).
345
+ export {
346
+ nodeAttrValue,
347
+ parseImgAttr,
348
+ sanitizeColor,
349
+ sanitizeFontSize,
350
+ sanitizeFontFamily,
351
+ } from './Syntax/nodeAttr'
352
+ export type { ImgAttr } from './Syntax/nodeAttr'
353
+
354
+ // ── Design Tokens ──
355
+ export {
356
+ toTokenResolver,
357
+ resolveTokenValue,
358
+ } from './Tokens/index'
359
+ export type {
360
+ DocumentTokens,
361
+ TokenResolverFn,
362
+ TokenSource,
363
+ } from './Tokens/index'
197
364