@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
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Quasar — Enumerated attribute vocabularies
3
+ *
4
+ * Some tags take an attribute that is not free text: `[effect=glow]`,
5
+ * `[anim=glitch]`, `[container=grid]`, `[separator=dots]`. The set of values
6
+ * each one accepts was, until now, knowable only by reading the `switch` in
7
+ * `HTMLRenderer` — so the editor could offer tag names but never values, and
8
+ * an author had to remember thirty-six magic words.
9
+ *
10
+ * ── Why this is a problem worth a module ──
11
+ *
12
+ * Every one of those switches ends in a `default:`, and none of the defaults
13
+ * is an error. A misspelling does not fail; it renders something else:
14
+ *
15
+ * [anim=glich] → `bb-pulse` (a different animation)
16
+ * [container=gird] → `bb-stack` (a different container)
17
+ * [effect=nen] → a bare span (no effect at all)
18
+ * [img=round,shadow]→ ignored (`parseImgAttr` is exclusive, not flags)
19
+ *
20
+ * The author sees a preview that works and never learns the value was wrong.
21
+ * Offering the vocabulary at the point of typing is the cheapest fix, and it
22
+ * needs the vocabulary to exist as data rather than as control flow.
23
+ *
24
+ * ── Why here and not in the editor ──
25
+ *
26
+ * This is the engine's knowledge: the renderer decides what `glow` means, so
27
+ * the renderer's package owns the list of what is spellable. A copy in the
28
+ * editor would be a fourth transcription of an engine fact, which is the
29
+ * exact mistake `TagSyntax` and `EffectMath` were both written to undo.
30
+ *
31
+ * `TagVocabulary.test.ts` pins every value below to the renderer: a value
32
+ * that stops being handled starts rendering as the fallback, and the test
33
+ * fails. The list cannot silently drift away from the behaviour.
34
+ *
35
+ * Human-readable descriptions are deliberately NOT here. Quasar has no
36
+ * locales; the editor names these values through its own i18n.
37
+ */
38
+
39
+ /** How an attribute's value is written. */
40
+ export type VocabularySyntax =
41
+ /** Exactly one of `values`. */
42
+ | 'enum'
43
+ /** Any subset of `values`, comma- or space-separated. */
44
+ | 'flags'
45
+
46
+ export interface AttributeVocabulary {
47
+ /** The tag whose attribute this describes. */
48
+ tag: string
49
+ /** Every value the engine actually handles, in the renderer's own order. */
50
+ values: readonly string[]
51
+ syntax: VocabularySyntax
52
+ /**
53
+ * The value an unrecognised attribute behaves as, or `null` when an
54
+ * unrecognised attribute simply does nothing.
55
+ *
56
+ * This is the honest part: it is what the `default:` branch does, not what
57
+ * the documentation wishes it did.
58
+ */
59
+ fallback: string | null
60
+ /**
61
+ * Whether a `:<param>` suffix is read after the value.
62
+ *
63
+ * What the suffix MEANS is per value, not per tag, which is why this is
64
+ * only a boolean: `[effect=glow:#2EE6E2]` and `[tables=striped:#hex]` take
65
+ * a colour, but `[container=grid:3]` takes a column count and
66
+ * `[container=flex:12]` a gap in pixels. Completion needs no more than
67
+ * "there is a suffix here, and it is not part of the vocabulary".
68
+ */
69
+ suffixed: boolean
70
+ }
71
+
72
+ /** `[effect=…]` — `HTMLRenderer.renderEffect`. */
73
+ export const EFFECT_TYPES = [
74
+ 'glow', 'neon', 'outline', 'emboss', 'engrave',
75
+ 'shimmer', 'ghost', 'rainbow', 'fire', 'ice',
76
+ ] as const
77
+
78
+ /** `[anim=…]` — `HTMLRenderer.renderAnim`. */
79
+ export const ANIM_TYPES = [
80
+ 'bounce', 'shake', 'pulse', 'fade-in', 'fade-out',
81
+ 'typewriter', 'wave', 'sparkle', 'glitch', 'levitate',
82
+ ] as const
83
+
84
+ /**
85
+ * `[container=…]` — `HTMLRenderer.renderContainer`.
86
+ *
87
+ * `neonbox` is the unhyphenated spelling of `neon-box` and renders
88
+ * identically; it stays in the list because a document may carry it, but the
89
+ * hyphenated form is the canonical one and comes first.
90
+ */
91
+ export const CONTAINER_TYPES = [
92
+ 'stack', 'flex', 'grid', 'middle', 'square', 'circle',
93
+ 'card', 'glass', 'neon-box', 'neonbox',
94
+ ] as const
95
+
96
+ /** `[separator=…]` — `HTMLRenderer.renderSeparator`. */
97
+ export const SEPARATOR_VARIANTS = ['line', 'dots', 'stars'] as const
98
+
99
+ /** `[tables=…]` — `HTMLRenderer.renderTables`. Combinable. */
100
+ export const TABLE_FLAGS = ['striped', 'borders'] as const
101
+
102
+ /**
103
+ * `[img=…]` — `parseImgAttr` in `Syntax/nodeAttr`.
104
+ *
105
+ * Exclusive despite reading like flags: the parser returns on the first
106
+ * match, so `[img=round,shadow]` yields neither. A dimension pair
107
+ * (`[img=200x100]`) is the other accepted form and is not enumerable.
108
+ */
109
+ export const IMG_MODIFIERS = ['round', 'shadow', 'float'] as const
110
+
111
+ const VOCABULARIES: readonly AttributeVocabulary[] = [
112
+ // `effect` is suffixed, but only `glow`, `neon` and `outline` read the
113
+ // colour; the other seven ignore it. `container` is suffixed with a colour
114
+ // for the panels and with a number for `flex` and `grid`.
115
+ { tag: 'effect', values: EFFECT_TYPES, syntax: 'enum', fallback: null, suffixed: true },
116
+ { tag: 'anim', values: ANIM_TYPES, syntax: 'enum', fallback: 'pulse', suffixed: false },
117
+ { tag: 'container', values: CONTAINER_TYPES, syntax: 'enum', fallback: 'stack', suffixed: true },
118
+ { tag: 'separator', values: SEPARATOR_VARIANTS, syntax: 'enum', fallback: 'line', suffixed: false },
119
+ { tag: 'tables', values: TABLE_FLAGS, syntax: 'flags', fallback: null, suffixed: true },
120
+ { tag: 'img', values: IMG_MODIFIERS, syntax: 'enum', fallback: null, suffixed: false },
121
+ ]
122
+
123
+ const BY_TAG = new Map(VOCABULARIES.map(v => [v.tag, v]))
124
+
125
+ /**
126
+ * The vocabulary for a tag's attribute, or `null` when its attribute is free
127
+ * text (`[box=Título]`, `[url=…]`, `[size=200]`).
128
+ *
129
+ * Case-insensitive, because the renderer lowercases before matching.
130
+ *
131
+ * Note this answers only "what can be spelled here"; whether the tag exists
132
+ * at all is a dialect question, and the caller already knows the dialect.
133
+ */
134
+ export function attributeVocabularyFor(tag: string): AttributeVocabulary | null {
135
+ return BY_TAG.get(tag.toLowerCase()) ?? null
136
+ }
137
+
138
+ /** Every vocabulary, for tests and for tooling that enumerates them. */
139
+ export function allAttributeVocabularies(): readonly AttributeVocabulary[] {
140
+ return VOCABULARIES
141
+ }
@@ -17,6 +17,8 @@ export interface DomToSVGOptions {
17
17
  includeBackground?: boolean
18
18
  /** Scale factor applied to all coordinates (default 1) */
19
19
  scale?: number
20
+ /** Maximum unscaled height in pixels to scan and render into the SVG (useful for thumbnails). Elements and lines below this cutoff are omitted. */
21
+ maxHeight?: number
20
22
  }
