@miliastry/quasar 1.0.5 → 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 +191 -21
  2. package/dist/Visuals/osu.css +36 -5
  3. package/dist/index.js +9619 -3135
  4. package/dist/index.mjs +9490 -3136
  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 +34 -11
  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 +132 -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 +452 -142
  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 +191 -21
  223. package/src/Visuals/osu.css +36 -5
  224. package/src/index.ts +172 -5
  225. package/dist/index.d.mts +0 -4609
  226. package/dist/index.d.ts +0 -4609
  227. package/src/Analysis/Passes/Analysis/__tests__/GradientAnalyzer.test.ts +0 -135
  228. package/src/Analysis/Passes/Analysis/__tests__/MergeableColorAnalyzer.test.ts +0 -84
  229. package/src/Analysis/Passes/Analysis/__tests__/RainbowAnalyzer.test.ts +0 -99
  230. package/src/Analysis/Passes/Analysis/__tests__/WaveAnalyzer.test.ts +0 -119
  231. package/src/Analysis/Passes/Decision/__tests__/DefaultDecision.test.ts +0 -179
  232. package/src/Analysis/__tests__/Integration.test.ts +0 -162
  233. package/src/Analysis/__tests__/Pipeline.test.ts +0 -133
  234. package/src/Diff/__tests__/TreeDiffer.test.ts +0 -65
  235. package/src/Incremental/__tests__/IncrementalParser.test.ts +0 -164
  236. package/src/Syntax/__tests__/GreenNode.test.ts +0 -33
  237. package/src/Syntax/__tests__/RedNode.test.ts +0 -81
  238. package/src/Syntax/__tests__/RedNodeStore.test.ts +0 -104
  239. package/src/Tests/ASTOptimizerIdempotence.test.ts +0 -77
  240. package/src/Tests/BlockPatcher.test.ts +0 -437
  241. package/src/Tests/BlockPatcherWindowed.test.ts +0 -364
  242. package/src/Tests/BoxDrawer.test.ts +0 -217
  243. package/src/Tests/BoxRichTitle.test.ts +0 -105
  244. package/src/Tests/Chars500kBenchmark.test.ts +0 -151
  245. package/src/Tests/Chars500kEdits.test.ts +0 -321
  246. package/src/Tests/CollabPositions.test.ts +0 -146
  247. package/src/Tests/CompilerPathProfiling.test.ts +0 -186
  248. package/src/Tests/DOMMorpher.test.ts +0 -142
  249. package/src/Tests/DomPatchPerf.test.ts +0 -60
  250. package/src/Tests/EffectSegments.snapshot.json +0 -616
  251. package/src/Tests/EffectSegments.test.ts +0 -68
  252. package/src/Tests/FindNodeAtOffset.test.ts +0 -65
  253. package/src/Tests/Fuzzer.test.ts +0 -166
  254. package/src/Tests/GreenNodePool.test.ts +0 -153
  255. package/src/Tests/Lexer.test.ts +0 -238
  256. package/src/Tests/LyneMode.test.ts +0 -291
  257. package/src/Tests/ModelCoherence.test.ts +0 -180
  258. package/src/Tests/Partition.test.ts +0 -238
  259. package/src/Tests/PluginTags.test.ts +0 -150
  260. package/src/Tests/ProblematicSection.test.ts +0 -46
  261. package/src/Tests/ProblematicSectionHTML.test.ts +0 -58
  262. package/src/Tests/RedReuse.test.ts +0 -134
  263. package/src/Tests/ReproDelete20k.test.ts +0 -62
  264. package/src/Tests/SemanticValidators.test.ts +0 -136
  265. package/src/Tests/StableNodeIds.test.ts +0 -210
  266. package/src/Tests/StudioColorBloat.test.ts +0 -25
  267. package/src/Tests/StudioDebugText.test.ts +0 -27
  268. package/src/Tests/StudioTrailingChar.test.ts +0 -25
  269. package/src/Tests/StudioValidText.test.ts +0 -25
  270. package/src/Tests/UrlImgBug.test.ts +0 -23
  271. package/src/Tests/VisualBuilderFidelity.test.ts +0 -105
  272. package/src/Tests/referenceDocument.ts +0 -119
  273. package/src/Utils/dom-to-svg.test.ts +0 -86
