@miliastry/quasar 1.0.6 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/README.md +1 -1
  2. package/dist/Visuals/lyne.css +374 -9
  3. package/dist/Visuals/osu.css +393 -11
  4. package/dist/index.js +10470 -3336
  5. package/dist/index.mjs +10336 -3337
  6. package/dist/types/Analysis/Contracts/AnalysisReport.d.ts +22 -0
  7. package/dist/types/Analysis/Contracts/Contribution.d.ts +68 -0
  8. package/dist/types/Analysis/Contracts/Pass.d.ts +59 -0
  9. package/dist/types/Analysis/Contracts/PipelineContext.d.ts +34 -0
  10. package/dist/types/Analysis/Passes/Analysis/ColorUsageAnalyzer.d.ts +56 -0
  11. package/dist/types/Analysis/Passes/Analysis/GradientAnalyzer.d.ts +111 -0
  12. package/dist/types/Analysis/Passes/Analysis/MergeableColorAnalyzer.d.ts +26 -0
  13. package/dist/types/Analysis/Passes/Analysis/RainbowAnalyzer.d.ts +47 -0
  14. package/dist/types/Analysis/Passes/Analysis/SymbolAnalyzer.d.ts +89 -0
  15. package/dist/types/Analysis/Passes/Analysis/WaveAnalyzer.d.ts +44 -0
  16. package/dist/types/Analysis/Passes/Analysis/__tests__/GradientAnalyzer.test.d.ts +4 -0
  17. package/dist/types/Analysis/Passes/Analysis/__tests__/MergeableColorAnalyzer.test.d.ts +4 -0
  18. package/dist/types/Analysis/Passes/Analysis/__tests__/RainbowAnalyzer.test.d.ts +4 -0
  19. package/dist/types/Analysis/Passes/Analysis/__tests__/WaveAnalyzer.test.d.ts +4 -0
  20. package/dist/types/Analysis/Passes/Decision/DefaultDecision.d.ts +30 -0
  21. package/dist/types/Analysis/Passes/Decision/PaletteRemapDecision.d.ts +169 -0
  22. package/dist/types/Analysis/Passes/Decision/__tests__/DefaultDecision.test.d.ts +4 -0
  23. package/dist/types/Analysis/Passes/Transform/CollapseGradientTransform.d.ts +37 -0
  24. package/dist/types/Analysis/Passes/Transform/MergeColorsTransform.d.ts +28 -0
  25. package/dist/types/Analysis/Passes/Transform/RainbowCollapseTransform.d.ts +19 -0
  26. package/dist/types/Analysis/Passes/Transform/WaveCollapseTransform.d.ts +20 -0
  27. package/dist/types/Analysis/Passes/Utility/CharacterCountAnalyzer.d.ts +19 -0
  28. package/dist/types/Analysis/Pipeline/Pipeline.d.ts +69 -0
  29. package/dist/types/Analysis/Pipeline/PipelineBuilder.d.ts +31 -0
  30. package/dist/types/Analysis/Pipeline/PipelineStage.d.ts +17 -0
  31. package/dist/types/Analysis/Utils/color-utils.d.ts +60 -0
  32. package/dist/types/Analysis/__tests__/Integration.test.d.ts +8 -0
  33. package/dist/types/Analysis/__tests__/PaletteRemap.test.d.ts +14 -0
  34. package/dist/types/Analysis/__tests__/Pipeline.test.d.ts +10 -0
  35. package/dist/types/Analysis/__tests__/RoundTrip.test.d.ts +30 -0
  36. package/dist/types/Analysis/__tests__/SymbolAnalyzer.test.d.ts +11 -0
  37. package/dist/types/Analysis/index.d.ts +40 -0
  38. package/dist/types/BBCode/BBCodeDocumentModel.d.ts +102 -0
  39. package/dist/types/BBCode/BBCodeToGreenNode.d.ts +111 -0
  40. package/dist/types/BBCode/Parser.d.ts +78 -0
  41. package/dist/types/BBCode/index.d.ts +4 -0
  42. package/dist/types/Collab/positions.d.ts +55 -0
  43. package/dist/types/Commands/Command.d.ts +37 -0
  44. package/dist/types/Commands/CommandRegistry.d.ts +48 -0
  45. package/dist/types/Commands/DeleteNode.d.ts +7 -0
  46. package/dist/types/Commands/InsertText.d.ts +8 -0
  47. package/dist/types/Commands/SplitMerge.d.ts +9 -0
  48. package/dist/types/Commands/WrapInTag.d.ts +8 -0
  49. package/dist/types/Commands/index.d.ts +6 -0
  50. package/dist/types/Diff/TreeDiffer.d.ts +52 -0
  51. package/dist/types/Diff/__tests__/TreeDiffer.test.d.ts +1 -0
  52. package/dist/types/Diff/index.d.ts +2 -0
  53. package/dist/types/Edits/EditPlan.d.ts +144 -0
  54. package/dist/types/Edits/Optimizer.d.ts +78 -0
  55. package/dist/types/Edits/Rules/Rule.d.ts +91 -0
  56. package/dist/types/Edits/Rules/dropEmptyTags.d.ts +40 -0
  57. package/dist/types/Edits/Rules/dropRedundantNesting.d.ts +59 -0
  58. package/dist/types/Edits/Rules/index.d.ts +9 -0
  59. package/dist/types/Edits/Rules/mergeAdjacent.d.ts +93 -0
  60. package/dist/types/Edits/Rules/reorderWrappers.d.ts +42 -0
  61. package/dist/types/Edits/Rules/shortenHex.d.ts +30 -0
  62. package/dist/types/Edits/Rules/tagValue.d.ts +56 -0
  63. package/dist/types/Edits/Rules/unwrapInvisibleColor.d.ts +47 -0
  64. package/dist/types/Edits/applyEdits.d.ts +36 -0
  65. package/dist/types/Edits/index.d.ts +6 -0
  66. package/dist/types/Events/EventBus.d.ts +103 -0
  67. package/dist/types/Events/index.d.ts +2 -0
  68. package/dist/types/Formatter/Formatter.d.ts +32 -0
  69. package/dist/types/Formatter/index.d.ts +2 -0
  70. package/dist/types/HTML/HTMLDocumentModel.d.ts +13 -0
  71. package/dist/types/HTML/HTMLToGreenNode.d.ts +4 -0
  72. package/dist/types/Incremental/BracketIndex.d.ts +101 -0
  73. package/dist/types/Incremental/ChangeTracker.d.ts +69 -0
  74. package/dist/types/Incremental/IncrementalParser.d.ts +246 -0
  75. package/dist/types/Incremental/__tests__/BracketIndex.test.d.ts +1 -0
  76. package/dist/types/Incremental/__tests__/IncrementalParser.test.d.ts +1 -0
  77. package/dist/types/Incremental/index.d.ts +4 -0
  78. package/dist/types/Lexer/BBCodeLexer.d.ts +74 -0
  79. package/dist/types/Lexer/Lexer.d.ts +54 -0
  80. package/dist/types/Lexer/index.d.ts +4 -0
  81. package/dist/types/Linter/Linter.d.ts +58 -0
  82. package/dist/types/Linter/index.d.ts +2 -0
  83. package/dist/types/Markdown/MarkdownAST.d.ts +122 -0
  84. package/dist/types/Markdown/MarkdownDocumentModel.d.ts +15 -0
  85. package/dist/types/Markdown/MarkdownLexer.d.ts +155 -0
  86. package/dist/types/Markdown/MarkdownParser.d.ts +42 -0
  87. package/dist/types/Markdown/MarkdownToGreenNode.d.ts +6 -0
  88. package/dist/types/MilHibri/MilHibriDocumentModel.d.ts +21 -0
  89. package/dist/types/Model/DocumentModel.d.ts +318 -0
  90. package/dist/types/Model/NodeFactory.d.ts +29 -0
  91. package/dist/types/Model/TagRegistry.d.ts +165 -0
  92. package/dist/types/Model/index.d.ts +5 -0
  93. package/dist/types/Plugins/PluginAPI.d.ts +44 -0
  94. package/dist/types/Plugins/PluginRegistry.d.ts +81 -0
  95. package/dist/types/Plugins/index.d.ts +3 -0
  96. package/dist/types/Queries/QueryEngine.d.ts +32 -0
  97. package/dist/types/Queries/index.d.ts +1 -0
  98. package/dist/types/Reconciler/SurgicalReconciler.d.ts +23 -0
  99. package/dist/types/RenderPipeline/RenderPipeline.d.ts +46 -0
  100. package/dist/types/RenderPipeline/RenderTree.d.ts +55 -0
  101. package/dist/types/RenderPipeline/index.d.ts +4 -0
  102. package/dist/types/Repair/NestingRepair.d.ts +48 -0
  103. package/dist/types/Repair/index.d.ts +1 -0
  104. package/dist/types/Semantic/SemanticAnalyzer.d.ts +321 -0
  105. package/dist/types/Semantic/index.d.ts +2 -0
  106. package/dist/types/Symbols/SymbolTable.d.ts +47 -0
  107. package/dist/types/Symbols/index.d.ts +1 -0
  108. package/dist/types/Syntax/GreenNode.d.ts +154 -0
  109. package/dist/types/Syntax/GreenNodePool.d.ts +134 -0
  110. package/dist/types/Syntax/NodeMatcher.d.ts +75 -0
  111. package/dist/types/Syntax/RedNode.d.ts +280 -0
  112. package/dist/types/Syntax/RedNodeStore.d.ts +116 -0
  113. package/dist/types/Syntax/TreeBuilder.d.ts +54 -0
  114. package/dist/types/Syntax/__tests__/GreenNode.test.d.ts +1 -0
  115. package/dist/types/Syntax/__tests__/RedNode.test.d.ts +1 -0
  116. package/dist/types/Syntax/__tests__/RedNodeStore.test.d.ts +1 -0
  117. package/dist/types/Syntax/greenEdit.d.ts +64 -0
  118. package/dist/types/Syntax/hash.d.ts +17 -0
  119. package/dist/types/Syntax/index.d.ts +8 -0
  120. package/dist/types/Syntax/nodeAttr.d.ts +63 -0
  121. package/dist/types/Syntax/partition.d.ts +67 -0
  122. package/dist/types/Syntax/preserveNodeIds.d.ts +47 -0
  123. package/dist/types/Tokens/TokenResolver.d.ts +25 -0
  124. package/dist/types/Tokens/index.d.ts +1 -0
  125. package/dist/types/Transactions/Transaction.d.ts +36 -0
  126. package/dist/types/Transactions/UndoManager.d.ts +63 -0
  127. package/dist/types/Transactions/index.d.ts +3 -0
  128. package/dist/types/Transformers/ASTOptimizer.d.ts +34 -0
  129. package/dist/types/Transformers/GradientTransformer.d.ts +18 -0
  130. package/dist/types/Transformers/GrowTransformer.d.ts +16 -0
  131. package/dist/types/Transformers/RainbowTransformer.d.ts +17 -0
  132. package/dist/types/Transformers/SineWaveTransformer.d.ts +17 -0
  133. package/dist/types/Transformers/Transformer.d.ts +17 -0
  134. package/dist/types/Types/core.d.ts +104 -0
  135. package/dist/types/Types/diagnostics.d.ts +85 -0
  136. package/dist/types/Types/index.d.ts +11 -0
  137. package/dist/types/Types/operations.d.ts +109 -0
  138. package/dist/types/Types/queries.d.ts +67 -0
  139. package/dist/types/Types/symbols.d.ts +42 -0
  140. package/dist/types/Types/tokens.d.ts +75 -0
  141. package/dist/types/Utils/BBCodeGenerator.d.ts +21 -0
  142. package/dist/types/Utils/ColorMath.d.ts +92 -0
  143. package/dist/types/Utils/EffectMath.d.ts +643 -0
  144. package/dist/types/Utils/TagVocabulary.d.ts +104 -0
  145. package/dist/types/Utils/TagVocabulary.test.d.ts +1 -0
  146. package/dist/types/Utils/color.d.ts +29 -0
  147. package/dist/types/Utils/dom-to-svg.d.ts +28 -0
  148. package/dist/types/Utils/dom-to-svg.test.d.ts +1 -0
  149. package/dist/types/Utils/treeTransformers.d.ts +160 -0
  150. package/dist/types/Visitors/BBBlocksExporter.d.ts +27 -0
  151. package/dist/types/Visitors/BBCodeExporter.d.ts +90 -0
  152. package/dist/types/Visitors/BlockPatcher.d.ts +91 -0
  153. package/dist/types/Visitors/DOMMorpher.d.ts +19 -0
  154. package/dist/types/Visitors/HTMLRenderer.d.ts +492 -0
  155. package/dist/types/Visitors/JSONExporter.d.ts +42 -0
  156. package/dist/types/Visitors/MarkdownExporter.d.ts +28 -0
  157. package/dist/types/Visitors/SVGRenderer.d.ts +16 -0
  158. package/dist/types/Visitors/TiptapExporter.d.ts +40 -0
  159. package/dist/types/Visitors/Visitor.d.ts +35 -0
  160. package/dist/types/Visitors/index.d.ts +9 -0
  161. package/dist/types/Visuals/BoxDrawer.d.ts +54 -0
  162. package/dist/types/Visuals/LyneAudio.d.ts +8 -0
  163. package/dist/types/Visuals/index.d.ts +39 -0
  164. package/dist/types/index.d.ts +155 -0
  165. package/package.json +14 -10
  166. package/src/Analysis/Passes/Analysis/ColorUsageAnalyzer.ts +103 -0
  167. package/src/Analysis/Passes/Analysis/GradientAnalyzer.ts +294 -20
  168. package/src/Analysis/Passes/Analysis/SymbolAnalyzer.ts +257 -0
  169. package/src/Analysis/Passes/Decision/PaletteRemapDecision.ts +425 -0
  170. package/src/Analysis/Pipeline/Pipeline.ts +15 -2
  171. package/src/Analysis/index.ts +8 -2
  172. package/src/BBCode/BBCodeDocumentModel.ts +39 -1
  173. package/src/BBCode/BBCodeToGreenNode.ts +18 -12
  174. package/src/BBCode/Parser.ts +38 -1
  175. package/src/Edits/EditPlan.ts +290 -0
  176. package/src/Edits/Optimizer.ts +185 -0
  177. package/src/Edits/Rules/Rule.ts +145 -0
  178. package/src/Edits/Rules/dropEmptyTags.ts +110 -0
  179. package/src/Edits/Rules/dropRedundantNesting.ts +120 -0
  180. package/src/Edits/Rules/index.ts +28 -0
  181. package/src/Edits/Rules/mergeAdjacent.ts +224 -0
  182. package/src/Edits/Rules/reorderWrappers.ts +154 -0
  183. package/src/Edits/Rules/shortenHex.ts +79 -0
  184. package/src/Edits/Rules/tagValue.ts +105 -0
  185. package/src/Edits/Rules/unwrapInvisibleColor.ts +93 -0
  186. package/src/Edits/applyEdits.ts +54 -0
  187. package/src/Edits/index.ts +22 -0
  188. package/src/Events/EventBus.ts +18 -0
  189. package/src/HTML/HTMLToGreenNode.ts +193 -37
  190. package/src/Incremental/BracketIndex.ts +249 -0
  191. package/src/Incremental/IncrementalParser.ts +464 -41
  192. package/src/Lexer/BBCodeLexer.ts +25 -3
  193. package/src/Lexer/index.ts +1 -1
  194. package/src/Markdown/MarkdownAST.ts +65 -2
  195. package/src/Markdown/MarkdownLexer.ts +69 -14
  196. package/src/Markdown/MarkdownParser.ts +749 -62
  197. package/src/Markdown/MarkdownToGreenNode.ts +148 -27
  198. package/src/MilHibri/MilHibriDocumentModel.ts +56 -0
  199. package/src/Model/DocumentModel.ts +138 -5
  200. package/src/Model/TagRegistry.ts +129 -134
  201. package/src/Reconciler/SurgicalReconciler.ts +460 -0
  202. package/src/Repair/NestingRepair.ts +116 -0
  203. package/src/Repair/index.ts +1 -0
  204. package/src/Semantic/SemanticAnalyzer.ts +1845 -65
  205. package/src/Syntax/GreenNodePool.ts +12 -12
  206. package/src/Syntax/RedNode.ts +17 -3
  207. package/src/Syntax/RedNodeStore.ts +9 -1
  208. package/src/Syntax/nodeAttr.ts +172 -0
  209. package/src/Tokens/TokenResolver.ts +55 -0
  210. package/src/Tokens/index.ts +1 -0
  211. package/src/Types/core.ts +15 -0
  212. package/src/Utils/ColorMath.ts +276 -55
  213. package/src/Utils/EffectMath.ts +2462 -0
  214. package/src/Utils/TagVocabulary.ts +141 -0
  215. package/src/Utils/dom-to-svg.ts +28 -12
  216. package/src/Visitors/BBBlocksExporter.ts +15 -1
  217. package/src/Visitors/BBCodeExporter.ts +365 -32
  218. package/src/Visitors/BlockPatcher.ts +123 -22
  219. package/src/Visitors/DOMMorpher.ts +44 -28
  220. package/src/Visitors/HTMLRenderer.ts +771 -205
  221. package/src/Visitors/MarkdownExporter.ts +98 -41
  222. package/src/Visitors/TiptapExporter.ts +1 -0
  223. package/src/Visitors/index.ts +1 -1
  224. package/src/Visuals/BoxDrawer.ts +56 -10
  225. package/src/Visuals/LyneAudio.ts +346 -0
  226. package/src/Visuals/index.ts +8 -4
  227. package/src/Visuals/lyne.css +374 -9
  228. package/src/Visuals/osu.css +393 -11
  229. package/src/index.ts +178 -8
  230. package/dist/index.d.mts +0 -4612
  231. package/dist/index.d.ts +0 -4612
  232. package/src/Analysis/Passes/Analysis/__tests__/GradientAnalyzer.test.ts +0 -135
  233. package/src/Analysis/Passes/Analysis/__tests__/MergeableColorAnalyzer.test.ts +0 -84
  234. package/src/Analysis/Passes/Analysis/__tests__/RainbowAnalyzer.test.ts +0 -99
  235. package/src/Analysis/Passes/Analysis/__tests__/WaveAnalyzer.test.ts +0 -119
  236. package/src/Analysis/Passes/Decision/__tests__/DefaultDecision.test.ts +0 -179
  237. package/src/Analysis/__tests__/Integration.test.ts +0 -162
  238. package/src/Analysis/__tests__/Pipeline.test.ts +0 -133
  239. package/src/Diff/__tests__/TreeDiffer.test.ts +0 -65
  240. package/src/Incremental/__tests__/IncrementalParser.test.ts +0 -164
  241. package/src/Syntax/__tests__/GreenNode.test.ts +0 -33
  242. package/src/Syntax/__tests__/RedNode.test.ts +0 -81
  243. package/src/Syntax/__tests__/RedNodeStore.test.ts +0 -104
  244. package/src/Tests/ASTOptimizerIdempotence.test.ts +0 -77
  245. package/src/Tests/BlockPatcher.test.ts +0 -437
  246. package/src/Tests/BlockPatcherWindowed.test.ts +0 -364
  247. package/src/Tests/BoxDrawer.test.ts +0 -217
  248. package/src/Tests/BoxRichTitle.test.ts +0 -105
  249. package/src/Tests/Chars500kBenchmark.test.ts +0 -151
  250. package/src/Tests/Chars500kEdits.test.ts +0 -321
  251. package/src/Tests/CollabPositions.test.ts +0 -146
  252. package/src/Tests/CompilerPathProfiling.test.ts +0 -186
  253. package/src/Tests/DOMMorpher.test.ts +0 -142
  254. package/src/Tests/DomPatchPerf.test.ts +0 -60
  255. package/src/Tests/EffectSegments.snapshot.json +0 -616
  256. package/src/Tests/EffectSegments.test.ts +0 -68
  257. package/src/Tests/FindNodeAtOffset.test.ts +0 -65
  258. package/src/Tests/Fuzzer.test.ts +0 -166
  259. package/src/Tests/GreenNodePool.test.ts +0 -153
  260. package/src/Tests/Lexer.test.ts +0 -238
  261. package/src/Tests/LyneMode.test.ts +0 -291
  262. package/src/Tests/ModelCoherence.test.ts +0 -180
  263. package/src/Tests/Partition.test.ts +0 -238
  264. package/src/Tests/PluginTags.test.ts +0 -150
  265. package/src/Tests/ProblematicSection.test.ts +0 -46
  266. package/src/Tests/ProblematicSectionHTML.test.ts +0 -58
  267. package/src/Tests/RedReuse.test.ts +0 -134
  268. package/src/Tests/ReproDelete20k.test.ts +0 -62
  269. package/src/Tests/SemanticValidators.test.ts +0 -136
  270. package/src/Tests/StableNodeIds.test.ts +0 -210
  271. package/src/Tests/StudioColorBloat.test.ts +0 -25
  272. package/src/Tests/StudioDebugText.test.ts +0 -27
  273. package/src/Tests/StudioTrailingChar.test.ts +0 -25
  274. package/src/Tests/StudioValidText.test.ts +0 -25
  275. package/src/Tests/UrlImgBug.test.ts +0 -23
  276. package/src/Tests/VisualBuilderFidelity.test.ts +0 -105
  277. package/src/Tests/referenceDocument.ts +0 -119
  278. package/src/Utils/dom-to-svg.test.ts +0 -86
