@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
@@ -252,18 +252,18 @@ export class GreenNodePool {
252
252
  }
253
253
  }
254
254
 
255
- /** Convenience helper for interning green leaves */
255
+ /**
256
+ * Convenience helper for interning green leaves, over the shared singleton.
257
+ *
258
+ * The internal-node twin of this helper used to sit alongside it and was
259
+ * removed: nothing imported it, and it could not have worked if anything had.
260
+ * `GreenNodePool.instance` is built with the default `'leaves'` mode, and
261
+ * `internNode` returns a fresh `GreenNode` unconditionally in that mode — so
262
+ * the one thing the helper existed for, structural sharing of internal nodes,
263
+ * was unreachable through the only pool it could reach. Interning internal
264
+ * nodes is done by passing a `GreenNodePool.create('full')` to the parser,
265
+ * which is what `BBCodeDocumentModel` does per document.
266
+ */
256
267
  export function internGreenLeaf(kind: string, text: string, width = text.length): GreenNode {
257
268
  return GreenNodePool.instance.internLeaf(kind, text, width)
258
269
  }
259
-
260
- /** Convenience helper for interning green nodes */
261
- export function internGreenNode(
262
- kind: string,
263
- text: string,
264
- children: GreenNode[],
265
- leadingWidth = 0,
266
- trailingWidth = 0,
267
- ): GreenNode {
268
- return GreenNodePool.instance.internNode(kind, text, children, leadingWidth, trailingWidth)
269
- }
@@ -175,6 +175,22 @@ export class RedNode {
175
175
  }
176
176
  node._range.start += delta
177
177
  node._range.end += delta