@@ -1,135 +0,0 @@
1
- /**
2
- * Tests for refined GradientAnalyzer v2 (OKLab, change-point detection)
3
- */
4
-
5
- import { describe, it, expect } from 'vitest'
6
- import { greenNode, greenLeaf } from '../../../../Syntax/GreenNode'
7
- import type { GreenNode } from '../../../../Syntax/GreenNode'
8
- import { GradientAnalyzer } from '../GradientAnalyzer'
9
- import type { PipelineContext } from '../../../Contracts/PipelineContext'
10
- import { PipelineMode, ExportTarget } from '../../../Contracts/PipelineContext'
11
- import { ContributionKind } from '../../../Contracts/Contribution'
12
-
13
- const mockContext: PipelineContext = {
14
- mode: PipelineMode.Batch,
15
- target: ExportTarget.Miliastry,
16
- featureFlags: {},
17
- metadata: {},
18
- }
19
-
20
- function colorNode(hex: string, text: string): GreenNode {
21
- // Widths of the real BBCode this stands for: `[color=#RRGGBB]` and `[/color]`.
22
- const textLeaf = greenLeaf('text', text)
23
- return greenNode('color', `=${hex}`, [textLeaf], 8 + hex.length, 8)
24
- }
25
-
26
- describe('GradientAnalyzer v2', () => {
27
- it('detects a smooth linear gradient with high confidence', () => {
28
- // Perfectly spaced perceptual gradient: #FF0000 → #990000 → #330000
29
- const tree = greenNode('document', '', [
30
- colorNode('#FF0000', 'A'),
31
- colorNode('#CC0022', 'B'),
32
- colorNode('#990044', 'C'),
33
- colorNode('#660066', 'D'),
34
- colorNode('#330088', 'E'),
35
- colorNode('#0000AA', 'F'),
36
- ])
37
-
38
- const analyzer = new GradientAnalyzer()
39
- const results = analyzer.run(tree, mockContext)
40
-
41
- expect(results.length).toBeGreaterThanOrEqual(1)
42
- if (results[0].kind === 'semantic') {
43
- expect(results[0].confidence).toBeGreaterThan(0.7)
44
- }
45
- })
46
-
47
- it('detects change points (stops) in plateau-heavy sequences', () => {
48
- // RRRR→G→BBBB: 4 R, 1 G, 4 B
49
- const tree = greenNode('document', '', [
50
- colorNode('#FF0000', 'R'),
51
- colorNode('#FF0000', 'R'),
52
- colorNode('#FF0000', 'R'),
53
- colorNode('#FF0000', 'R'),
54
- colorNode('#00FF00', 'G'),
55
- colorNode('#0000FF', 'B'),
56
- colorNode('#0000FF', 'B'),
57
- colorNode('#0000FF', 'B'),
58
- colorNode('#0000FF', 'B'),
59
- ])
60
-
61
- const analyzer = new GradientAnalyzer()
62
- const results = analyzer.run(tree, mockContext)
63
-
64
- expect(results.length).toBeGreaterThanOrEqual(1)
65
- if (results[0].kind === 'semantic') {
66
- // Check that stops were detected via change-point detection
67
- const model = results[0].metadata.model as Record<string, unknown>
68
- const stops = model.stops as Array<{ color: string; position: number }>
69
- // Should detect 3 stops: R, G, B
70
- expect(stops.length).toBeGreaterThanOrEqual(3)
71
- }
72
- })
73
-
74
- it('gives low confidence to random colour jumps', () => {
75
- // Red→Green→Blue: sharp perceptual jumps
76
- const tree = greenNode('document', '', [
77
- colorNode('#FF0000', 'A'),
78
- colorNode('#00FF00', 'B'),
79
- colorNode('#0000FF', 'C'),
80
- ])
81
-
82
- const analyzer = new GradientAnalyzer()
83
- const results = analyzer.run(tree, mockContext)
84
-
85
- // Should detect, but with low confidence (large perceptual error)
86
- if (results.length > 0 && results[0].kind === 'semantic') {
87
- expect(results[0].confidence).toBeLessThan(0.7)
88
- }
89
- })
90
-
91
- it('ignores sequences shorter than minimum length', () => {
92
- const tree = greenNode('document', '', [
93
- colorNode('#FF0000', 'A'),
94
- colorNode('#00FF00', 'B'),
95
- ])
96
-
97
- const analyzer = new GradientAnalyzer()
98
- const results = analyzer.run(tree, mockContext)
99
-
100
- expect(results).toHaveLength(0)
101
- })
102
-
103
- it('handles mixed content (non-color nodes between colors)', () => {
104
- const tree = greenNode('document', '', [
105
- colorNode('#FF0000', 'A'),
106
- greenLeaf('text', ' break '),
107
- colorNode('#EE1100', 'B'),
108
- ])
109
-
110
- const analyzer = new GradientAnalyzer()
111
- const results = analyzer.run(tree, mockContext)
112
-
113
- // The break text should separate the sequence into single nodes
114
- expect(results).toHaveLength(0)
115
- })
116
-
117
- it('returns diagnostics with stop count in metadata', () => {
118
- const tree = greenNode('document', '', [
119
- colorNode('#FF0000', 'A'),
120
- colorNode('#DD2222', 'B'),
121
- colorNode('#BB4444', 'C'),
122
- colorNode('#996666', 'D'),
123
- colorNode('#778888', 'E'),
124
- ])
125
-
126
- const analyzer = new GradientAnalyzer()
127
- const results = analyzer.run(tree, mockContext)
128
-
129
- if (results.length > 0 && results[0].kind === 'semantic') {
130
- const diag = results[0].metadata.diagnostics as Record<string, unknown>
131
- expect(typeof diag.stopCount).toBe('number')
132
- expect(typeof diag.maxPerceptualError).toBe('number')
133
- }
134
- })
135
- })
@@ -1,84 +0,0 @@
1
- /**
2
- * Tests for MergeableColorAnalyzer
3
- */
4
-
5
- import { describe, it, expect } from 'vitest'
6
- import { greenNode, greenLeaf } from '../../../../Syntax/GreenNode'
7
- import { MergeableColorAnalyzer } from '../MergeableColorAnalyzer'
8
- import type { PipelineContext } from '../../../Contracts/PipelineContext'
9
- import { PipelineMode, ExportTarget } from '../../../Contracts/PipelineContext'
10
- import { ContributionKind } from '../../../Contracts/Contribution'
11
-
12
- const mockContext: PipelineContext = {
13
- mode: PipelineMode.Batch,
14
- target: ExportTarget.Osu,
15
- featureFlags: {},
16
- metadata: {},
17
- }
18
-
19
- function colorNode(hex: string, text: string): import('../../../../Syntax/GreenNode').GreenNode {
20
- // Widths of the real BBCode this stands for: `[color=#RRGGBB]` and `[/color]`.
21
- const textLeaf = greenLeaf('text', text)
22
- return greenNode('color', `=${hex}`, [textLeaf], 8 + hex.length, 8)
23
- }
24
-
25
- describe('MergeableColorAnalyzer', () => {
26
- it('detects mergeable consecutive identical colors', () => {
27
- // [color=#FF0000]H[/color][color=#FF0000]e[/color][color=#FF0000]l[/color]
28
- const tree = greenNode('document', '', [
29
- colorNode('#FF0000', 'H'),
30
- colorNode('#FF0000', 'e'),
31
- colorNode('#FF0000', 'l'),
32
- ])
33
-
34
- const analyzer = new MergeableColorAnalyzer()
35
- const results = analyzer.run(tree, mockContext)
36
-
37
- expect(results).toHaveLength(1)
38
- expect(results[0]).toMatchObject({
39
- kind: ContributionKind.Optimization,
40
- label: 'Merge Colors',
41
- })
42
- })
43
-
44
- it('does NOT report non-identical consecutive colors', () => {
45
- // [color=#FF0000]H[/color][color=#EE1100]e[/color][color=#DD2200]l[/color]
46
- const tree = greenNode('document', '', [
47
- colorNode('#FF0000', 'H'),
48
- colorNode('#EE1100', 'e'),
49
- colorNode('#DD2200', 'l'),
50
- ])
51
-
52
- const analyzer = new MergeableColorAnalyzer()
53
- const results = analyzer.run(tree, mockContext)
54
-
55
- expect(results).toHaveLength(0)
56
- })
57
-
58
- it('does NOT report single color nodes', () => {
59
- const tree = greenNode('document', '', [
60
- colorNode('#FF0000', 'H'),
61
- ])
62
-
63
- const analyzer = new MergeableColorAnalyzer()
64
- const results = analyzer.run(tree, mockContext)
65
-
66
- expect(results).toHaveLength(0)
67
- })
68
-
69
- it('detects multiple separate mergeable sequences', () => {
70
- // [color=#FF0000]A[/color][color=#FF0000]B[/color] --- [color=#00FF00]X[/color][color=#00FF00]Y[/color][color=#00FF00]Z[/color]
71
- const tree = greenNode('document', '', [
72
- colorNode('#FF0000', 'A'),
73
- colorNode('#FF0000', 'B'),
74
- colorNode('#00FF00', 'X'),
75
- colorNode('#00FF00', 'Y'),
76
- colorNode('#00FF00', 'Z'),
77
- ])
78
-
79
- const analyzer = new MergeableColorAnalyzer()
80
- const results = analyzer.run(tree, mockContext)
81
-
82
- expect(results).toHaveLength(2)
83
- })
84
- })
@@ -1,99 +0,0 @@
1
- /**
2
- * Tests for RainbowAnalyzer
3
- */
4
-
5
- import { describe, it, expect } from 'vitest'
6
- import { greenNode, greenLeaf } from '../../../../Syntax/GreenNode'
7
- import type { GreenNode } from '../../../../Syntax/GreenNode'
8
- import { RainbowAnalyzer } from '../RainbowAnalyzer'
9
- import type { PipelineContext } from '../../../Contracts/PipelineContext'
10
- import { PipelineMode, ExportTarget } from '../../../Contracts/PipelineContext'
11
- import { ContributionKind } from '../../../Contracts/Contribution'
12
-
13
- const mockContext: PipelineContext = {
14
- mode: PipelineMode.Batch,
15
- target: ExportTarget.Miliastry,
16
- featureFlags: {},
17
- metadata: {},
18
- }
19
-
20
- function colorNode(hex: string, text: string): GreenNode {
21
- // Widths of the real BBCode this stands for: `[color=#RRGGBB]` and `[/color]`.
22
- const textLeaf = greenLeaf('text', text)
23
- return greenNode('color', `=${hex}`, [textLeaf], 8 + hex.length, 8)
24
- }
25
-
26
- describe('RainbowAnalyzer', () => {
27
- it('detects a classic rainbow (hue rotation, stable S/L)', () => {
28
- // Red → Yellow → Green → Cyan → Blue — classic rainbow with high S, mid L
29
- const tree = greenNode('document', '', [
30
- colorNode('#FF0000', 'R'),
31
- colorNode('#FFAA00', 'A'),
32
- colorNode('#FFFF00', 'Y'),
33
- colorNode('#00FF00', 'G'),
34
- colorNode('#00FFFF', 'C'),
35
- colorNode('#0000FF', 'B'),
36
- colorNode('#AA00FF', 'V'),
37
- ])
38
-
39
- const analyzer = new RainbowAnalyzer()
40
- const results = analyzer.run(tree, mockContext)
41
-
42
- expect(results.length).toBeGreaterThanOrEqual(1)
43
- if (results[0].kind === 'semantic') {
44
- expect(results[0].confidence).toBeGreaterThan(0.5)
45
- }
46
- })
47
-
48
- it('gives lower confidence to random colors (no hue rotation)', () => {
49
- // Colors with varying hues, saturations, and lightness — no clear rainbow
50
- const tree = greenNode('document', '', [
51
- colorNode('#804868', 'A'),
52
- colorNode('#7EB8E0', 'B'),
53
- colorNode('#D194B3', 'C'),
54
- colorNode('#43383A', 'D'),
55
- colorNode('#595255', 'E'),
56
- ])
57
-
58
- const analyzer = new RainbowAnalyzer()
59
- const results = analyzer.run(tree, mockContext)
60
-
61
- // These have no clear hue rotation — confidence should be low
62
- if (results.length > 0 && results[0].kind === 'semantic') {
63
- expect(results[0].confidence).toBeLessThan(0.7)
64
- }
65
- })
66
-
67
- it('ignores sequences shorter than minimum length (4)', () => {
68
- const tree = greenNode('document', '', [
69
- colorNode('#FF0000', 'A'),
70
- colorNode('#00FF00', 'B'),
71
- colorNode('#0000FF', 'C'),
72
- ])
73
-
74
- const analyzer = new RainbowAnalyzer()
75
- const results = analyzer.run(tree, mockContext)
76
-
77
- expect(results).toHaveLength(0)
78
- })
79
-
80
- it('reports diagnostics with hue delta and saturation deviation', () => {
81
- const tree = greenNode('document', '', [
82
- colorNode('#FF0000', 'A'),
83
- colorNode('#FF8800', 'B'),
84
- colorNode('#FFFF00', 'C'),
85
- colorNode('#88FF00', 'D'),
86
- colorNode('#00FF00', 'E'),
87
- ])
88
-
89
- const analyzer = new RainbowAnalyzer()
90
- const results = analyzer.run(tree, mockContext)
91
-
92
- if (results.length > 0 && results[0].kind === 'semantic') {
93
- const diag = results[0].metadata.diagnostics as Record<string, unknown>
94
- expect(typeof diag.hueDelta).toBe('number')
95
- expect(typeof diag.saturationDeviation).toBe('number')
96
- expect(typeof diag.lightnessDeviation).toBe('number')
97
- }
98
- })
99
- })
@@ -1,119 +0,0 @@
1
- /**
2
- * Tests for WaveAnalyzer (size oscillation detection)
3
- */
4
-
5
- import { describe, it, expect } from 'vitest'
6
- import { greenNode, greenLeaf } from '../../../../Syntax/GreenNode'
7
- import type { GreenNode } from '../../../../Syntax/GreenNode'
8
- import { WaveAnalyzer } from '../WaveAnalyzer'
9
- import type { PipelineContext } from '../../../Contracts/PipelineContext'
10
- import { PipelineMode, ExportTarget } from '../../../Contracts/PipelineContext'
11
- import { ContributionKind } from '../../../Contracts/Contribution'
12
-
13
- const mockContext: PipelineContext = {
14
- mode: PipelineMode.Batch,
15
- target: ExportTarget.Miliastry,
16
- featureFlags: {},
17
- metadata: {},
18
- }
19
-
20
- function sizeNode(size: number, text: string): GreenNode {
21
- // Widths of the real BBCode this stands for: `[size=NN]` and `[/size]`.
22
- const textLeaf = greenLeaf('text', text)
23
- return greenNode('font_size', `=${size}`, [textLeaf], 7 + String(size).length, 7)
24
- }
25
-
26
- describe('WaveAnalyzer', () => {
27
- it('detects a sine wave size pattern', () => {
28
- // Sinusoidal sizes: 90 → 120 → 150 → 120 → 90
29
- const tree = greenNode('document', '', [
30
- sizeNode(90, 'A'),
31
- sizeNode(120, 'B'),
32
- sizeNode(150, 'C'),
33
- sizeNode(120, 'D'),
34
- sizeNode(90, 'E'),
35
- ])
36
-
37
- const analyzer = new WaveAnalyzer()
38
- const results = analyzer.run(tree, mockContext)
39
-
40
- expect(results.length).toBeGreaterThanOrEqual(1)
41
- })
42
-
43
- it('gives higher confidence to clear wave patterns', () => {
44
- // Perfect sine-like: 90→125→160→125→90
45
- const tree = greenNode('document', '', [
46
- sizeNode(90, 'A'),
47
- sizeNode(110, 'B'),
48
- sizeNode(140, 'C'),
49
- sizeNode(160, 'D'),
50
- sizeNode(140, 'E'),
51
- sizeNode(110, 'F'),
52
- sizeNode(90, 'G'),
53
- ])
54
-
55
- const analyzer = new WaveAnalyzer()
56
- const results = analyzer.run(tree, mockContext)
57
-
58
- if (results.length > 0 && results[0].kind === 'semantic') {
59
- expect(results[0].confidence).toBeGreaterThan(0.4)
60
- }
61
- })
62
-
63
- it('detects a wave pattern and produces a contribution', () => {
64
- // Validates the WaveAnalyzer produces a semantic contribution for size patterns
65
- const tree = greenNode('document', '', [
66
- sizeNode(90, 'A'),
67
- sizeNode(120, 'B'),
68
- sizeNode(150, 'C'),
69
- sizeNode(120, 'D'),
70
- sizeNode(90, 'E'),
71
- sizeNode(120, 'F'),
72
- sizeNode(150, 'G'),
73
- ])
74
-
75
- const analyzer = new WaveAnalyzer()
76
- const results = analyzer.run(tree, mockContext)
77
-
78
- expect(results.length).toBeGreaterThanOrEqual(1)
79
- if (results[0].kind === 'semantic') {
80
- expect(results[0].label).toBe('Wave')
81
- expect(results[0].metadata.model).toBeDefined()
82
- }
83
- })
84
-
85
- it('ignores sequences shorter than minimum length (5)', () => {
86
- const tree = greenNode('document', '', [
87
- sizeNode(90, 'A'),
88
- sizeNode(120, 'B'),
89
- sizeNode(150, 'C'),
90
- sizeNode(120, 'D'),
91
- ])
92
-
93
- const analyzer = new WaveAnalyzer()
94
- const results = analyzer.run(tree, mockContext)
95
-
96
- expect(results).toHaveLength(0)
97
- })
98
-
99
- it('reports diagnostics with periodicity score', () => {
100
- const tree = greenNode('document', '', [
101
- sizeNode(90, 'A'),
102
- sizeNode(120, 'B'),
103
- sizeNode(150, 'C'),
104
- sizeNode(120, 'D'),
105
- sizeNode(90, 'E'),
106
- sizeNode(120, 'F'),
107
- sizeNode(150, 'G'),
108
- ])
109
-
110
- const analyzer = new WaveAnalyzer()
111
- const results = analyzer.run(tree, mockContext)
112
-
113
- if (results.length > 0 && results[0].kind === 'semantic') {
114
- const diag = results[0].metadata.diagnostics as Record<string, unknown>
115
- expect(typeof diag.periodicityScore).toBe('number')
116
- expect(typeof diag.transitionSmoothness).toBe('number')
117
- }
118
- })
119
- })
@@ -1,179 +0,0 @@
1
- /**
2
- * Tests for DefaultDecision
3
- */
4
-
5
- import { describe, it, expect } from 'vitest'
6
- import { DefaultDecision } from '../DefaultDecision'
7
- import { ContributionKind } from '../../../Contracts/Contribution'
8
- import type { AnalysisReport } from '../../../Contracts/AnalysisReport'
9
- import type { PipelineContext } from '../../../Contracts/PipelineContext'
10
- import { PipelineMode, ExportTarget } from '../../../Contracts/PipelineContext'
11
-
12
- const baseReport: AnalysisReport = {
13
- contributions: [],
14
- passCount: 1,
15
- elapsedMs: 10,
16
- }
17
-
18
- function osuContext(overrides: Partial<PipelineContext> = {}): PipelineContext {
19
- return {
20
- mode: PipelineMode.Batch,
21
- target: ExportTarget.Osu,
22
- featureFlags: {},
23
- metadata: {},
24
- ...overrides,
25
- }
26
- }
27
-
28
- function miliastryContext(overrides: Partial<PipelineContext> = {}): PipelineContext {
29
- return {
30
- mode: PipelineMode.Interactive,
31
- target: ExportTarget.Miliastry,
32
- featureFlags: {},
33
- metadata: {},
34
- ...overrides,
35
- }
36
- }
37
-
38
- describe('DefaultDecision', () => {
39
- describe('gradient detection', () => {
40
- it('excludes gradients below the threshold for osu target', () => {
41
- const decision = new DefaultDecision()
42
- const report: AnalysisReport = {
43
- ...baseReport,
44
- contributions: [
45
- {
46
- kind: ContributionKind.Semantic,
47
- label: 'Gradient',
48
- confidence: 0.5, // Below threshold
49
- range: { start: 0, end: 50 },
50
- metadata: {
51
- model: { colors: ['#FF0000', '#00FF00'], easing: 'linear', charCount: 5 },
52
- diagnostics: {},
53
- },
54
- },
55
- ],
56
- }
57
-
58
- const plan = decision.run(report, osuContext())
59
- expect(plan.actions).toHaveLength(0)
60
- })
61
-
62
- it('includes gradients above auto-threshold for osu target', () => {
63
- const decision = new DefaultDecision()
64
- const report: AnalysisReport = {
65
- ...baseReport,
66
- contributions: [
67
- {
68
- kind: ContributionKind.Semantic,
69
- label: 'Gradient',
70
- confidence: 0.96, // Above osu threshold (0.95)
71
- range: { start: 0, end: 50 },
72
- metadata: {
73
- model: { colors: ['#FF0000', '#EE1100', '#DD2200'], easing: 'linear', charCount: 3 },
74
- diagnostics: {},
75
- },
76
- },
77
- ],
78
- }
79
-
80
- const plan = decision.run(report, osuContext())
81
- expect(plan.actions).toHaveLength(1)
82
- expect(plan.actions[0].kind).toBe('collapse-gradient')
83
- })
84
-
85
- it('uses lower threshold for miliastry target', () => {
86
- const decision = new DefaultDecision()
87
- const report: AnalysisReport = {
88
- ...baseReport,
89
- contributions: [
90
- {
91
- kind: ContributionKind.Semantic,
92
- label: 'Gradient',
93
- confidence: 0.7, // Above miliastry threshold (0.6)
94
- range: { start: 0, end: 50 },
95
- metadata: {
96
- model: { colors: ['#FF0000', '#EE1100', '#DD2200'], easing: 'linear', charCount: 3 },
97
- diagnostics: {},
98
- },
99
- },
100
- ],
101
- }
102
-
103
- const plan = decision.run(report, miliastryContext())
104
- expect(plan.actions).toHaveLength(1)
105
- expect(plan.actions[0].kind).toBe('collapse-gradient')
106
- })
107
-
108
- it('uses forceCollapse for very high confidence', () => {
109
- const decision = new DefaultDecision()
110
- const report: AnalysisReport = {
111
- ...baseReport,
112
- contributions: [
113
- {
114
- kind: ContributionKind.Semantic,
115
- label: 'Gradient',
116
- confidence: 0.9,
117
- range: { start: 0, end: 50 },
118
- metadata: {
119
- model: { colors: ['#FF0000', '#EE1100', '#DD2200'], easing: 'linear', charCount: 3 },
120
- diagnostics: {},
121
- },
122
- },
123
- ],
124
- }
125
-
126
- const plan = decision.run(report, miliastryContext())
127
- // 0.9 ≥ 0.85 = force collapse
128
- expect(plan.actions[0].payload.autoCollapse).toBe(true)
129
- })
130
- })
131
-
132
- describe('optimization detection', () => {
133
- it('includes merge-colors optimizations', () => {
134
- const decision = new DefaultDecision()
135
- const report: AnalysisReport = {
136
- ...baseReport,
137
- contributions: [
138
- {
139
- kind: ContributionKind.Optimization,
140
- label: 'Merge Colors',
141
- description: '3 consecutive [color=#FF0000] tags',
142
- estimatedImprovement: '-66%',
143
- range: { start: 0, end: 30 },
144
- },
145
- ],
146
- }
147
-
148
- const plan = decision.run(report, osuContext())
149
- expect(plan.actions).toHaveLength(1)
150
- expect(plan.actions[0].kind).toBe('merge-colors')
151
- })
152
- })
153
-
154
- describe('edge cases', () => {
155
- it('returns empty plan for empty report', () => {
156
- const decision = new DefaultDecision()
157
- const plan = decision.run(baseReport, osuContext())
158
- expect(plan.actions).toHaveLength(0)
159
- })
160
-
161
- it('ignores non-semantic and non-optimization contributions', () => {
162
- const decision = new DefaultDecision()
163
- const report: AnalysisReport = {
164
- ...baseReport,
165
- contributions: [
166
- { kind: ContributionKind.Metrics, metrics: { characters: 100 } },
167
- {
168
- kind: ContributionKind.Diagnostic,
169
- severity: 'info',
170
- message: 'Test diagnostic',
171
- },
172
- ],
173
- }
174
-
175
- const plan = decision.run(report, osuContext())
176
- expect(plan.actions).toHaveLength(0)
177
- })
178
- })
179
- })