@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.
- package/dist/Visuals/lyne.css +117 -6
- package/dist/Visuals/osu.css +36 -5
- package/dist/index.js +9664 -3282
- package/dist/index.mjs +9535 -3283
- package/dist/types/Analysis/Contracts/AnalysisReport.d.ts +22 -0
- package/dist/types/Analysis/Contracts/Contribution.d.ts +68 -0
- package/dist/types/Analysis/Contracts/Pass.d.ts +59 -0
- package/dist/types/Analysis/Contracts/PipelineContext.d.ts +34 -0
- package/dist/types/Analysis/Passes/Analysis/ColorUsageAnalyzer.d.ts +56 -0
- package/dist/types/Analysis/Passes/Analysis/GradientAnalyzer.d.ts +111 -0
- package/dist/types/Analysis/Passes/Analysis/MergeableColorAnalyzer.d.ts +26 -0
- package/dist/types/Analysis/Passes/Analysis/RainbowAnalyzer.d.ts +47 -0
- package/dist/types/Analysis/Passes/Analysis/SymbolAnalyzer.d.ts +89 -0
- package/dist/types/Analysis/Passes/Analysis/WaveAnalyzer.d.ts +44 -0
- package/dist/types/Analysis/Passes/Analysis/__tests__/GradientAnalyzer.test.d.ts +4 -0
- package/dist/types/Analysis/Passes/Analysis/__tests__/MergeableColorAnalyzer.test.d.ts +4 -0
- package/dist/types/Analysis/Passes/Analysis/__tests__/RainbowAnalyzer.test.d.ts +4 -0
- package/dist/types/Analysis/Passes/Analysis/__tests__/WaveAnalyzer.test.d.ts +4 -0
- package/dist/types/Analysis/Passes/Decision/DefaultDecision.d.ts +30 -0
- package/dist/types/Analysis/Passes/Decision/PaletteRemapDecision.d.ts +169 -0
- package/dist/types/Analysis/Passes/Decision/__tests__/DefaultDecision.test.d.ts +4 -0
- package/dist/types/Analysis/Passes/Transform/CollapseGradientTransform.d.ts +37 -0
- package/dist/types/Analysis/Passes/Transform/MergeColorsTransform.d.ts +28 -0
- package/dist/types/Analysis/Passes/Transform/RainbowCollapseTransform.d.ts +19 -0
- package/dist/types/Analysis/Passes/Transform/WaveCollapseTransform.d.ts +20 -0
- package/dist/types/Analysis/Passes/Utility/CharacterCountAnalyzer.d.ts +19 -0
- package/dist/types/Analysis/Pipeline/Pipeline.d.ts +69 -0
- package/dist/types/Analysis/Pipeline/PipelineBuilder.d.ts +31 -0
- package/dist/types/Analysis/Pipeline/PipelineStage.d.ts +17 -0
- package/dist/types/Analysis/Utils/color-utils.d.ts +60 -0
- package/dist/types/Analysis/__tests__/Integration.test.d.ts +8 -0
- package/dist/types/Analysis/__tests__/PaletteRemap.test.d.ts +14 -0
- package/dist/types/Analysis/__tests__/Pipeline.test.d.ts +10 -0
- package/dist/types/Analysis/__tests__/RoundTrip.test.d.ts +30 -0
- package/dist/types/Analysis/__tests__/SymbolAnalyzer.test.d.ts +11 -0
- package/dist/types/Analysis/index.d.ts +40 -0
- package/dist/types/BBCode/BBCodeDocumentModel.d.ts +92 -0
- package/dist/types/BBCode/BBCodeToGreenNode.d.ts +111 -0
- package/dist/types/BBCode/Parser.d.ts +78 -0
- package/dist/types/BBCode/index.d.ts +4 -0
- package/dist/types/Collab/positions.d.ts +55 -0
- package/dist/types/Commands/Command.d.ts +37 -0
- package/dist/types/Commands/CommandRegistry.d.ts +48 -0
- package/dist/types/Commands/DeleteNode.d.ts +7 -0
- package/dist/types/Commands/InsertText.d.ts +8 -0
- package/dist/types/Commands/SplitMerge.d.ts +9 -0
- package/dist/types/Commands/WrapInTag.d.ts +8 -0
- package/dist/types/Commands/index.d.ts +6 -0
- package/dist/types/Diff/TreeDiffer.d.ts +52 -0
- package/dist/types/Diff/__tests__/TreeDiffer.test.d.ts +1 -0
- package/dist/types/Diff/index.d.ts +2 -0
- package/dist/types/Edits/EditPlan.d.ts +144 -0
- package/dist/types/Edits/Optimizer.d.ts +78 -0
- package/dist/types/Edits/Rules/Rule.d.ts +91 -0
- package/dist/types/Edits/Rules/dropEmptyTags.d.ts +40 -0
- package/dist/types/Edits/Rules/dropRedundantNesting.d.ts +59 -0
- package/dist/types/Edits/Rules/index.d.ts +9 -0
- package/dist/types/Edits/Rules/mergeAdjacent.d.ts +93 -0
- package/dist/types/Edits/Rules/reorderWrappers.d.ts +42 -0
- package/dist/types/Edits/Rules/shortenHex.d.ts +30 -0
- package/dist/types/Edits/Rules/tagValue.d.ts +56 -0
- package/dist/types/Edits/Rules/unwrapInvisibleColor.d.ts +47 -0
- package/dist/types/Edits/applyEdits.d.ts +36 -0
- package/dist/types/Edits/index.d.ts +6 -0
- package/dist/types/Events/EventBus.d.ts +103 -0
- package/dist/types/Events/index.d.ts +2 -0
- package/dist/types/Formatter/Formatter.d.ts +32 -0
- package/dist/types/Formatter/index.d.ts +2 -0
- package/dist/types/HTML/HTMLDocumentModel.d.ts +13 -0
- package/dist/types/HTML/HTMLToGreenNode.d.ts +4 -0
- package/dist/types/Incremental/BracketIndex.d.ts +101 -0
- package/dist/types/Incremental/ChangeTracker.d.ts +69 -0
- package/dist/types/Incremental/IncrementalParser.d.ts +246 -0
- package/dist/types/Incremental/__tests__/BracketIndex.test.d.ts +1 -0
- package/dist/types/Incremental/__tests__/IncrementalParser.test.d.ts +1 -0
- package/dist/types/Incremental/index.d.ts +4 -0
- package/dist/types/Lexer/BBCodeLexer.d.ts +74 -0
- package/dist/types/Lexer/Lexer.d.ts +54 -0
- package/dist/types/Lexer/index.d.ts +4 -0
- package/dist/types/Linter/Linter.d.ts +58 -0
- package/dist/types/Linter/index.d.ts +2 -0
- package/dist/types/Markdown/MarkdownAST.d.ts +122 -0
- package/dist/types/Markdown/MarkdownDocumentModel.d.ts +15 -0
- package/dist/types/Markdown/MarkdownLexer.d.ts +155 -0
- package/dist/types/Markdown/MarkdownParser.d.ts +42 -0
- package/dist/types/Markdown/MarkdownToGreenNode.d.ts +6 -0
- package/dist/types/MilHibri/MilHibriDocumentModel.d.ts +21 -0
- package/dist/types/Model/DocumentModel.d.ts +318 -0
- package/dist/types/Model/NodeFactory.d.ts +29 -0
- package/dist/types/Model/TagRegistry.d.ts +165 -0
- package/dist/types/Model/index.d.ts +5 -0
- package/dist/types/Plugins/PluginAPI.d.ts +44 -0
- package/dist/types/Plugins/PluginRegistry.d.ts +81 -0
- package/dist/types/Plugins/index.d.ts +3 -0
- package/dist/types/Queries/QueryEngine.d.ts +32 -0
- package/dist/types/Queries/index.d.ts +1 -0
- package/dist/types/Reconciler/SurgicalReconciler.d.ts +23 -0
- package/dist/types/RenderPipeline/RenderPipeline.d.ts +46 -0
- package/dist/types/RenderPipeline/RenderTree.d.ts +55 -0
- package/dist/types/RenderPipeline/index.d.ts +4 -0
- package/dist/types/Repair/NestingRepair.d.ts +48 -0
- package/dist/types/Repair/index.d.ts +1 -0
- package/dist/types/Semantic/SemanticAnalyzer.d.ts +321 -0
- package/dist/types/Semantic/index.d.ts +2 -0
- package/dist/types/Symbols/SymbolTable.d.ts +47 -0
- package/dist/types/Symbols/index.d.ts +1 -0
- package/dist/types/Syntax/GreenNode.d.ts +154 -0
- package/dist/types/Syntax/GreenNodePool.d.ts +134 -0
- package/dist/types/Syntax/NodeMatcher.d.ts +75 -0
- package/dist/types/Syntax/RedNode.d.ts +280 -0
- package/dist/types/Syntax/RedNodeStore.d.ts +116 -0
- package/dist/types/Syntax/TreeBuilder.d.ts +54 -0
- package/dist/types/Syntax/__tests__/GreenNode.test.d.ts +1 -0
- package/dist/types/Syntax/__tests__/RedNode.test.d.ts +1 -0
- package/dist/types/Syntax/__tests__/RedNodeStore.test.d.ts +1 -0
- package/dist/types/Syntax/greenEdit.d.ts +64 -0
- package/dist/types/Syntax/hash.d.ts +17 -0
- package/dist/types/Syntax/index.d.ts +8 -0
- package/dist/types/Syntax/nodeAttr.d.ts +63 -0
- package/dist/types/Syntax/partition.d.ts +67 -0
- package/dist/types/Syntax/preserveNodeIds.d.ts +47 -0
- package/dist/types/Tokens/TokenResolver.d.ts +25 -0
- package/dist/types/Tokens/index.d.ts +1 -0
- package/dist/types/Transactions/Transaction.d.ts +36 -0
- package/dist/types/Transactions/UndoManager.d.ts +63 -0
- package/dist/types/Transactions/index.d.ts +3 -0
- package/dist/types/Transformers/ASTOptimizer.d.ts +34 -0
- package/dist/types/Transformers/GradientTransformer.d.ts +18 -0
- package/dist/types/Transformers/GrowTransformer.d.ts +16 -0
- package/dist/types/Transformers/RainbowTransformer.d.ts +17 -0
- package/dist/types/Transformers/SineWaveTransformer.d.ts +17 -0
- package/dist/types/Transformers/Transformer.d.ts +17 -0
- package/dist/types/Types/core.d.ts +104 -0
- package/dist/types/Types/diagnostics.d.ts +85 -0
- package/dist/types/Types/index.d.ts +11 -0
- package/dist/types/Types/operations.d.ts +109 -0
- package/dist/types/Types/queries.d.ts +67 -0
- package/dist/types/Types/symbols.d.ts +42 -0
- package/dist/types/Types/tokens.d.ts +75 -0
- package/dist/types/Utils/BBCodeGenerator.d.ts +21 -0
- package/dist/types/Utils/ColorMath.d.ts +92 -0
- package/dist/types/Utils/EffectMath.d.ts +643 -0
- package/dist/types/Utils/TagVocabulary.d.ts +104 -0
- package/dist/types/Utils/TagVocabulary.test.d.ts +1 -0
- package/dist/types/Utils/color.d.ts +29 -0
- package/dist/types/Utils/dom-to-svg.d.ts +28 -0
- package/dist/types/Utils/dom-to-svg.test.d.ts +1 -0
- package/dist/types/Utils/treeTransformers.d.ts +160 -0
- package/dist/types/Visitors/BBBlocksExporter.d.ts +27 -0
- package/dist/types/Visitors/BBCodeExporter.d.ts +90 -0
- package/dist/types/Visitors/BlockPatcher.d.ts +91 -0
- package/dist/types/Visitors/DOMMorpher.d.ts +13 -0
- package/dist/types/Visitors/HTMLRenderer.d.ts +380 -0
- package/dist/types/Visitors/JSONExporter.d.ts +42 -0
- package/dist/types/Visitors/MarkdownExporter.d.ts +28 -0
- package/dist/types/Visitors/SVGRenderer.d.ts +16 -0
- package/dist/types/Visitors/TiptapExporter.d.ts +40 -0
- package/dist/types/Visitors/Visitor.d.ts +35 -0
- package/dist/types/Visitors/index.d.ts +9 -0
- package/dist/types/Visuals/BoxDrawer.d.ts +49 -0
- package/dist/types/Visuals/index.d.ts +38 -0
- package/dist/types/index.d.ts +154 -0
- package/package.json +14 -10
- package/src/Analysis/Passes/Analysis/ColorUsageAnalyzer.ts +103 -0
- package/src/Analysis/Passes/Analysis/GradientAnalyzer.ts +294 -20
- package/src/Analysis/Passes/Analysis/SymbolAnalyzer.ts +257 -0
- package/src/Analysis/Passes/Decision/PaletteRemapDecision.ts +425 -0
- package/src/Analysis/Pipeline/Pipeline.ts +15 -2
- package/src/Analysis/index.ts +8 -2
- package/src/BBCode/BBCodeDocumentModel.ts +3 -0
- package/src/BBCode/BBCodeToGreenNode.ts +18 -12
- package/src/BBCode/Parser.ts +38 -1
- package/src/Edits/EditPlan.ts +290 -0
- package/src/Edits/Optimizer.ts +185 -0
- package/src/Edits/Rules/Rule.ts +145 -0
- package/src/Edits/Rules/dropEmptyTags.ts +110 -0
- package/src/Edits/Rules/dropRedundantNesting.ts +120 -0
- package/src/Edits/Rules/index.ts +28 -0
- package/src/Edits/Rules/mergeAdjacent.ts +224 -0
- package/src/Edits/Rules/reorderWrappers.ts +154 -0
- package/src/Edits/Rules/shortenHex.ts +79 -0
- package/src/Edits/Rules/tagValue.ts +105 -0
- package/src/Edits/Rules/unwrapInvisibleColor.ts +93 -0
- package/src/Edits/applyEdits.ts +54 -0
- package/src/Edits/index.ts +22 -0
- package/src/Events/EventBus.ts +18 -0
- package/src/HTML/HTMLToGreenNode.ts +185 -37
- package/src/Incremental/BracketIndex.ts +249 -0
- package/src/Incremental/IncrementalParser.ts +464 -41
- package/src/Lexer/BBCodeLexer.ts +25 -3
- package/src/Lexer/index.ts +1 -1
- package/src/Markdown/MarkdownAST.ts +65 -2
- package/src/Markdown/MarkdownLexer.ts +69 -14
- package/src/Markdown/MarkdownParser.ts +749 -62
- package/src/Markdown/MarkdownToGreenNode.ts +148 -27
- package/src/MilHibri/MilHibriDocumentModel.ts +56 -0
- package/src/Model/DocumentModel.ts +138 -5
- package/src/Model/TagRegistry.ts +129 -134
- package/src/Reconciler/SurgicalReconciler.ts +460 -0
- package/src/Repair/NestingRepair.ts +116 -0
- package/src/Repair/index.ts +1 -0
- package/src/Semantic/SemanticAnalyzer.ts +1802 -65
- package/src/Syntax/GreenNodePool.ts +12 -12
- package/src/Syntax/RedNode.ts +17 -3
- package/src/Syntax/RedNodeStore.ts +9 -1
- package/src/Syntax/nodeAttr.ts +172 -0
- package/src/Tokens/TokenResolver.ts +55 -0
- package/src/Tokens/index.ts +1 -0
- package/src/Types/core.ts +15 -0
- package/src/Utils/ColorMath.ts +276 -55
- package/src/Utils/EffectMath.ts +2462 -0
- package/src/Utils/TagVocabulary.ts +141 -0
- package/src/Utils/dom-to-svg.ts +28 -12
- package/src/Visitors/BBBlocksExporter.ts +15 -1
- package/src/Visitors/BBCodeExporter.ts +319 -32
- package/src/Visitors/BlockPatcher.ts +87 -11
- package/src/Visitors/DOMMorpher.ts +15 -5
- package/src/Visitors/HTMLRenderer.ts +374 -151
- package/src/Visitors/MarkdownExporter.ts +98 -41
- package/src/Visitors/TiptapExporter.ts +1 -0
- package/src/Visuals/index.ts +7 -4
- package/src/Visuals/lyne.css +117 -6
- package/src/Visuals/osu.css +36 -5
- package/src/index.ts +172 -5
- package/dist/index.d.mts +0 -4612
- package/dist/index.d.ts +0 -4612
- package/src/Analysis/Passes/Analysis/__tests__/GradientAnalyzer.test.ts +0 -135
- package/src/Analysis/Passes/Analysis/__tests__/MergeableColorAnalyzer.test.ts +0 -84
- package/src/Analysis/Passes/Analysis/__tests__/RainbowAnalyzer.test.ts +0 -99
- package/src/Analysis/Passes/Analysis/__tests__/WaveAnalyzer.test.ts +0 -119
- package/src/Analysis/Passes/Decision/__tests__/DefaultDecision.test.ts +0 -179
- package/src/Analysis/__tests__/Integration.test.ts +0 -162
- package/src/Analysis/__tests__/Pipeline.test.ts +0 -133
- package/src/Diff/__tests__/TreeDiffer.test.ts +0 -65
- package/src/Incremental/__tests__/IncrementalParser.test.ts +0 -164
- package/src/Syntax/__tests__/GreenNode.test.ts +0 -33
- package/src/Syntax/__tests__/RedNode.test.ts +0 -81
- package/src/Syntax/__tests__/RedNodeStore.test.ts +0 -104
- package/src/Tests/ASTOptimizerIdempotence.test.ts +0 -77
- package/src/Tests/BlockPatcher.test.ts +0 -437
- package/src/Tests/BlockPatcherWindowed.test.ts +0 -364
- package/src/Tests/BoxDrawer.test.ts +0 -217
- package/src/Tests/BoxRichTitle.test.ts +0 -105
- package/src/Tests/Chars500kBenchmark.test.ts +0 -151
- package/src/Tests/Chars500kEdits.test.ts +0 -321
- package/src/Tests/CollabPositions.test.ts +0 -146
- package/src/Tests/CompilerPathProfiling.test.ts +0 -186
- package/src/Tests/DOMMorpher.test.ts +0 -142
- package/src/Tests/DomPatchPerf.test.ts +0 -60
- package/src/Tests/EffectSegments.snapshot.json +0 -616
- package/src/Tests/EffectSegments.test.ts +0 -68
- package/src/Tests/FindNodeAtOffset.test.ts +0 -65
- package/src/Tests/Fuzzer.test.ts +0 -166
- package/src/Tests/GreenNodePool.test.ts +0 -153
- package/src/Tests/Lexer.test.ts +0 -238
- package/src/Tests/LyneMode.test.ts +0 -291
- package/src/Tests/ModelCoherence.test.ts +0 -180
- package/src/Tests/Partition.test.ts +0 -238
- package/src/Tests/PluginTags.test.ts +0 -150
- package/src/Tests/ProblematicSection.test.ts +0 -46
- package/src/Tests/ProblematicSectionHTML.test.ts +0 -58
- package/src/Tests/RedReuse.test.ts +0 -134
- package/src/Tests/ReproDelete20k.test.ts +0 -62
- package/src/Tests/SemanticValidators.test.ts +0 -136
- package/src/Tests/StableNodeIds.test.ts +0 -210
- package/src/Tests/StudioColorBloat.test.ts +0 -25
- package/src/Tests/StudioDebugText.test.ts +0 -27
- package/src/Tests/StudioTrailingChar.test.ts +0 -25
- package/src/Tests/StudioValidText.test.ts +0 -25
- package/src/Tests/UrlImgBug.test.ts +0 -23
- package/src/Tests/VisualBuilderFidelity.test.ts +0 -105
- package/src/Tests/referenceDocument.ts +0 -119
- 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
|
-
})
|