21
23
 
22
24
  export interface SVGLayerInfo {
@@ -51,7 +53,7 @@ export function domToSVGResult(
51
53
  root: HTMLElement,
52
54
  options: DomToSVGOptions = {},
53
55
  ): DomToSVGResult {
54
- const { backgroundColor = "#0d0d0d", includeBackground = true, scale = 1 } = options
56
+ const { backgroundColor = "#0d0d0d", includeBackground = true, scale = 1, maxHeight } = options
55
57
 
56
58
  // OPTIMIZATION & FIX: Remove visual interaction artifacts before measuring.
57
59
  // We strip the cursor-highlight class synchronously so getComputedStyle reads the pure colors.
@@ -65,8 +67,9 @@ export function domToSVGResult(
65
67
  closedDetails.forEach(el => el.setAttribute('open', ''))
66
68
 
67
69
  const rootRect = root.getBoundingClientRect()
70
+ const unscaledH = maxHeight ? Math.min(rootRect.height, maxHeight) : rootRect.height
68
71
  const W = Math.ceil(rootRect.width * scale)
69
- const H = Math.ceil(rootRect.height * scale)
72
+ const H = Math.ceil(unscaledH * scale)
70
73
 
71
74
  if (W < 4 || H < 4) {
72
75
  // RESTORE on error
@@ -78,7 +81,7 @@ export function domToSVGResult(
78
81
  }
79
82
 
80
83
  try {
81
- const state: WalkState = { rootRect, scale, parts: [], layerCount: 0, layersList: [] }
84
+ const state: WalkState = { rootRect, scale, parts: [], layerCount: 0, layersList: [], maxHeight }
82
85
 
83
86
  const fontFamilies = collectFontFamilies(root)
84
87
 
@@ -141,6 +144,7 @@ interface WalkState {
141
144
  parts: string[]
142
145
  layerCount: number
143
146
  layersList: SVGLayerInfo[]
147
+ maxHeight?: number
144
148
  }
145
149
 
146
150
  // ────────────────────────────────────────────────────────────
@@ -223,21 +227,24 @@ interface VisualLine {
223
227
  * text string on the first line's rect, we emit only the characters that
224
228
  * actually appear on each visual line.
225
229
  */
226
- function getVisualLines(textNode: Text): VisualLine[] {
230
+ function getVisualLines(textNode: Text, maxHeight?: number, rootTop?: number): VisualLine[] {
227
231
  const text = textNode.textContent ?? ""
228
232
  if (!text.trim()) return [] // Skip purely empty/whitespace nodes
229
233
 
230
234
  const range = document.createRange()
231
235
  range.selectNode(textNode)
232
236
  const fullRects = Array.from(range.getClientRects()).filter(r => r.width > 0 && r.height > 0)
233
-
237
+ if (fullRects.length === 0) return []
238
+
239
+ // If entire text node is below cutoff, skip immediately!
240
+ if (rootTop !== undefined && maxHeight !== undefined && fullRects[0].top - rootTop > maxHeight) {
241
+ return []
242
+ }
243
+
234
244
  // OPTIMIZATION 1: If the entire text node fits on a single line, return immediately!
235
245
  // This skips the expensive scan for 90% of text nodes (short inline elements).
236
246
  if (fullRects.length <= 1) {
237
- if (fullRects.length === 1) {
238
- return [{ text, rect: fullRects[0] }]
239
- }
240
- return []
247
+ return [{ text, rect: fullRects[0] }]
241
248
  }
242
249
 
243
250
  // OPTIMIZATION 2: Word-by-word scan instead of char-by-char for multi-line text.
@@ -267,6 +274,10 @@ function getVisualLines(textNode: Text): VisualLine[] {
267
274
  const tokenRect = rects[0]
268
275
  if (tokenRect.width === 0 && tokenRect.height === 0) continue
269
276
 
277
+ if (rootTop !== undefined && maxHeight !== undefined && tokenRect.top - rootTop > maxHeight) {
278
+ break
279
+ }
280
+
270
281
  if (!lineRect) {
271
282
  lineRect = tokenRect
272
283
  lineText = token
@@ -336,7 +347,12 @@ function walkElement(el: Element, state: WalkState, depth: number): void {
336
347
  if (opacityVal === 0) return
337
348
 
338
349
  const rect = el.getBoundingClientRect()
339
- const { rootRect, scale } = state
350
+ const { rootRect, scale, maxHeight } = state
351
+
352
+ const unscaledTop = rect.top - rootRect.top
353
+ if (maxHeight !== undefined && unscaledTop > maxHeight) {
354
+ return
355
+ }
340
356
 
341
357
  const x = (rect.left - rootRect.left) * scale
342
358
  const y = (rect.top - rootRect.top) * scale
@@ -537,8 +553,8 @@ function walkTextNode(textNode: Text, state: WalkState, depth: number): void {
537
553
  const cs = window.getComputedStyle(parent)
538
554
  if (cs.display === "none" || cs.visibility === "hidden") return
539
555
 
540
- // Get correctly-split visual lines via character-scan
541
- const lines = getVisualLines(textNode)
556
+ // Get correctly-split visual lines via character-scan (capped at maxHeight)
557
+ const lines = getVisualLines(textNode, state.maxHeight, state.rootRect.top)
542
558
  if (lines.length === 0) return
543
559
 
544
560
  const { rootRect, scale } = state
@@ -10,12 +10,26 @@ export interface UIBBBlock {
10
10
  content?: string
11
11
  }
12
12
 
13
+ /** Distingue exportadores creados dentro del mismo milisegundo. */
14
+ let exporterSeq = 0
15
+
13
16
  export class BBBlocksExporter extends Visitor<UIBBBlock[]> {
14
17
  private idCounter = 0
15
18
 
19
+ /**
20
+ * Un identificador único dentro de ESTA exportación.
21
+ *
22
+ * Llevaba un `Date.now().toString(36)` por nodo. El reloj no aportaba nada
23
+ * —los identificadores solo tienen que distinguirse entre sí dentro del
24
+ * lote— y se leía, y se convertía a base 36, una vez por nodo del árbol.
25
+ * El prefijo se calcula una sola vez por exportador para que dos lotes
26
+ * consecutivos sigan sin colisionar.
27
+ */
28
+ private readonly idPrefix = `block-${(exporterSeq++).toString(36)}-${Date.now().toString(36)}`
29
+
16
30
  private generateId(): string {
17
31
  this.idCounter++
18
- return `block-${Date.now().toString(36)}-${this.idCounter}`
32
+ return `${this.idPrefix}-${this.idCounter}`
19
33
  }
20
34
 
21
35
  visit(node: RedNode, context?: VisitorContext): UIBBBlock[] {