@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.
- package/README.md +1 -1
- package/dist/Visuals/lyne.css +374 -9
- package/dist/Visuals/osu.css +393 -11
- package/dist/index.js +10470 -3336
- package/dist/index.mjs +10336 -3337
- 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 +102 -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 +19 -0
- package/dist/types/Visitors/HTMLRenderer.d.ts +492 -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 +54 -0
- package/dist/types/Visuals/LyneAudio.d.ts +8 -0
- package/dist/types/Visuals/index.d.ts +39 -0
- package/dist/types/index.d.ts +155 -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 +39 -1
- 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 +193 -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 +1845 -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 +365 -32
- package/src/Visitors/BlockPatcher.ts +123 -22
- package/src/Visitors/DOMMorpher.ts +44 -28
- package/src/Visitors/HTMLRenderer.ts +771 -205
- package/src/Visitors/MarkdownExporter.ts +98 -41
- package/src/Visitors/TiptapExporter.ts +1 -0
- package/src/Visitors/index.ts +1 -1
- package/src/Visuals/BoxDrawer.ts +56 -10
- package/src/Visuals/LyneAudio.ts +346 -0
- package/src/Visuals/index.ts +8 -4
- package/src/Visuals/lyne.css +374 -9
- package/src/Visuals/osu.css +393 -11
- package/src/index.ts +178 -8
- 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,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('<strong>Literal</strong>')
|
|
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('"')
|
|
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&t=83000">1:23</a>')
|
|
254
|
-
expect(html).toContain('<a class="bb-timeref" href="line://edit?map=42&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
|
-
})
|