@@ -1,291 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { BBCodeDocumentModel } from '../BBCode/BBCodeDocumentModel'
3
- import { HTMLRenderer } from '../Visitors/HTMLRenderer'
4
- import { BBCodeExporter } from '../Visitors/BBCodeExporter'
5
-
6
- describe('Lyne Mode & Dialect Isolation', () => {
7
- describe('Mode: osu (strict isolation)', () => {
8
- it('treats Lyne-exclusive tags as literal text with zero parsing errors', () => {
9
- const doc = new BBCodeDocumentModel({
10
- source: '[effect=glow]Neon text[/effect] and [wnotice]Warning[/wnotice] and [tables][row][col]1[/col][/row][/tables]',
11
- mode: 'osu',
12
- })
13
-
14
- const html = doc.toHTML()
15
- // In osu mode, effect, wnotice, and tables are unknown tags so they should not render as Lyne elements
16
- expect(html).not.toContain('bb-wnotice')
17
- expect(html).not.toContain('bb-table')
18
- expect(html).toContain('[effect=glow]')
19
- expect(html).toContain('[wnotice]')
20
- expect(html).toContain('[tables]')
21
- })
22
-
23
- it('degrades Lyne-only tags to plain content when exporting to osu target', () => {
24
- const doc = new BBCodeDocumentModel({
25
- source: '[wnotice]Important notice[/wnotice]',
26
- mode: 'lyne',
27
- })
28
-
29
- const exporter = new BBCodeExporter()
30
- const osuBBCode = exporter.export(doc.root!, 'osu')
31
- expect(osuBBCode).toBe('Important notice')
32
- expect(osuBBCode).not.toContain('[wnotice]')
33
- })
34
- })
35
-
36
- describe('Mode: lyne (custom tags & rich features)', () => {
37
- it('parses and renders consolidated [effect] and alias tags', () => {
38
- const doc = new BBCodeDocumentModel({
39
- source: '[effect=neon]Cyberpunk[/effect] [glow=#2EE6E2]Cyan Glow[/glow] [shimmer]Shiny[/shimmer]',
40
- mode: 'lyne',
41
- })
42
-
43
- const html = doc.toHTML()
44
- expect(html).toContain('class="bb-neon"')
45
- expect(html).toContain('class="bb-glow"')
46
- expect(html).toContain('text-shadow:0 0 4px #2EE6E2')
47
- expect(html).toContain('class="bb-shimmer"')
48
- })
49
-
50
- it('parses and renders consolidated [anim] and alias tags', () => {
51
- const doc = new BBCodeDocumentModel({
52
- source: '[anim=pulse]Pulsing[/anim] [wave]Waving[/wave] [glitch]Glitched[/glitch] [typewriter]Typed[/typewriter]',
53
- mode: 'lyne',
54
- })
55
-
56
- const html = doc.toHTML()
57
- expect(html).toContain('class="bb-pulse"')
58
- expect(html).toContain('class="bb-wave"')
59
- expect(html).toContain('class="bb-glitch"')
60
- expect(html).toContain('class="bb-typewriter"')
61
- })
62
-
63
- it('parses and renders consolidated [container] and hyphenated [neon-box] tags', () => {
64
- const doc = new BBCodeDocumentModel({
65
- source: '[container=card]Card content[/container] [container=neon-box:#ff0055]Neon Box[/container] [neon-box=#2ee6e2]Direct Neon Box[/neon-box]',
66
- mode: 'lyne',
67
- })
68
-
69
- const html = doc.toHTML()
70
- expect(html).toContain('class="bb-cut-panel bb-card"')
71
- expect(html).toContain('class="bb-cut-panel bb-neon-box"')
72
- expect(html).toContain('--neon-color:#ff0055')
73
- expect(html).toContain('Direct Neon Box')
74
- expect(html).not.toContain('[/neon-box]')
75
- })
76
-
77
- it('parses and renders safe [style] tags with CSS whitelist', () => {
78
- const doc = new BBCodeDocumentModel({
79
- source: '[style="display:flex;opacity:0.8;color:#2ee6e2;evil-prop:expression()"]Styled text[/style]',
80
- mode: 'lyne',
81
- })
82
-
83
- const html = doc.toHTML()
84
- expect(html).toContain('display:flex;opacity:0.8;color:#2ee6e2')
85
- expect(html).not.toContain('evil-prop')
86
- })
87
-
88
- it('parses and renders [wnotice] with warning symbol and custom color', () => {
89
- const doc = new BBCodeDocumentModel({
90
- source: '[wnotice=#ffaa00]Dangerous action ahead[/wnotice]',
91
- mode: 'lyne',
92
- })
93
-
94
- const html = doc.toHTML()
95
- expect(html).toContain('bb-wnotice')
96
- expect(html).toContain('⚠')
97
- expect(html).toContain('Dangerous action ahead')
98
- expect(html).toContain('border-left-color:#ffaa00')
99
- })
100
-
101
- it('parses and renders [tables], [columns], [gallery], [separator], [scroll]', () => {
102
- const doc = new BBCodeDocumentModel({
103
- source: `[tables=striped,borders][row][th]Header[/th][/row][row][col]Cell[/col][/row][/tables]
104
- [columns=3]Col1 Col2 Col3[/columns]
105
- [gallery][img]https://example.com/1.png[/img][img]https://example.com/2.png[/img][/gallery]
106
- [separator=stars]
107
- [scroll=150]Scrollable text[/scroll]`,
108
- mode: 'lyne',
109
- })
110
-
111
- const html = doc.toHTML()
112
- expect(html).toContain('class="bb-table bb-table-striped bb-table-borders"')
113
- expect(html).toContain('bb-th')
114
- expect(html).toContain('class="bb-columns" style="column-count:3;"')
115
- expect(html).toContain('class="bb-gallery"')
116
- expect(html).toContain('✦ ✦ ✦')
117
- expect(html).toContain('class="bb-scroll" style="max-height:150px;"')
118
- })
119
-
120
- it('parses and renders inline typography tags (sup, sub, mark, kbd, tooltip, flip, raw)', () => {
121
- const doc = new BBCodeDocumentModel({
122
- source: '[sup]super[/sup] [sub]sub[/sub] [mark]highlight[/mark] [kbd]CTRL[/kbd] [tooltip=Help text]Hover me[/tooltip] [flip]Flipped[/flip] [raw]<strong>Literal</strong>[/raw]',
123
- mode: 'lyne',
124
- })
125
-
126
- const html = doc.toHTML()
127
- expect(html).toContain('>super</sup>')
128
- expect(html).toContain('>sub</sub>')
129
- expect(html).toContain('class="bb-mark">highlight</mark>')
130
- expect(html).toContain('class="bb-kbd">CTRL</kbd>')
131
- expect(html).toContain('class="bb-tooltip" title="Help text"')
132
- expect(html).toContain('transform:scaleX(-1)')
133
- expect(html).toContain('&lt;strong&gt;Literal&lt;/strong&gt;')
134
- })
135
-
136
- it('supports mediaProxy and entityLinkResolver options', () => {
137
- const renderer = new HTMLRenderer({
138
- theme: 'lyne',
139
- dialect: 'lyne',
140
- mediaProxy: (url) => `https://proxy.lyne.game/?url=${encodeURIComponent(url)}`,
141
- entityLinkResolver: (kind, val) => {
142
- if (kind === 'guild') return { href: `/g/${val}`, external: false }
143
- if (kind === 'map') return { href: `/m/${val}`, external: true }
144
- return null
145
- },
146
- })
147
-
148
- const doc = new BBCodeDocumentModel({
149
- source: '[img]https://externalsite.com/pic.jpg[/img] [guild]CYBER[/guild] [map]12345[/map]',
150
- mode: 'lyne',
151
- })
152
-
153
- const html = renderer.render(doc.root!)
154
- expect(html).toContain('src="https://proxy.lyne.game/?url=https%3A%2F%2Fexternalsite.com%2Fpic.jpg"')
155
- expect(html).toContain('href="/g/CYBER"')
156
- expect(html).toContain('href="/m/12345" target="_blank"')
157
- })
158
-
159
- it('supports rich centered and aligned box titles in lyne mode', () => {
160
- const doc = new BBCodeDocumentModel({
161
- source: '[centre][box="Centered Mission"]Content[/box][/centre]\n[box=[centre]Inner Centered Title[/centre]]Content[/box]\n[centre][box=joined contests][notice][box=[b]nested box[/b]]inner[/box][/notice][/box][/centre]',
162
- mode: 'lyne',
163
- })
164
-
165
- const renderer = new HTMLRenderer({ theme: 'lyne', dialect: 'lyne' })
166
- const html = renderer.render(doc.root!)
167
- expect(html).toContain('<details')
168
- expect(html).toContain('class="box"')
169
- expect(html).toContain('Centered Mission')
170
- expect(html).toContain('style="text-align:center;"')
171
- expect(html).toContain('nested box')
172
- expect(html).toContain('class="bb-notice-body"')
173
- })
174
-
175
- it('exports correctly with target lyne', () => {
176
- const input = '[wnotice=#ff0000]Be careful[/wnotice]'
177
- const doc = new BBCodeDocumentModel({
178
- source: input,
179
- mode: 'lyne',
180
- })
181
-
182
- const exporter = new BBCodeExporter(undefined, 'lyne')
183
- const exported = exporter.export(doc.root!)
184
- expect(exported).toBe('[wnotice=#ff0000]Be careful[/wnotice]')
185
- })
186
-
187
- it('linkifies @mentions via mentionResolver and keeps unresolved ones as text', () => {
188
- const doc = new BBCodeDocumentModel({
189
- source: '[b]Hey @airi and @ghost[/b] tail',
190
- mode: 'lyne',
191
- })
192
- const renderer = new HTMLRenderer({
193
- registry: doc.tagRegistry,
194
- dialect: 'lyne',
195
- theme: 'lyne',
196
- mentionResolver: (name) => (name === 'airi' ? { href: `/u/${name}`, external: false } : null),
197
- })
198
- const html = renderer.render(doc.root!)
199
- expect(html).toContain('<a class="bb-mention" href="/u/airi">@airi</a>')
200
- expect(html).toContain('@ghost')
201
- expect(html).toContain('tail')
202
- })
203
-
204
- it('escapes mention names and rejects dangerous mention hrefs', () => {
205
- const doc = new BBCodeDocumentModel({
206
- source: 'Hi @evil"onclick="alert(1)',
207
- mode: 'lyne',
208
- })
209
- const renderer = new HTMLRenderer({
210
- registry: doc.tagRegistry,
211
- dialect: 'lyne',
212
- theme: 'lyne',
213
- mentionResolver: () => ({ href: 'javascript:alert(1)', external: false }),
214
- })
215
- const html = renderer.render(doc.root!)
216
- // Dangerous protocol → mention stays plain text, escaped
217
- expect(html).not.toContain('javascript:')
218
- expect(html).not.toContain('<a class="bb-mention"')
219
- expect(html).toContain('&quot;')
220
- })
221
-
222
- it('allows safe mention hrefs (root-relative and http)', () => {
223
- const doc = new BBCodeDocumentModel({
224
- source: '@airi @jane',
225
- mode: 'lyne',
226
- })
227
- const renderer = new HTMLRenderer({
228
- registry: doc.tagRegistry,
229
- dialect: 'lyne',
230
- theme: 'lyne',
231
- mentionResolver: (name) => ({
232
- href: name === 'airi' ? `/u/${name}` : `https://example.com/u/${name}`,
233
- external: name !== 'airi',
234
- }),
235
- })
236
- const html = renderer.render(doc.root!)
237
- expect(html).toContain('<a class="bb-mention" href="/u/airi">@airi</a>')
238
- expect(html).toContain('<a class="bb-mention" href="https://example.com/u/jane" target="_blank" rel="noopener">@jane</a>')
239
- })
240
-
241
- it('turns timestamps into editor deep-link chips via timestampResolver', () => {
242
- const doc = new BBCodeDocumentModel({
243
- source: 'At 1:23 the beat drops, see 04:05.5 for the outro',
244
- mode: 'lyne',
245
- })
246
- const renderer = new HTMLRenderer({
247
- registry: doc.tagRegistry,
248
- dialect: 'lyne',
249
- theme: 'lyne',
250
- timestampResolver: (ms, label) => ({ href: `line://edit?map=42&t=${ms}`, external: false }),
251
- })
252
- const html = renderer.render(doc.root!)
253
- expect(html).toContain('<a class="bb-timeref" href="line://edit?map=42&amp;t=83000">1:23</a>')
254
- expect(html).toContain('<a class="bb-timeref" href="line://edit?map=42&amp;t=245500">04:05.5</a>')
255
- })
256
-
257
- it('keeps timestamps as plain text without timestampResolver', () => {
258
- const doc = new BBCodeDocumentModel({
259
- source: 'At 1:23 nothing happens',
260
- mode: 'lyne',
261
- })
262
- const renderer = new HTMLRenderer({
263
- registry: doc.tagRegistry,
264
- dialect: 'lyne',
265
- theme: 'lyne',
266
- })
267
- const html = renderer.render(doc.root!)
268
- expect(html).toContain('At 1:23 nothing happens')
269
- expect(html).not.toContain('bb-timeref')
270
- })
271
-
272
- it('renders [col] outside [tables] as a plain span, inside as td', () => {
273
- const doc = new BBCodeDocumentModel({
274
- source: `[columns=2][col]Left[/col][col]Right[/col][/columns]
275
- [tables][row][col]Cell[/col][/row][/tables]`,
276
- mode: 'lyne',
277
- })
278
- const prev = HTMLRenderer.idMode
279
- HTMLRenderer.idMode = 'none'
280
- try {
281
- const html = doc.toHTML()
282
- // Orphaned [col] inside [columns] → span, not td
283
- expect(html).toContain('<div class="bb-columns" style="column-count:2;"><span>Left</span><span>Right</span></div>')
284
- // [col] inside [tables] stays a real cell
285
- expect(html).toContain('<td>Cell</td>')
286
- } finally {
287
- HTMLRenderer.idMode = prev
288
- }
289
- })
290
- })
291
- })
@@ -1,180 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { BBCodeDocumentModel } from '../BBCode/BBCodeDocumentModel'
3
- import type { RedNode } from '../Syntax/RedNode'
4
- import type { Operation } from '../Types/operations'
5
- import type { NodeId } from '../Types/core'
6
-
7
- /**
8
- * Three properties this file pins down:
9
- *
10
- * 1. `ensureAnalyzed` — diagnostics must describe the CURRENT text. The
11
- * incremental edit path defers `analyze()` behind a debounce, so a caller
12
- * reading `diagnostics` right after `applyTextUpdate` used to see the
13
- * previous document's errors (the error panel ran one keystroke behind).
14
- *
15
- * 2. EventBus laziness — with no subscribers, an edit must not retain event
16
- * history (each retained event pinned the source string and, via the lazy
17
- * `nodeMatch` closure, the whole previous red/green tree). The capability
18
- * stays: subscribers get events, `recordHistory` opts back in.
19
- *
20
- * 3. transact/undo/redo coherence — after any of them, `source`, `greenRoot`
21
- * and `redRoot` describe the same document. The old implementation swapped
22
- * the red root and left source/green stale, and its undo re-applied
23
- * non-inverted operations.
24
- */
25
-
26
- function findKind(root: RedNode, kind: string): RedNode | null {
27
- let found: RedNode | null = null
28
- root.walk(n => {
29
- if (!found && n.kind === kind) found = n
30
- })
31
- return found
32
- }
33
-
34
- function deleteOp(node: RedNode): Operation {
35
- return {
36
- kind: 'delete_node',
37
- nodeId: node.id,
38
- node,
39
- parentId: node.parent!.id,
40
- index: node.index,
41
- id: '',
42
- undoable: true,
43
- timestamp: 0,
44
- }
45
- }
46
-
47
- describe('ensureAnalyzed — diagnostics freshness', () => {
48
- it('diagnostics describe the current text right after an incremental edit', () => {
49
- const model = new BBCodeDocumentModel({ source: 'hola mundo' })
50
- expect(model.diagnostics?.items ?? []).toHaveLength(0)
51
-
52
- model.applyTextUpdate('hola [b]mundo')
53
- model.ensureAnalyzed()
54
-
55
- expect(model.diagnostics!.items.map(d => d.code)).toContain('unclosed-tag')
56
- })
57
-
58
- it('clears again when the error is fixed', () => {
59
- const model = new BBCodeDocumentModel({ source: 'hola [b]mundo' })
60
- model.applyTextUpdate('hola [b]mundo[/b]')
61
- model.ensureAnalyzed()
62
-
63
- expect(model.diagnostics!.items).toHaveLength(0)
64
- })
65
-
66
- it('flushes the pending work exactly once', async () => {
67
- const model = new BBCodeDocumentModel({ source: 'hola mundo' })
68
- let emitted = 0
69
- model.events.on('diagnostics_updated', () => { emitted++ })
70
-
71
- model.applyTextUpdate('hola mundo!')
72
- model.ensureAnalyzed()
73
- expect(emitted).toBe(1)
74
-
75
- // The debounce timer must not fire a second run afterwards.
76
- await new Promise(r => setTimeout(r, 30))
77
- expect(emitted).toBe(1)
78
- })
79
- })
80
-
81
- describe('EventBus — no cost without subscribers', () => {
82
- it('keeps no event history by default', () => {
83
- const model = new BBCodeDocumentModel({ source: 'hola' })
84
- model.applyTextUpdate('hola mundo')
85
- model.ensureAnalyzed()
86
- model.rebuild('adios')
87
-
88
- expect(model.events.getHistory()).toHaveLength(0)
89
- })
90
-
91
- it('records history when opted in', () => {
92
- const model = new BBCodeDocumentModel({ source: 'hola' })
93
- model.events.recordHistory = true
94
- model.rebuild('adios')
95
-
96
- expect(model.events.getHistory().length).toBeGreaterThan(0)
97
- })
98
-
99
- it('subscribers still receive events, with a working lazy nodeMatch', () => {
100
- const model = new BBCodeDocumentModel({ source: 'hola' })
101
- const seen: Array<Record<string, unknown>> = []
102
- model.events.on('document_changed', e => seen.push(e))
103
-
104
- model.rebuild('adios')
105
-
106
- expect(seen).toHaveLength(1)
107
- // Reading the accessor computes the match on demand.
108
- expect(seen[0].nodeMatch).toBeTruthy()
109
- })
110
- })
111
-
112
- describe('transact / undo / redo — model coherence', () => {
113
- it('transact updates source, green and red together', () => {
114
- const model = new BBCodeDocumentModel({ source: '[b]hola[/b] mundo' })
115
- const before = model.source
116
- const bold = findKind(model.redRoot!, 'bold')!
117
-
118
- const changed = model.transact([deleteOp(bold)], 'delete bold')
119
-
120
- expect(changed).toBe(true)
121
- expect(model.source).not.toContain('[b]')
122
- expect(model.source).toContain('mundo')
123
- // The red tree is a view over the green tree the model holds — not a
124
- // mutated orphan, which is what the old implementation left behind.
125
- expect(model.redRoot!.green).toBe(model.greenRoot)
126
- expect(model.source).not.toBe(before)
127
- })
128
-
129
- it('undo and redo replay snapshots and stay coherent', () => {
130
- const model = new BBCodeDocumentModel({ source: '[b]hola[/b] mundo' })
131
- const before = model.source
132
- const bold = findKind(model.redRoot!, 'bold')!
133
- model.transact([deleteOp(bold)], 'delete bold')
134
- const after = model.source
135
-
136
- expect(model.undo()).toBe(true)
137
- expect(model.source).toBe(before)
138
- expect(model.redRoot!.green).toBe(model.greenRoot)
139
- expect(findKind(model.redRoot!, 'bold')).toBeTruthy()
140
-
141
- expect(model.redo()).toBe(true)
142
- expect(model.source).toBe(after)
143
- expect(findKind(model.redRoot!, 'bold')).toBeNull()
144
-
145
- expect(model.undo()).toBe(true)
146
- expect(model.source).toBe(before)
147
- })
148
-
149
- it('text editing keeps working after a transact', () => {
150
- const model = new BBCodeDocumentModel({ source: '[b]hola[/b] mundo' })
151
- const bold = findKind(model.redRoot!, 'bold')!
152
- model.transact([deleteOp(bold)])
153
-
154
- // This used to diff against a stale `_source` and splice a stale green
155
- // tree; with the coherence contract it is just another edit.
156
- const edited = model.source + '!'
157
- model.applyTextUpdate(edited)
158
- expect(model.source).toBe(edited)
159
- expect(model.redRoot!.green).toBe(model.greenRoot)
160
- })
161
-
162
- it('a no-op transaction changes nothing and is not undoable', () => {
163
- const model = new BBCodeDocumentModel({ source: 'hola mundo' })
164
- const missing: Operation = {
165
- kind: 'delete_node',
166
- nodeId: 'no-such-node' as unknown as NodeId,
167
- node: model.redRoot!,
168
- parentId: 'no-such-parent' as unknown as NodeId,
169
- index: 0,
170
- id: '',
171
- undoable: true,
172
- timestamp: 0,
173
- }
174
-
175
- expect(model.transact([missing])).toBe(false)
176
- expect(model.source).toBe('hola mundo')
177
- expect(model.undoManager.undoCount).toBe(0)
178
- expect(model.undo()).toBe(false)
179
- })
180
- })