@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
|
@@ -12,6 +12,12 @@ import { RedNode } from '../Syntax/RedNode'
|
|
|
12
12
|
import { Visitor } from './Visitor'
|
|
13
13
|
import type { VisitorContext } from './Visitor'
|
|
14
14
|
import { TagRegistry, type TagDefinition } from '../Model/TagRegistry'
|
|
15
|
+
import {
|
|
16
|
+
toTokenResolver,
|
|
17
|
+
resolveTokenValue,
|
|
18
|
+
type TokenResolverFn,
|
|
19
|
+
type TokenSource,
|
|
20
|
+
} from '../Tokens'
|
|
15
21
|
|
|
16
22
|
/**
|
|
17
23
|
* Export target for BBCodeExporter.
|
|
@@ -21,8 +27,42 @@ import { TagRegistry, type TagDefinition } from '../Model/TagRegistry'
|
|
|
21
27
|
*/
|
|
22
28
|
export type ExportTarget = 'osu' | 'miliastry' | 'lyne'
|
|
23
29
|
|
|
30
|
+
export interface BBCodeExporterOptions {
|
|
31
|
+
registry?: TagRegistry
|
|
32
|
+
target?: ExportTarget
|
|
33
|
+
tokens?: TokenSource
|
|
34
|
+
resolveTokens?: boolean
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type BBCodeExportOptions = {
|
|
38
|
+
target?: ExportTarget
|
|
39
|
+
tokens?: TokenSource
|
|
40
|
+
resolveTokens?: boolean
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Referencia a un token de diseño dentro de un texto: `$nombre`.
|
|
45
|
+
*
|
|
46
|
+
* A nivel de módulo porque el sitio caliente es la rama de nodo TEXTO, la
|
|
47
|
+
* clase de nodo más frecuente de cualquier documento: un literal de expresión
|
|
48
|
+
* regular ahí dentro construye un `RegExp` por nodo. Había además tres copias
|
|
49
|
+
* idénticas del mismo patrón repartidas por el fichero.
|
|
50
|
+
*/
|
|
51
|
+
const TOKEN_REF_RE = /\$([a-zA-Z_][a-zA-Z0-9_-]*)/g
|
|
52
|
+
|
|
53
|
+
/** Sustituye cada `$token` por su valor; deja intacto el que no resuelva. */
|
|
54
|
+
function expandTokenRefs(text: string, resolve: TokenResolverFn): string {
|
|
55
|
+
// `indexOf` antes que `replace`: la inmensa mayoría de los textos no lleva
|
|
56
|
+
// ningún `$`, y así no se pone en marcha el motor de expresiones regulares.
|
|
57
|
+
if (text.indexOf('$') === -1) return text
|
|
58
|
+
return text.replace(TOKEN_REF_RE, (match, name: string) => {
|
|
59
|
+
const resolved = resolve(name) ?? resolve(match)
|
|
60
|
+
return resolved !== undefined ? resolved : match
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
24
64
|
/** Miliastry-native effect tags that osu! doesn't support natively */
|
|
25
|
-
const MILIASTRY_INTERNAL_TAGS = new Set(['gradient', 'grow', 'sinewave', 'rainbow'])
|
|
65
|
+
const MILIASTRY_INTERNAL_TAGS = new Set(['gradient', 'grow', 'sinewave', 'rainbow', 'paint'])
|
|
26
66
|
|
|
27
67
|
/**
|
|
28
68
|
* Tags que existen SOLO en Miliastry (registrados en el TagRegistry) y que
|
|
@@ -39,6 +79,9 @@ export const LYNE_ONLY_TAGS = new Set([
|
|
|
39
79
|
'boxw', 'wnotice', 'tables', 'table_row', 'table_col', 'table_th', 'gallery', 'columns',
|
|
40
80
|
'separator', 'scroll', 'sup', 'sub', 'abbr', 'mark', 'kbd', 'tooltip', 'flip',
|
|
41
81
|
'raw', 'plain', 'guild', 'map', 'align', 'effect', 'anim', 'container', 'style_tag',
|
|
82
|
+
'neon', 'shimmer', 'glitch', 'typewriter', 'wave', 'fire', 'ice', 'ghost', 'glow',
|
|
83
|
+
'outline', 'emboss', 'engrave', 'pulse', 'bounce', 'shake', 'levitate', 'fade-in', 'fade-out',
|
|
84
|
+
'card', 'glass', 'neon-box', 'square', 'row', 'col', 'th',
|
|
42
85
|
])
|
|
43
86
|
|
|
44
87
|
/**
|
|
@@ -89,11 +132,17 @@ const KIND_TO_TAG_NAME: Record<string, string> = {
|
|
|
89
132
|
flower: 'flower',
|
|
90
133
|
gradient: 'gradient',
|
|
91
134
|
grow: 'grow',
|
|
135
|
+
rainbow: 'rainbow',
|
|
136
|
+
sinewave: 'sinewave',
|
|
137
|
+
paint: 'paint',
|
|
92
138
|
align: 'align',
|
|
93
139
|
tables: 'tables',
|
|
94
140
|
table_row: 'row',
|
|
95
141
|
table_col: 'col',
|
|
96
142
|
table_th: 'th',
|
|
143
|
+
row: 'row',
|
|
144
|
+
col: 'col',
|
|
145
|
+
th: 'th',
|
|
97
146
|
gallery: 'gallery',
|
|
98
147
|
columns: 'columns',
|
|
99
148
|
separator: 'separator',
|
|
@@ -111,18 +160,147 @@ const KIND_TO_TAG_NAME: Record<string, string> = {
|
|
|
111
160
|
anim: 'anim',
|
|
112
161
|
container: 'container',
|
|
113
162
|
style_tag: 'style',
|
|
163
|
+
neon: 'neon',
|
|
164
|
+
shimmer: 'shimmer',
|
|
165
|
+
glitch: 'glitch',
|
|
166
|
+
typewriter: 'typewriter',
|
|
167
|
+
wave: 'wave',
|
|
168
|
+
fire: 'fire',
|
|
169
|
+
ice: 'ice',
|
|
170
|
+
ghost: 'ghost',
|
|
171
|
+
glow: 'glow',
|
|
172
|
+
outline: 'outline',
|
|
173
|
+
emboss: 'emboss',
|
|
174
|
+
engrave: 'engrave',
|
|
175
|
+
pulse: 'pulse',
|
|
176
|
+
bounce: 'bounce',
|
|
177
|
+
shake: 'shake',
|
|
178
|
+
levitate: 'levitate',
|
|
179
|
+
'fade-in': 'fade-in',
|
|
180
|
+
'fade-out': 'fade-out',
|
|
181
|
+
card: 'card',
|
|
182
|
+
glass: 'glass',
|
|
183
|
+
'neon-box': 'neon-box',
|
|
184
|
+
square: 'square',
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Whether a parsed attribute actually carries a value worth re-emitting.
|
|
190
|
+
*
|
|
191
|
+
* The parser stores an empty string for the attribute of a bare tag, so a
|
|
192
|
+
* plain `[box]` arrives here with `rawTitle: ''` rather than `undefined`.
|
|
193
|
+
* The guards below used to test `!== undefined`, which an empty string
|
|
194
|
+
* passes — exporting `[box]` as `[box=]` and `[quote]` as `[quote=""]`.
|
|
195
|
+
* Both are corruption, not normalization: bare boxes and quotes are
|
|
196
|
+
* ordinary BBCode and must round-trip untouched.
|
|
197
|
+
*/
|
|
198
|
+
function hasAttrValue(value: unknown): boolean {
|
|
199
|
+
return value !== undefined && value !== null && String(value) !== ''
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Expande un cuerpo hexadecimal a los 6 dígitos que exige osu, o `null` si no
|
|
204
|
+
* es un hex de longitud reconocible (3, 4, 6 u 8).
|
|
205
|
+
*
|
|
206
|
+
* El canal alfa se descarta a propósito: osu no soporta transparencia en
|
|
207
|
+
* ninguna forma, así que la alternativa a perderlo es publicar un `[color=…]`
|
|
208
|
+
* que su parser rechaza y deja como texto literal en la página.
|
|
209
|
+
*/
|
|
210
|
+
function expandHexForOsu(body: string): string | null {
|
|
211
|
+
if (!/^[0-9a-fA-F]+$/.test(body)) return null
|
|
212
|
+
switch (body.length) {
|
|
213
|
+
case 3:
|
|
214
|
+
return body[0] + body[0] + body[1] + body[1] + body[2] + body[2]
|
|
215
|
+
case 4:
|
|
216
|
+
// RGBA corto: expandimos RGB y tiramos el alfa.
|
|
217
|
+
return body[0] + body[0] + body[1] + body[1] + body[2] + body[2]
|
|
218
|
+
case 6:
|
|
219
|
+
// Ya es legal: se devuelve tal cual, sin tocar mayúsculas del autor.
|
|
220
|
+
return body
|
|
221
|
+
case 8:
|
|
222
|
+
// RGBA largo: los dos últimos dígitos son el alfa.
|
|
223
|
+
return body.slice(0, 6)
|
|
224
|
+
default:
|
|
225
|
+
return null
|
|
226
|
+
}
|
|
114
227
|
}
|
|
115
228
|
|
|
229
|
+
/**
|
|
230
|
+
* `target` decide si además de convertir `rgb()` hay que adaptar el hex a la
|
|
231
|
+
* gramática de osu (`BBCodeForDB::parseColour`), que sólo acepta `#` + 6
|
|
232
|
+
* dígitos hex o una secuencia puramente alfabética. Para 'miliastry' y 'lyne'
|
|
233
|
+
* el comportamiento es el de siempre: no se reescribe nada.
|
|
234
|
+
*/
|
|
235
|
+
function normalizeColorToHex(color: string, target: ExportTarget = 'miliastry'): string {
|
|
236
|
+
if (!color) return color
|
|
237
|
+
const trimmed = color.trim()
|
|
238
|
+
// Un hex ya es canónico: bajarlo a minúsculas reescribía `[color=#FF0000]`
|
|
239
|
+
// del autor en cada exportación, y `Analysis/RoundTrip` fija lo contrario.
|
|
240
|
+
if (trimmed.startsWith('#')) {
|
|
241
|
+
if (target !== 'osu') return trimmed
|
|
242
|
+
const expanded = expandHexForOsu(trimmed.slice(1))
|
|
243
|
+
return expanded === null ? trimmed : `#${expanded}`
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const rgbMatch = trimmed.match(/^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\d.]+))?\s*\)$/i)
|
|
247
|
+
if (rgbMatch) {
|
|
248
|
+
const r = parseInt(rgbMatch[1], 10).toString(16).padStart(2, '0')
|
|
249
|
+
const g = parseInt(rgbMatch[2], 10).toString(16).padStart(2, '0')
|
|
250
|
+
const b = parseInt(rgbMatch[3], 10).toString(16).padStart(2, '0')
|
|
251
|
+
return `#${r}${g}${b}`.toLowerCase()
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Hex desnudo: osu exige el `#`, así que `ff0000` se publicaría roto. Sólo
|
|
255
|
+
// lo tratamos como hex si trae algún dígito; una palabra puramente
|
|
256
|
+
// alfabética (`red`, y también `beef`, que es hex válido) ya matchea la
|
|
257
|
+
// alternativa alfabética de osu y no hay que tocarla.
|
|
258
|
+
if (target === 'osu' && /\d/.test(trimmed)) {
|
|
259
|
+
const expanded = expandHexForOsu(trimmed)
|
|
260
|
+
if (expanded !== null) return `#${expanded}`
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return trimmed
|
|
264
|
+
}
|
|
116
265
|
|
|
117
266
|
export class BBCodeExporter extends Visitor<string> {
|
|
118
267
|
private registry: TagRegistry
|
|
119
268
|
private depth: number = 0
|
|
120
269
|
private target: ExportTarget
|
|
270
|
+
private tokens?: TokenSource
|
|
271
|
+
private tokenResolver?: TokenResolverFn
|
|
272
|
+
private explicitResolveTokens?: boolean
|
|
273
|
+
/**
|
|
274
|
+
* El resolvedor a aplicar en ESTE recorrido, o `null` si no hay expansión.
|
|
275
|
+
*
|
|
276
|
+
* Se decide una vez en `visit`, no por nodo: `shouldResolveTokens()` da la
|
|
277
|
+
* misma respuesta para todo el documento, y se estaba llamando en la rama
|
|
278
|
+
* de nodo texto, la más frecuente que hay.
|
|
279
|
+
*/
|
|
280
|
+
private expandTokens: TokenResolverFn | null = null
|
|
121
281
|
|
|
122
|
-
constructor(
|
|
282
|
+
constructor(
|
|
283
|
+
registryOrOptions?: TagRegistry | BBCodeExporterOptions,
|
|
284
|
+
target: ExportTarget = 'osu',
|
|
285
|
+
options?: BBCodeExporterOptions | { tokens?: TokenSource; resolveTokens?: boolean },
|
|
286
|
+
) {
|
|
123
287
|
super()
|
|
124
|
-
|
|
125
|
-
|
|
288
|
+
if (registryOrOptions && !(registryOrOptions instanceof TagRegistry) && !('get' in registryOrOptions)) {
|
|
289
|
+
const opts = registryOrOptions as BBCodeExporterOptions
|
|
290
|
+
this.registry = opts.registry ?? new TagRegistry()
|
|
291
|
+
this.target = opts.target ?? 'osu'
|
|
292
|
+
this.tokens = opts.tokens
|
|
293
|
+
this.tokenResolver = toTokenResolver(opts.tokens)
|
|
294
|
+
this.explicitResolveTokens = opts.resolveTokens
|
|
295
|
+
} else {
|
|
296
|
+
this.registry = (registryOrOptions as TagRegistry) ?? new TagRegistry()
|
|
297
|
+
this.target = target
|
|
298
|
+
if (options) {
|
|
299
|
+
this.tokens = options.tokens
|
|
300
|
+
this.tokenResolver = toTokenResolver(options.tokens)
|
|
301
|
+
this.explicitResolveTokens = options.resolveTokens
|
|
302
|
+
}
|
|
303
|
+
}
|
|
126
304
|
}
|
|
127
305
|
|
|
128
306
|
/**
|
|
@@ -132,30 +310,95 @@ export class BBCodeExporter extends Visitor<string> {
|
|
|
132
310
|
this.target = target
|
|
133
311
|
}
|
|
134
312
|
|
|
313
|
+
setTokens(tokens?: TokenSource): void {
|
|
314
|
+
this.tokens = tokens
|
|
315
|
+
this.tokenResolver = toTokenResolver(tokens)
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
getTokens(): TokenSource | undefined {
|
|
319
|
+
return this.tokens
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
getTokenResolver(): TokenResolverFn | undefined {
|
|
323
|
+
return this.tokenResolver
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private shouldResolveTokens(): boolean {
|
|
327
|
+
if (this.explicitResolveTokens !== undefined) {
|
|
328
|
+
return this.explicitResolveTokens
|
|
329
|
+
}
|
|
330
|
+
return this.target === 'osu'
|
|
331
|
+
}
|
|
332
|
+
|
|
135
333
|
/**
|
|
136
334
|
* Export a RedNode tree to BBCode.
|
|
137
335
|
*/
|
|
138
336
|
visit(node: RedNode, context?: VisitorContext): string {
|
|
139
337
|
if (context) this.context = context
|
|
140
338
|
this.depth = 0
|
|
339
|
+
this.expandTokens =
|
|
340
|
+
this.tokenResolver !== undefined && this.shouldResolveTokens() ? this.tokenResolver : null
|
|
141
341
|
return this.exportNode(node)
|
|
142
342
|
}
|
|
143
343
|
|
|
144
344
|
/**
|
|
145
345
|
* Export the entire document to BBCode.
|
|
146
|
-
* Optionally override the export target for this specific call.
|
|
346
|
+
* Optionally override the export target or options for this specific call.
|
|
147
347
|
*/
|
|
148
|
-
export(
|
|
149
|
-
|
|
348
|
+
export(
|
|
349
|
+
root: RedNode,
|
|
350
|
+
targetOrOptions?: ExportTarget | BBCodeExportOptions,
|
|
351
|
+
options?: BBCodeExportOptions,
|
|
352
|
+
): string {
|
|
353
|
+
if (typeof targetOrOptions === 'string') {
|
|
354
|
+
this.target = targetOrOptions
|
|
355
|
+
if (options?.tokens !== undefined) {
|
|
356
|
+
this.setTokens(options.tokens)
|
|
357
|
+
}
|
|
358
|
+
if (options?.resolveTokens !== undefined) {
|
|
359
|
+
this.explicitResolveTokens = options.resolveTokens
|
|
360
|
+
}
|
|
361
|
+
} else if (targetOrOptions && typeof targetOrOptions === 'object') {
|
|
362
|
+
if (targetOrOptions.target !== undefined) {
|
|
363
|
+
this.target = targetOrOptions.target
|
|
364
|
+
}
|
|
365
|
+
if (targetOrOptions.tokens !== undefined) {
|
|
366
|
+
this.setTokens(targetOrOptions.tokens)
|
|
367
|
+
}
|
|
368
|
+
if (targetOrOptions.resolveTokens !== undefined) {
|
|
369
|
+
this.explicitResolveTokens = targetOrOptions.resolveTokens
|
|
370
|
+
}
|
|
371
|
+
}
|
|
150
372
|
return this.visit(root)
|
|
151
373
|
}
|
|
152
374
|
|
|
375
|
+
/**
|
|
376
|
+
* Exporta los hijos directos y los concatena.
|
|
377
|
+
*
|
|
378
|
+
* Reemplaza el `children.map(...).join('')`, que asignaba un cierre y un
|
|
379
|
+
* array intermedio de N cadenas en cada nivel del árbol. `HTMLRenderer`
|
|
380
|
+
* hizo este mismo cambio y dejó la nota; el exportador se quedó atrás, y es
|
|
381
|
+
* el camino que corre bajo un límite de 60.000 caracteres.
|
|
382
|
+
*/
|
|
383
|
+
private exportChildren(node: RedNode): string {
|
|
384
|
+
const children = node.children
|
|
385
|
+
let out = ''
|
|
386
|
+
for (let i = 0; i < children.length; i++) out += this.exportNode(children[i])
|
|
387
|
+
return out
|
|
388
|
+
}
|
|
389
|
+
|
|
153
390
|
private exportNode(node: RedNode): string {
|
|
391
|
+
// Un cierre que no cerró nada no vuelve al source. Escribirlo hacía que el
|
|
392
|
+
// siguiente parseo lo leyera otra vez como etiqueta viva, y el documento no
|
|
393
|
+
// convergía al reexportarlo.
|
|
394
|
+
if (node.kind === 'discarded_tag') return ''
|
|
395
|
+
|
|
154
396
|
const tagDef = this.registry.getByKind(node.kind)
|
|
155
397
|
|
|
156
398
|
// Text nodes
|
|
157
399
|
if (node.kind === 'text') {
|
|
158
400
|
let out = node.text
|
|
401
|
+
if (this.expandTokens !== null) out = expandTokenRefs(out, this.expandTokens)
|
|
159
402
|
const style = node.metadata?.style as Record<string, string> | undefined
|
|
160
403
|
if (style) {
|
|
161
404
|
// Envolvemos desde adentro hacia afuera (o al revés, no importa mucho para osu)
|
|
@@ -163,8 +406,20 @@ export class BBCodeExporter extends Visitor<string> {
|
|
|
163
406
|
if (style.fontStyle === 'italic') out = `[i]${out}[/i]`
|
|
164
407
|
if (style.textDecoration === 'underline') out = `[u]${out}[/u]`
|
|
165
408
|
if (style.textDecoration === 'line-through') out = `[s]${out}[/s]`
|
|
166
|
-
if (style.color)
|
|
167
|
-
|
|
409
|
+
if (style.color) {
|
|
410
|
+
let col = style.color
|
|
411
|
+
if (this.shouldResolveTokens() && col.startsWith('$')) {
|
|
412
|
+
col = resolveTokenValue(col, this.tokenResolver)
|
|
413
|
+
}
|
|
414
|
+
out = `[color=${normalizeColorToHex(col, this.target)}]${out}[/color]`
|
|
415
|
+
}
|
|
416
|
+
if (style.fontSize) {
|
|
417
|
+
let size = style.fontSize
|
|
418
|
+
if (this.shouldResolveTokens() && size.startsWith('$')) {
|
|
419
|
+
size = resolveTokenValue(size, this.tokenResolver)
|
|
420
|
+
}
|
|
421
|
+
out = `[size=${size}]${out}[/size]`
|
|
422
|
+
}
|
|
168
423
|
// Se pueden seguir sumando estilos dinámicos
|
|
169
424
|
}
|
|
170
425
|
return out
|
|
@@ -172,7 +427,7 @@ export class BBCodeExporter extends Visitor<string> {
|
|
|
172
427
|
|
|
173
428
|
// Document root
|
|
174
429
|
if (node.kind === 'document') {
|
|
175
|
-
return
|
|
430
|
+
return this.exportChildren(node)
|
|
176
431
|
}
|
|
177
432
|
|
|
178
433
|
// Custom tag handlers from registry
|
|
@@ -196,7 +451,7 @@ export class BBCodeExporter extends Visitor<string> {
|
|
|
196
451
|
// Default BBCode serialization
|
|
197
452
|
let tagName = this.kindToTagName(node.kind)
|
|
198
453
|
const attrs = this.getTagAttributes(node)
|
|
199
|
-
const content =
|
|
454
|
+
const content = this.exportChildren(node)
|
|
200
455
|
|
|
201
456
|
if (!tagName) {
|
|
202
457
|
if (node.kind === 'spacing') return '\n'
|
|
@@ -246,38 +501,78 @@ export class BBCodeExporter extends Visitor<string> {
|
|
|
246
501
|
if (tag?.properties) {
|
|
247
502
|
const parts: string[] = []
|
|
248
503
|
for (const prop of tag.properties) {
|
|
249
|
-
|
|
504
|
+
let val = node.metadata[prop.name]
|
|
250
505
|
if (val !== undefined && val !== null) {
|
|
506
|
+
if (this.shouldResolveTokens() && typeof val === 'string' && val.startsWith('$')) {
|
|
507
|
+
val = resolveTokenValue(val, this.tokenResolver)
|
|
508
|
+
}
|
|
251
509
|
parts.push(`${prop.name}=${val}`)
|
|
252
510
|
}
|
|
253
511
|
}
|
|
254
512
|
if (parts.length > 0) return `=${parts[0]}`
|
|
255
513
|
}
|
|
256
|
-
} else if (node.kind === 'font_size' && node.metadata.size
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
return `=${
|
|
514
|
+
} else if (node.kind === 'font_size' && hasAttrValue(node.metadata.size)) {
|
|
515
|
+
let size = String(node.metadata.size)
|
|
516
|
+
if (this.shouldResolveTokens() && size.startsWith('$')) {
|
|
517
|
+
size = resolveTokenValue(size, this.tokenResolver)
|
|
518
|
+
}
|
|
519
|
+
return `=${size}`
|
|
520
|
+
} else if (node.kind === 'color' && hasAttrValue(node.metadata.color)) {
|
|
521
|
+
let color = String(node.metadata.color)
|
|
522
|
+
if (this.shouldResolveTokens() && color.startsWith('$')) {
|
|
523
|
+
color = resolveTokenValue(color, this.tokenResolver)
|
|
524
|
+
}
|
|
525
|
+
return `=${normalizeColorToHex(color, this.target)}`
|
|
526
|
+
} else if (node.kind === 'font' && hasAttrValue(node.metadata.font)) {
|
|
527
|
+
let font = String(node.metadata.font)
|
|
528
|
+
if (this.shouldResolveTokens() && font.startsWith('$')) {
|
|
529
|
+
font = resolveTokenValue(font, this.tokenResolver)
|
|
530
|
+
}
|
|
531
|
+
return `=${font}`
|
|
262
532
|
} else if (node.kind === 'url' && node.metadata.href !== undefined) {
|
|
263
533
|
return `=${node.metadata.href}`
|
|
264
|
-
} else if (node.kind === 'email' && node.metadata.href
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
534
|
+
} else if (node.kind === 'email' && hasAttrValue(node.metadata.href)) {
|
|
535
|
+
// The parser prefixes the address with `mailto:`, so a bare `[email]`
|
|
536
|
+
// arrives as exactly that prefix and nothing else — test the address.
|
|
537
|
+
const address = (node.metadata.href as string).replace('mailto:', '')
|
|
538
|
+
return address === '' ? '' : `=${address}`
|
|
539
|
+
} else if (node.kind === 'quote' && hasAttrValue(node.metadata.source)) {
|
|
540
|
+
let src = String(node.metadata.source)
|
|
541
|
+
if (this.expandTokens !== null) src = expandTokenRefs(src, this.expandTokens)
|
|
542
|
+
return `="${src}"`
|
|
543
|
+
} else if (node.kind === 'box' || node.kind === 'boxw' || node.kind === 'spoilerbox') {
|
|
544
|
+
// `rawTitle` is the source spelling; `title` is the parsed one, which
|
|
545
|
+
// falls back to a synthetic "Box"/"Spoiler" when the tag is bare. Only
|
|
546
|
+
// the raw form can be trusted to reproduce the input, so it wins.
|
|
547
|
+
const raw = node.metadata.rawTitle
|
|
548
|
+
let titleVal = String((raw !== undefined ? raw : node.metadata.title) ?? '')
|
|
549
|
+
if (this.expandTokens !== null) titleVal = expandTokenRefs(titleVal, this.expandTokens)
|
|
270
550
|
// `[box=Title:#hex]`: el color se guardó aparte en metadata; se vuelve
|
|
271
551
|
// a añadir aquí para que el round-trip no pierda el sufijo.
|
|
272
|
-
|
|
552
|
+
let color = node.metadata.color as string | undefined
|
|
553
|
+
if (this.shouldResolveTokens() && color && color.startsWith('$')) {
|
|
554
|
+
color = resolveTokenValue(color, this.tokenResolver)
|
|
555
|
+
}
|
|
556
|
+
// A bare `[box]` has neither, and must stay bare.
|
|
557
|
+
if (titleVal === '' && !color) return ''
|
|
273
558
|
return `=${titleVal}${color ? `:${color}` : ''}`
|
|
274
559
|
} else if ((node.kind === 'notice' || node.kind === 'wnotice') && node.metadata.color !== undefined) {
|
|
275
|
-
|
|
560
|
+
let color = String(node.metadata.color)
|
|
561
|
+
if (this.shouldResolveTokens() && color.startsWith('$')) {
|
|
562
|
+
color = resolveTokenValue(color, this.tokenResolver)
|
|
563
|
+
}
|
|
564
|
+
return `=${color}`
|
|
276
565
|
} else if (node.kind === 'tables' && node.metadata.variant !== undefined) {
|
|
277
|
-
|
|
566
|
+
let color = node.metadata.color as string | undefined
|
|
567
|
+
if (this.shouldResolveTokens() && color && color.startsWith('$')) {
|
|
568
|
+
color = resolveTokenValue(color, this.tokenResolver)
|
|
569
|
+
}
|
|
278
570
|
return `=${node.metadata.variant}${color ? `:${color}` : ''}`
|
|
279
571
|
} else if (node.kind === 'columns' && node.metadata.columns !== undefined) {
|
|
280
|
-
|
|
572
|
+
let color = node.metadata.color as string | undefined
|
|
573
|
+
if (this.shouldResolveTokens() && color && color.startsWith('$')) {
|
|
574
|
+
color = resolveTokenValue(color, this.tokenResolver)
|
|
575
|
+
}
|
|
281
576
|
return `=${node.metadata.columns}${color ? `:${color}` : ''}`
|
|
282
577
|
} else if (node.kind === 'separator' && node.metadata.variant !== undefined) {
|
|
283
578
|
return `=${node.metadata.variant}`
|
|
@@ -294,22 +589,60 @@ export class BBCodeExporter extends Visitor<string> {
|
|
|
294
589
|
} else if (node.kind === 'align' && node.metadata.align !== undefined) {
|
|
295
590
|
return `=${node.metadata.align}`
|
|
296
591
|
} else if (node.kind === 'effect' && node.metadata.effectType !== undefined) {
|
|
297
|
-
|
|
592
|
+
let color = node.metadata.color as string | undefined
|
|
593
|
+
if (this.shouldResolveTokens() && color && color.startsWith('$')) {
|
|
594
|
+
color = resolveTokenValue(color, this.tokenResolver)
|
|
595
|
+
}
|
|
596
|
+
return `=${node.metadata.effectType}${color ? `:${color}` : ''}`
|
|
298
597
|
} else if (node.kind === 'image' && node.metadata.imgAttr !== undefined) {
|
|
299
598
|
return `=${node.metadata.imgAttr}`
|
|
300
599
|
} else if (node.kind === 'anim' && node.metadata.animType !== undefined) {
|
|
301
600
|
return `=${node.metadata.animType}`
|
|
302
601
|
} else if (node.kind === 'container' && node.metadata.containerType !== undefined) {
|
|
303
|
-
|
|
602
|
+
let color = node.metadata.color as string | undefined
|
|
603
|
+
if (this.shouldResolveTokens() && color && color.startsWith('$')) {
|
|
604
|
+
color = resolveTokenValue(color, this.tokenResolver)
|
|
605
|
+
}
|
|
606
|
+
return `=${node.metadata.containerType}${color ? `:${color}` : ''}`
|
|
304
607
|
} else if (node.kind === 'style_tag' && node.metadata.style !== undefined) {
|
|
305
|
-
|
|
608
|
+
let style = String(node.metadata.style)
|
|
609
|
+
if (this.shouldResolveTokens()) {
|
|
610
|
+
style = style.replace(/\$([a-zA-Z0-9_.-]+)/g, (match) => {
|
|
611
|
+
return resolveTokenValue(match, this.tokenResolver)
|
|
612
|
+
})
|
|
613
|
+
}
|
|
614
|
+
return `=${style}`
|
|
615
|
+
} else if (hasAttrValue(node.metadata.value)) {
|
|
616
|
+
let val = String(node.metadata.value)
|
|
617
|
+
if (this.shouldResolveTokens() && val.startsWith('$')) {
|
|
618
|
+
val = resolveTokenValue(val, this.tokenResolver)
|
|
619
|
+
}
|
|
620
|
+
return `=${val}`
|
|
621
|
+
} else if (hasAttrValue(node.metadata.raw)) {
|
|
622
|
+
let raw = String(node.metadata.raw)
|
|
623
|
+
if (this.shouldResolveTokens() && raw.startsWith('$')) {
|
|
624
|
+
raw = resolveTokenValue(raw, this.tokenResolver)
|
|
625
|
+
}
|
|
626
|
+
return `=${raw}`
|
|
306
627
|
}
|
|
307
628
|
}
|
|
308
629
|
|
|
309
630
|
// Fallback: check node text for attribute info
|
|
310
631
|
const text = node.text || ''
|
|
311
632
|
if (text.startsWith('=') || text.startsWith(' ')) {
|
|
312
|
-
|
|
633
|
+
if (node.kind === 'color' && text.startsWith('=')) {
|
|
634
|
+
let col = text.slice(1)
|
|
635
|
+
if (this.shouldResolveTokens() && col.startsWith('$')) {
|
|
636
|
+
col = resolveTokenValue(col, this.tokenResolver)
|
|
637
|
+
}
|
|
638
|
+
return `=${normalizeColorToHex(col, this.target)}`
|
|
639
|
+
}
|
|
640
|
+
if (this.shouldResolveTokens() && text.startsWith('=')) {
|
|
641
|
+
let val = text.slice(1)
|
|
642
|
+
if (val.startsWith('$')) {
|
|
643
|
+
return `=${resolveTokenValue(val, this.tokenResolver)}`
|
|
644
|
+
}
|
|
645
|
+
}
|
|
313
646
|
return text
|
|
314
647
|
}
|
|
315
648
|
|