178
+ RedNode.shiftDescendants(node, delta)
179
+ }
180
+
181
+ /**
182
+ * Push `delta` into every red node that lives under `node` — its title nodes
183
+ * as well as its children.
184
+ *
185
+ * The title subtree of a rich `[box=[b]heading[/b]]` is stored in
186
+ * `metadata.titleNodes`, never appended to `children`, so a loop over
187
+ * `children` alone leaves it at pre-edit offsets. That is not cosmetic:
188
+ * `findNodeAtOffset` consults `metadata.titleNodes` BEFORE `children`, so a
189
+ * stale title range mis-resolves caret, hover and selection inside the
190
+ * heading. Both shift paths — the recursive one and `materialize`'s ancestor
191
+ * loop — route through here so they cannot drift apart again.
192
+ */
193
+ private static shiftDescendants(node: RedNode, delta: number): void {
178
194
  const titleNodes = node.metadata?.titleNodes as RedNode[] | undefined
179
195
  if (titleNodes) {
180
196
  for (let i = 0; i < titleNodes.length; i++) {
@@ -223,9 +239,7 @@ export class RedNode {
223
239
  ancestor._lazyShift = 0
224
240
  ancestor._range.start += delta
225
241
  ancestor._range.end += delta
226
- for (let c = 0; c < ancestor.children.length; c++) {
227
- RedNode.applyShift(ancestor.children[c], delta)
228
- }
242
+ RedNode.shiftDescendants(ancestor, delta)
229
243
  }
230
244
  }
231
245
 
@@ -145,9 +145,17 @@ export class RedNodeStore {
145
145
 
146
146
  /**
147
147
  * Check if a GreenNode already has a canonical RedNode.
148
+ *
149
+ * Mirrors `getOrCreate`'s discriminator, including the identity check. A
150
+ * present hash entry only proves *some* structure registered under this
151
+ * 32-bit hash; if it is a different green, `getOrCreate` counts a collision
152
+ * and builds an unregistered node, so answering `true` here would be the
153
+ * opposite of the truth for any caller deciding whether canonicalization has
154
+ * already happened.
148
155
  */
149
156
  has(green: GreenNode): boolean {
150
- return this.canonicals.has(green._hash.toString())
157
+ const existing = this.canonicals.get(green._hash.toString())
158
+ return existing !== undefined && existing.green === green
151
159
  }
152
160
 
153
161
  /**
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Cómo se lee el atributo de una etiqueta BBCode, y qué valores se aceptan.
3
+ *
4
+ * ── Por qué esto vive fuera del renderizador ───────────────────────────────
5
+ *
6
+ * Era privado de `HTMLRenderer`, así que cualquier otro consumidor que
7
+ * necesitara el mismo dato tenía que reinventarlo — y se reinventó mal. Los
8
+ * presets del lienzo de BBCodeCanvas leían `node.text` en crudo, con el `=`
9
+ * pegado delante: `[color=#ff0000]` acababa produciendo `color: "=#ff0000"`,
10
+ * que es CSS inválido, así que el color no se pintaba en absoluto. Lo mismo con
11
+ * `font_size` y `url`.
12
+ *
13
+ * No es un helper nuevo: es el mismo código, movido a donde los dos lo ven. Si
14
+ * el renderizador y el lienzo vuelven a divergir, al menos divergirán desde el
15
+ * mismo sitio.
16
+ */
17
+
18
+ import type { RedNode } from './RedNode'
19
+ import type { TokenResolverFn } from '../Tokens'
20
+
21
+ /** #rgb / #rgba / #rrggbb / #rrggbbaa, una palabra clave CSS, o rgb()/hsl(). */
22
+ const CSS_COLOR_RE =
23
+ /^(?:#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})|[a-z]{3,20}|(?:rgb|hsl)a?\([0-9a-z.,%\s/+-]{1,64}\))$/i
24
+
25
+ /** Número desnudo — se interpola como porcentaje. */
26
+ const CSS_SIZE_RE = /^\d{1,4}(?:\.\d{1,2})?$/
27
+
28
+ /** Lista de familias. Las comillas se rechazan; sin comillas es CSS válido. */
29
+ const CSS_FONT_RE = /^[A-Za-z0-9 ,_-]{1,120}$/
30
+
31
+ const BARE_HEX_RE = /^[0-9a-f]{3,8}$/i
32
+
33
+ /**
34
+ * El valor del atributo de un nodo.
35
+ *
36
+ * Los atributos BBCode vienen como `=VALOR` (`=#61afef`, `="Autor"`,
37
+ * `=https://osu.ppy.sh`): se quita el `=` y las comillas envolventes. Una
38
+ * etiqueta sin atributo —el contenido de un `[img]`, por ejemplo— devuelve su
39
+ * texto tal cual.
40
+ *
41
+ * Con `key`, gana lo que haya en `metadata`: es lo que escribe el Inspector al
42
+ * editar una propiedad, y tiene que pesar más que el texto original del
43
+ * documento, que todavía no se ha reescrito.
44
+ */
45
+ export function nodeAttrValue(node: RedNode, key?: string): string {
46
+ if (key) {
47
+ const meta = node.metadata?.[key]
48
+ if (typeof meta === 'string' && meta) return meta
49
+ }
50
+
51
+ const text = node.text || ''
52
+ if (!text) return ''
53
+
54
+ const eqIdx = text.indexOf('=')
55
+ if (eqIdx < 0) return text
56
+
57
+ let value = text.slice(eqIdx + 1)
58
+ if (
59
+ (value.startsWith('"') && value.endsWith('"')) ||
60
+ (value.startsWith("'") && value.endsWith("'"))
61
+ ) {
62
+ value = value.slice(1, -1)
63
+ }
64
+ return value
65
+ }
66
+
67
+ /**
68
+ * Un color que se puede meter en un `style`, o `null`.
69
+ *
70
+ * Devolver `null` en vez de el valor crudo no es remilgo: `[color=red;"
71
+ * onmouseover="alert(1)]` cierra el atributo e inyecta un manejador de eventos.
72
+ * Escapar solo neutralizaría la inyección, pero dejaría CSS roto, así que se
73
+ * valida la FORMA y se descarta lo que no sea un valor que quisiéramos admitir.
74
+ *
75
+ * Un hex sin `#` se completa: `[color=ff0000]` es como lo escribe medio mundo.
76
+ */
77
+ export function sanitizeColor(raw: string, resolver?: TokenResolverFn): string | null {
78
+ // Atajo para `#RRGGBB`, que es la forma que produce el propio motor y con
79
+ // la que llega la inmensa mayoría de las llamadas. Un degradado sin
80
+ // fusionar emite UN SEGMENTO POR CARÁCTER y cada uno pasaba por `trim`,
81
+ // dos expresiones regulares y el `startsWith` de los tokens. La comprobación
82
+ // por códigos de carácter acepta exactamente el mismo conjunto que
83
+ // `CSS_COLOR_RE` para siete caracteres, así que la garantía no cambia:
84
+ // sigue siendo imposible que salga de aquí algo que no sea un color.
85
+ if (raw.length === 7 && raw.charCodeAt(0) === 35 /* # */ && isPlainHex6(raw)) return raw
86
+
87
+ let v = raw.trim()
88
+ if (!v) return null
89
+ if (v.startsWith('$') && resolver) {
90
+ const resolved = resolver(v.slice(1)) ?? resolver(v)
91
+ if (resolved !== undefined) {
92
+ v = resolved.trim()
93
+ }
94
+ }
95
+ if (BARE_HEX_RE.test(v) && (v.length === 3 || v.length === 4 || v.length === 6 || v.length === 8)) {
96
+ v = '#' + v
97
+ }
98
+ return CSS_COLOR_RE.test(v) ? v : null
99
+ }
100
+
101
+ /** `true` si `s[1..7]` son seis dígitos hexadecimales. Sin asignar nada. */
102
+ function isPlainHex6(s: string): boolean {
103
+ for (let i = 1; i < 7; i++) {
104
+ const c = s.charCodeAt(i)
105
+ const isDigit = c >= 0x30 && c <= 0x39
106
+ const isLower = c >= 0x61 && c <= 0x66
107
+ const isUpper = c >= 0x41 && c <= 0x46
108
+ if (!isDigit && !isLower && !isUpper) return false
109
+ }
110
+ return true
111
+ }
112
+
113
+ /** El tamaño de `[size=…]` como número, o `null`. Se interpola en `%`. */
114
+ export function sanitizeFontSize(raw: string, resolver?: TokenResolverFn): string | null {
115
+ let v = raw.trim()
116
+ if (v.startsWith('$') && resolver) {
117
+ const resolved = resolver(v.slice(1)) ?? resolver(v)
118
+ if (resolved !== undefined) {
119
+ v = resolved.trim()
120
+ if (v.endsWith('%')) {
121
+ v = v.slice(0, -1).trim()
122
+ }
123
+ }
124
+ }
125
+ return CSS_SIZE_RE.test(v) ? v : null
126
+ }
127
+
128
+ /** La familia de `[font=…]`, o `null`. */
129
+ export function sanitizeFontFamily(raw: string, resolver?: TokenResolverFn): string | null {
130
+ let v = raw.trim()
131
+ if (v.startsWith('$') && resolver) {
132
+ const resolved = resolver(v.slice(1)) ?? resolver(v)
133
+ if (resolved !== undefined) {
134
+ v = resolved.trim()
135
+ if (
136
+ (v.startsWith('"') && v.endsWith('"')) ||
137
+ (v.startsWith("'") && v.endsWith("'"))
138
+ ) {
139
+ v = v.slice(1, -1).trim()
140
+ }
141
+ }
142
+ }
143
+ return CSS_FONT_RE.test(v) ? v : null
144
+ }
145
+
146
+ /** Lo que puede significar el atributo de un `[img=…]`. */
147
+ export interface ImgAttr {
148
+ w?: number
149
+ h?: number
150
+ round?: boolean
151
+ shadow?: boolean
152
+ float?: boolean
153
+ }
154
+
155
+ /**
156
+ * El modificador de una imagen: `[img=200x100]`, `[img=round]`, `[img=shadow]`,
157
+ * `[img=float]`.
158
+ *
159
+ * Vive aquí y no en el renderizador porque es interpretación del ATRIBUTO, no
160
+ * de cómo se pinta: quien quiera respetar un `round` necesita este mismo
161
+ * significado, y hasta ahora el lienzo simplemente lo ignoraba.
162
+ */
163
+ export function parseImgAttr(v: string | null): ImgAttr {
164
+ if (!v) return {}
165
+ const t = v.trim().toLowerCase()
166
+ if (t === 'round') return { round: true }
167
+ if (t === 'shadow') return { shadow: true }
168
+ if (t === 'float') return { float: true }
169
+ const m = /^(\d{1,4})x(\d{1,4})$/i.exec(t)
170
+ if (m) return { w: Math.min(2000, parseInt(m[1], 10)), h: Math.min(2000, parseInt(m[2], 10)) }
171
+ return {}
172
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Quasar Document Engine — Design Tokens
3
+ *
4
+ * Provides token types and normalization utilities for design token resolution.
5
+ * Completely independent of @miliastry/core.
6
+ */
7
+
8
+ export interface DocumentTokens {
9
+ palette?: Record<string, string>
10
+ variables?: Record<string, string>
11
+ }
12
+
13
+ export type TokenResolverFn = (name: string) => string | undefined
14
+ export type TokenSource = DocumentTokens | TokenResolverFn
15
+
16
+ /**
17
+ * Normalizes a TokenSource into a TokenResolverFn.
18
+ *
19
+ * For DocumentTokens, checks palette[name] ?? variables[name], handling both
20
+ * with and without leading `$`.
21
+ * If source is already a function, passes it through.
22
+ */
23
+ export function toTokenResolver(source?: TokenSource): TokenResolverFn | undefined {
24
+ if (!source) return undefined
25
+ if (typeof source === 'function') return source
26
+
27
+ return (name: string): string | undefined => {
28
+ const bare = name.startsWith('$') ? name.slice(1) : name
29
+ const dollar = '$' + bare
30
+ const palette = source.palette
31
+ const variables = source.variables
32
+
33
+ return (
34
+ palette?.[name] ??
35
+ palette?.[bare] ??
36
+ palette?.[dollar] ??
37
+ variables?.[name] ??
38
+ variables?.[bare] ??
39
+ variables?.[dollar]
40
+ )
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Resolves a token value if val starts with `$`.
46
+ * Strips `$` and looks up in resolver. If found, returns resolved value; otherwise returns val.
47
+ */
48
+ export function resolveTokenValue(val: string, resolver?: TokenResolverFn): string {
49
+ if (!resolver || !val.startsWith('$')) {
50
+ return val
51
+ }
52
+ const tokenName = val.slice(1)
53
+ const resolved = resolver(tokenName) ?? resolver(val)
54
+ return resolved !== undefined ? resolved : val
55
+ }
@@ -0,0 +1 @@
1
+ export * from './TokenResolver'
package/src/Types/core.ts CHANGED
@@ -92,8 +92,23 @@ export type NodeKind =
92
92
  | 'grow'
93
93
  | 'sinewave'
94
94
  | 'rainbow'
95
+ | 'paint'
95
96
  | 'spacing'
96
97
  | 'empty_line'
98
+ /**
99
+ * Una etiqueta de cierre que no cerró nada y que no debe verse.
100
+ *
101
+ * Nace cuando un cierre llega tarde: su etiqueta ya se cerró sola porque un
102
+ * cierre anterior pasó por encima de ella (ver `Parser.ts`, modo legacy).
103
+ * osu! tira esos cierres, así que el renderer los omite.
104
+ *
105
+ * Existe como nodo, y no como texto, por dos razones. Conserva su rango, que
106
+ * es lo que necesita el parser incremental para saber a qué nodo pertenece
107
+ * cada carácter. Y el exportador puede saltárselo: escrito como texto volvía
108
+ * a salir por el otro lado convertido en etiqueta viva, y el documento no
109
+ * era estable al reexportarlo.
110
+ */
111
+ | 'discarded_tag'
97
112
  | 'group'
98
113
  | 'wnotice'
99
114
  | 'align'