@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.
Files changed (273) hide show
  1. package/dist/Visuals/lyne.css +117 -6
  2. package/dist/Visuals/osu.css +36 -5
  3. package/dist/index.js +9664 -3282
  4. package/dist/index.mjs +9535 -3283
  5. package/dist/types/Analysis/Contracts/AnalysisReport.d.ts +22 -0
  6. package/dist/types/Analysis/Contracts/Contribution.d.ts +68 -0
  7. package/dist/types/Analysis/Contracts/Pass.d.ts +59 -0
  8. package/dist/types/Analysis/Contracts/PipelineContext.d.ts +34 -0
  9. package/dist/types/Analysis/Passes/Analysis/ColorUsageAnalyzer.d.ts +56 -0
  10. package/dist/types/Analysis/Passes/Analysis/GradientAnalyzer.d.ts +111 -0
  11. package/dist/types/Analysis/Passes/Analysis/MergeableColorAnalyzer.d.ts +26 -0
  12. package/dist/types/Analysis/Passes/Analysis/RainbowAnalyzer.d.ts +47 -0
  13. package/dist/types/Analysis/Passes/Analysis/SymbolAnalyzer.d.ts +89 -0
  14. package/dist/types/Analysis/Passes/Analysis/WaveAnalyzer.d.ts +44 -0
  15. package/dist/types/Analysis/Passes/Analysis/__tests__/GradientAnalyzer.test.d.ts +4 -0
  16. package/dist/types/Analysis/Passes/Analysis/__tests__/MergeableColorAnalyzer.test.d.ts +4 -0
  17. package/dist/types/Analysis/Passes/Analysis/__tests__/RainbowAnalyzer.test.d.ts +4 -0
  18. package/dist/types/Analysis/Passes/Analysis/__tests__/WaveAnalyzer.test.d.ts +4 -0
  19. package/dist/types/Analysis/Passes/Decision/DefaultDecision.d.ts +30 -0
  20. package/dist/types/Analysis/Passes/Decision/PaletteRemapDecision.d.ts +169 -0
  21. package/dist/types/Analysis/Passes/Decision/__tests__/DefaultDecision.test.d.ts +4 -0
  22. package/dist/types/Analysis/Passes/Transform/CollapseGradientTransform.d.ts +37 -0
  23. package/dist/types/Analysis/Passes/Transform/MergeColorsTransform.d.ts +28 -0
  24. package/dist/types/Analysis/Passes/Transform/RainbowCollapseTransform.d.ts +19 -0
  25. package/dist/types/Analysis/Passes/Transform/WaveCollapseTransform.d.ts +20 -0
  26. package/dist/types/Analysis/Passes/Utility/CharacterCountAnalyzer.d.ts +19 -0
  27. package/dist/types/Analysis/Pipeline/Pipeline.d.ts +69 -0
  28. package/dist/types/Analysis/Pipeline/PipelineBuilder.d.ts +31 -0
  29. package/dist/types/Analysis/Pipeline/PipelineStage.d.ts +17 -0
  30. package/dist/types/Analysis/Utils/color-utils.d.ts +60 -0
  31. package/dist/types/Analysis/__tests__/Integration.test.d.ts +8 -0
  32. package/dist/types/Analysis/__tests__/PaletteRemap.test.d.ts +14 -0
  33. package/dist/types/Analysis/__tests__/Pipeline.test.d.ts +10 -0
  34. package/dist/types/Analysis/__tests__/RoundTrip.test.d.ts +30 -0
  35. package/dist/types/Analysis/__tests__/SymbolAnalyzer.test.d.ts +11 -0
  36. package/dist/types/Analysis/index.d.ts +40 -0
  37. package/dist/types/BBCode/BBCodeDocumentModel.d.ts +92 -0
  38. package/dist/types/BBCode/BBCodeToGreenNode.d.ts +111 -0
  39. package/dist/types/BBCode/Parser.d.ts +78 -0
  40. package/dist/types/BBCode/index.d.ts +4 -0
  41. package/dist/types/Collab/positions.d.ts +55 -0
  42. package/dist/types/Commands/Command.d.ts +37 -0
  43. package/dist/types/Commands/CommandRegistry.d.ts +48 -0
  44. package/dist/types/Commands/DeleteNode.d.ts +7 -0
  45. package/dist/types/Commands/InsertText.d.ts +8 -0
  46. package/dist/types/Commands/SplitMerge.d.ts +9 -0
  47. package/dist/types/Commands/WrapInTag.d.ts +8 -0
  48. package/dist/types/Commands/index.d.ts +6 -0
  49. package/dist/types/Diff/TreeDiffer.d.ts +52 -0
  50. package/dist/types/Diff/__tests__/TreeDiffer.test.d.ts +1 -0
  51. package/dist/types/Diff/index.d.ts +2 -0
  52. package/dist/types/Edits/EditPlan.d.ts +144 -0
  53. package/dist/types/Edits/Optimizer.d.ts +78 -0
  54. package/dist/types/Edits/Rules/Rule.d.ts +91 -0
  55. package/dist/types/Edits/Rules/dropEmptyTags.d.ts +40 -0
  56. package/dist/types/Edits/Rules/dropRedundantNesting.d.ts +59 -0
  57. package/dist/types/Edits/Rules/index.d.ts +9 -0
  58. package/dist/types/Edits/Rules/mergeAdjacent.d.ts +93 -0
  59. package/dist/types/Edits/Rules/reorderWrappers.d.ts +42 -0
  60. package/dist/types/Edits/Rules/shortenHex.d.ts +30 -0
  61. package/dist/types/Edits/Rules/tagValue.d.ts +56 -0
  62. package/dist/types/Edits/Rules/unwrapInvisibleColor.d.ts +47 -0
  63. package/dist/types/Edits/applyEdits.d.ts +36 -0
  64. package/dist/types/Edits/index.d.ts +6 -0
  65. package/dist/types/Events/EventBus.d.ts +103 -0
  66. package/dist/types/Events/index.d.ts +2 -0
  67. package/dist/types/Formatter/Formatter.d.ts +32 -0
  68. package/dist/types/Formatter/index.d.ts +2 -0
  69. package/dist/types/HTML/HTMLDocumentModel.d.ts +13 -0
  70. package/dist/types/HTML/HTMLToGreenNode.d.ts +4 -0
  71. package/dist/types/Incremental/BracketIndex.d.ts +101 -0
  72. package/dist/types/Incremental/ChangeTracker.d.ts +69 -0
  73. package/dist/types/Incremental/IncrementalParser.d.ts +246 -0
  74. package/dist/types/Incremental/__tests__/BracketIndex.test.d.ts +1 -0
  75. package/dist/types/Incremental/__tests__/IncrementalParser.test.d.ts +1 -0
  76. package/dist/types/Incremental/index.d.ts +4 -0
  77. package/dist/types/Lexer/BBCodeLexer.d.ts +74 -0
  78. package/dist/types/Lexer/Lexer.d.ts +54 -0
  79. package/dist/types/Lexer/index.d.ts +4 -0
  80. package/dist/types/Linter/Linter.d.ts +58 -0
  81. package/dist/types/Linter/index.d.ts +2 -0
  82. package/dist/types/Markdown/MarkdownAST.d.ts +122 -0
  83. package/dist/types/Markdown/MarkdownDocumentModel.d.ts +15 -0
  84. package/dist/types/Markdown/MarkdownLexer.d.ts +155 -0
  85. package/dist/types/Markdown/MarkdownParser.d.ts +42 -0
  86. package/dist/types/Markdown/MarkdownToGreenNode.d.ts +6 -0
  87. package/dist/types/MilHibri/MilHibriDocumentModel.d.ts +21 -0
  88. package/dist/types/Model/DocumentModel.d.ts +318 -0
  89. package/dist/types/Model/NodeFactory.d.ts +29 -0
  90. package/dist/types/Model/TagRegistry.d.ts +165 -0
  91. package/dist/types/Model/index.d.ts +5 -0
  92. package/dist/types/Plugins/PluginAPI.d.ts +44 -0
  93. package/dist/types/Plugins/PluginRegistry.d.ts +81 -0
  94. package/dist/types/Plugins/index.d.ts +3 -0
  95. package/dist/types/Queries/QueryEngine.d.ts +32 -0
  96. package/dist/types/Queries/index.d.ts +1 -0
  97. package/dist/types/Reconciler/SurgicalReconciler.d.ts +23 -0
  98. package/dist/types/RenderPipeline/RenderPipeline.d.ts +46 -0
  99. package/dist/types/RenderPipeline/RenderTree.d.ts +55 -0
  100. package/dist/types/RenderPipeline/index.d.ts +4 -0
  101. package/dist/types/Repair/NestingRepair.d.ts +48 -0
  102. package/dist/types/Repair/index.d.ts +1 -0
  103. package/dist/types/Semantic/SemanticAnalyzer.d.ts +321 -0
  104. package/dist/types/Semantic/index.d.ts +2 -0
  105. package/dist/types/Symbols/SymbolTable.d.ts +47 -0
  106. package/dist/types/Symbols/index.d.ts +1 -0
  107. package/dist/types/Syntax/GreenNode.d.ts +154 -0
  108. package/dist/types/Syntax/GreenNodePool.d.ts +134 -0
  109. package/dist/types/Syntax/NodeMatcher.d.ts +75 -0
  110. package/dist/types/Syntax/RedNode.d.ts +280 -0
  111. package/dist/types/Syntax/RedNodeStore.d.ts +116 -0
  112. package/dist/types/Syntax/TreeBuilder.d.ts +54 -0
  113. package/dist/types/Syntax/__tests__/GreenNode.test.d.ts +1 -0
  114. package/dist/types/Syntax/__tests__/RedNode.test.d.ts +1 -0
  115. package/dist/types/Syntax/__tests__/RedNodeStore.test.d.ts +1 -0
  116. package/dist/types/Syntax/greenEdit.d.ts +64 -0
  117. package/dist/types/Syntax/hash.d.ts +17 -0
  118. package/dist/types/Syntax/index.d.ts +8 -0
  119. package/dist/types/Syntax/nodeAttr.d.ts +63 -0
  120. package/dist/types/Syntax/partition.d.ts +67 -0
  121. package/dist/types/Syntax/preserveNodeIds.d.ts +47 -0
  122. package/dist/types/Tokens/TokenResolver.d.ts +25 -0
  123. package/dist/types/Tokens/index.d.ts +1 -0
  124. package/dist/types/Transactions/Transaction.d.ts +36 -0
  125. package/dist/types/Transactions/UndoManager.d.ts +63 -0
  126. package/dist/types/Transactions/index.d.ts +3 -0
  127. package/dist/types/Transformers/ASTOptimizer.d.ts +34 -0
  128. package/dist/types/Transformers/GradientTransformer.d.ts +18 -0
  129. package/dist/types/Transformers/GrowTransformer.d.ts +16 -0
  130. package/dist/types/Transformers/RainbowTransformer.d.ts +17 -0
  131. package/dist/types/Transformers/SineWaveTransformer.d.ts +17 -0
  132. package/dist/types/Transformers/Transformer.d.ts +17 -0
  133. package/dist/types/Types/core.d.ts +104 -0
  134. package/dist/types/Types/diagnostics.d.ts +85 -0
  135. package/dist/types/Types/index.d.ts +11 -0
  136. package/dist/types/Types/operations.d.ts +109 -0
  137. package/dist/types/Types/queries.d.ts +67 -0
  138. package/dist/types/Types/symbols.d.ts +42 -0
  139. package/dist/types/Types/tokens.d.ts +75 -0
  140. package/dist/types/Utils/BBCodeGenerator.d.ts +21 -0
  141. package/dist/types/Utils/ColorMath.d.ts +92 -0
  142. package/dist/types/Utils/EffectMath.d.ts +643 -0
  143. package/dist/types/Utils/TagVocabulary.d.ts +104 -0
  144. package/dist/types/Utils/TagVocabulary.test.d.ts +1 -0
  145. package/dist/types/Utils/color.d.ts +29 -0
  146. package/dist/types/Utils/dom-to-svg.d.ts +28 -0
  147. package/dist/types/Utils/dom-to-svg.test.d.ts +1 -0
  148. package/dist/types/Utils/treeTransformers.d.ts +160 -0
  149. package/dist/types/Visitors/BBBlocksExporter.d.ts +27 -0
  150. package/dist/types/Visitors/BBCodeExporter.d.ts +90 -0
  151. package/dist/types/Visitors/BlockPatcher.d.ts +91 -0
  152. package/dist/types/Visitors/DOMMorpher.d.ts +13 -0
  153. package/dist/types/Visitors/HTMLRenderer.d.ts +380 -0
  154. package/dist/types/Visitors/JSONExporter.d.ts +42 -0
  155. package/dist/types/Visitors/MarkdownExporter.d.ts +28 -0
  156. package/dist/types/Visitors/SVGRenderer.d.ts +16 -0
  157. package/dist/types/Visitors/TiptapExporter.d.ts +40 -0
  158. package/dist/types/Visitors/Visitor.d.ts +35 -0
  159. package/dist/types/Visitors/index.d.ts +9 -0
  160. package/dist/types/Visuals/BoxDrawer.d.ts +49 -0
  161. package/dist/types/Visuals/index.d.ts +38 -0
  162. package/dist/types/index.d.ts +154 -0
  163. package/package.json +14 -10
  164. package/src/Analysis/Passes/Analysis/ColorUsageAnalyzer.ts +103 -0
  165. package/src/Analysis/Passes/Analysis/GradientAnalyzer.ts +294 -20
  166. package/src/Analysis/Passes/Analysis/SymbolAnalyzer.ts +257 -0
  167. package/src/Analysis/Passes/Decision/PaletteRemapDecision.ts +425 -0
  168. package/src/Analysis/Pipeline/Pipeline.ts +15 -2
  169. package/src/Analysis/index.ts +8 -2
  170. package/src/BBCode/BBCodeDocumentModel.ts +3 -0
  171. package/src/BBCode/BBCodeToGreenNode.ts +18 -12
  172. package/src/BBCode/Parser.ts +38 -1
  173. package/src/Edits/EditPlan.ts +290 -0
  174. package/src/Edits/Optimizer.ts +185 -0
  175. package/src/Edits/Rules/Rule.ts +145 -0
  176. package/src/Edits/Rules/dropEmptyTags.ts +110 -0
  177. package/src/Edits/Rules/dropRedundantNesting.ts +120 -0
  178. package/src/Edits/Rules/index.ts +28 -0
  179. package/src/Edits/Rules/mergeAdjacent.ts +224 -0
  180. package/src/Edits/Rules/reorderWrappers.ts +154 -0
  181. package/src/Edits/Rules/shortenHex.ts +79 -0
  182. package/src/Edits/Rules/tagValue.ts +105 -0
  183. package/src/Edits/Rules/unwrapInvisibleColor.ts +93 -0
  184. package/src/Edits/applyEdits.ts +54 -0
  185. package/src/Edits/index.ts +22 -0
  186. package/src/Events/EventBus.ts +18 -0
  187. package/src/HTML/HTMLToGreenNode.ts +185 -37
  188. package/src/Incremental/BracketIndex.ts +249 -0
  189. package/src/Incremental/IncrementalParser.ts +464 -41
  190. package/src/Lexer/BBCodeLexer.ts +25 -3
  191. package/src/Lexer/index.ts +1 -1
  192. package/src/Markdown/MarkdownAST.ts +65 -2
  193. package/src/Markdown/MarkdownLexer.ts +69 -14
  194. package/src/Markdown/MarkdownParser.ts +749 -62
  195. package/src/Markdown/MarkdownToGreenNode.ts +148 -27
  196. package/src/MilHibri/MilHibriDocumentModel.ts +56 -0
  197. package/src/Model/DocumentModel.ts +138 -5
  198. package/src/Model/TagRegistry.ts +129 -134
  199. package/src/Reconciler/SurgicalReconciler.ts +460 -0
  200. package/src/Repair/NestingRepair.ts +116 -0
  201. package/src/Repair/index.ts +1 -0
  202. package/src/Semantic/SemanticAnalyzer.ts +1802 -65
  203. package/src/Syntax/GreenNodePool.ts +12 -12
  204. package/src/Syntax/RedNode.ts +17 -3
  205. package/src/Syntax/RedNodeStore.ts +9 -1
  206. package/src/Syntax/nodeAttr.ts +172 -0
  207. package/src/Tokens/TokenResolver.ts +55 -0
  208. package/src/Tokens/index.ts +1 -0
  209. package/src/Types/core.ts +15 -0
  210. package/src/Utils/ColorMath.ts +276 -55
  211. package/src/Utils/EffectMath.ts +2462 -0
  212. package/src/Utils/TagVocabulary.ts +141 -0
  213. package/src/Utils/dom-to-svg.ts +28 -12
  214. package/src/Visitors/BBBlocksExporter.ts +15 -1
  215. package/src/Visitors/BBCodeExporter.ts +319 -32
  216. package/src/Visitors/BlockPatcher.ts +87 -11
  217. package/src/Visitors/DOMMorpher.ts +15 -5
  218. package/src/Visitors/HTMLRenderer.ts +374 -151
  219. package/src/Visitors/MarkdownExporter.ts +98 -41
  220. package/src/Visitors/TiptapExporter.ts +1 -0
  221. package/src/Visuals/index.ts +7 -4
  222. package/src/Visuals/lyne.css +117 -6
  223. package/src/Visuals/osu.css +36 -5
  224. package/src/index.ts +172 -5
  225. package/dist/index.d.mts +0 -4612
  226. package/dist/index.d.ts +0 -4612
  227. package/src/Analysis/Passes/Analysis/__tests__/GradientAnalyzer.test.ts +0 -135
  228. package/src/Analysis/Passes/Analysis/__tests__/MergeableColorAnalyzer.test.ts +0 -84
  229. package/src/Analysis/Passes/Analysis/__tests__/RainbowAnalyzer.test.ts +0 -99
  230. package/src/Analysis/Passes/Analysis/__tests__/WaveAnalyzer.test.ts +0 -119
  231. package/src/Analysis/Passes/Decision/__tests__/DefaultDecision.test.ts +0 -179
  232. package/src/Analysis/__tests__/Integration.test.ts +0 -162
  233. package/src/Analysis/__tests__/Pipeline.test.ts +0 -133
  234. package/src/Diff/__tests__/TreeDiffer.test.ts +0 -65
  235. package/src/Incremental/__tests__/IncrementalParser.test.ts +0 -164
  236. package/src/Syntax/__tests__/GreenNode.test.ts +0 -33
  237. package/src/Syntax/__tests__/RedNode.test.ts +0 -81
  238. package/src/Syntax/__tests__/RedNodeStore.test.ts +0 -104
  239. package/src/Tests/ASTOptimizerIdempotence.test.ts +0 -77
  240. package/src/Tests/BlockPatcher.test.ts +0 -437
  241. package/src/Tests/BlockPatcherWindowed.test.ts +0 -364
  242. package/src/Tests/BoxDrawer.test.ts +0 -217
  243. package/src/Tests/BoxRichTitle.test.ts +0 -105
  244. package/src/Tests/Chars500kBenchmark.test.ts +0 -151
  245. package/src/Tests/Chars500kEdits.test.ts +0 -321
  246. package/src/Tests/CollabPositions.test.ts +0 -146
  247. package/src/Tests/CompilerPathProfiling.test.ts +0 -186
  248. package/src/Tests/DOMMorpher.test.ts +0 -142
  249. package/src/Tests/DomPatchPerf.test.ts +0 -60
  250. package/src/Tests/EffectSegments.snapshot.json +0 -616
  251. package/src/Tests/EffectSegments.test.ts +0 -68
  252. package/src/Tests/FindNodeAtOffset.test.ts +0 -65
  253. package/src/Tests/Fuzzer.test.ts +0 -166
  254. package/src/Tests/GreenNodePool.test.ts +0 -153
  255. package/src/Tests/Lexer.test.ts +0 -238
  256. package/src/Tests/LyneMode.test.ts +0 -291
  257. package/src/Tests/ModelCoherence.test.ts +0 -180
  258. package/src/Tests/Partition.test.ts +0 -238
  259. package/src/Tests/PluginTags.test.ts +0 -150
  260. package/src/Tests/ProblematicSection.test.ts +0 -46
  261. package/src/Tests/ProblematicSectionHTML.test.ts +0 -58
  262. package/src/Tests/RedReuse.test.ts +0 -134
  263. package/src/Tests/ReproDelete20k.test.ts +0 -62
  264. package/src/Tests/SemanticValidators.test.ts +0 -136
  265. package/src/Tests/StableNodeIds.test.ts +0 -210
  266. package/src/Tests/StudioColorBloat.test.ts +0 -25
  267. package/src/Tests/StudioDebugText.test.ts +0 -27
  268. package/src/Tests/StudioTrailingChar.test.ts +0 -25
  269. package/src/Tests/StudioValidText.test.ts +0 -25
  270. package/src/Tests/UrlImgBug.test.ts +0 -23
  271. package/src/Tests/VisualBuilderFidelity.test.ts +0 -105
  272. package/src/Tests/referenceDocument.ts +0 -119
  273. package/src/Utils/dom-to-svg.test.ts +0 -86
@@ -0,0 +1,380 @@
1
+ /**
2
+ * DocumentEngine — HTMLRenderer
3
+ *
4
+ * Renders the Document Model to HTML for preview.
5
+ * This is the main renderer for the visual BBCode preview.
6
+ *
7
+ * Uses the TagRegistry for custom rendering.
8
+ * Plugins can register custom renderers for preview components.
9
+ */
10
+ import { RedNode } from '../Syntax/RedNode';
11
+ import { Visitor } from './Visitor';
12
+ import type { TagRegistry } from '../Model/TagRegistry';
13
+ import type { BBCodeDialect } from '../BBCode/BBCodeToGreenNode';
14
+ import { type TokenResolverFn, type TokenSource } from '../Tokens';
15
+ export interface HTMLRendererOptions {
16
+ /**
17
+ * Replicate osu! forum BBCode spacing quirks.
18
+ * Defaults to true for full compatibility with Miliastry.
19
+ * Set to false for a more logical, predictable rendering engine.
20
+ */
21
+ osuBehaviour?: boolean;
22
+ /** Registry for resolving custom tags */
23
+ registry?: TagRegistry;
24
+ /** BBCode dialect to render for ('osu' | 'miliastry' | 'lyne') */
25
+ dialect?: BBCodeDialect;
26
+ /** Visual theme for markup classes ('osu' | 'lyne' | 'miliastry') */
27
+ theme?: 'osu' | 'lyne' | 'miliastry';
28
+ /** Safe media proxy callback to rewrite image and media URLs */
29
+ mediaProxy?: (url: string) => string;
30
+ /** Resolver for entity links like profile, guild, map */
31
+ entityLinkResolver?: (kind: string, value: string) => {
32
+ href: string;
33
+ external?: boolean;
34
+ } | null;
35
+ /**
36
+ * Forum-style `@mention` linkifier. Called with the bare name (no `@`);
37
+ * return null to leave the mention as plain text. Used by forum hosts that
38
+ * linkify `@username` to a profile route.
39
+ */
40
+ mentionResolver?: (name: string) => {
41
+ href: string;
42
+ external?: boolean;
43
+ } | null;
44
+ /**
45
+ * Timestamp chip linkifier (`1:23`, `01:23.456`, `01:23:456`). Called with
46
+ * the millisecond offset and the display label; return null to leave the
47
+ * timestamp as plain text. Used by map hosts that deep-link into an editor.
48
+ */
49
+ timestampResolver?: (ms: number, label: string) => {
50
+ href: string;
51
+ external?: boolean;
52
+ } | null;
53
+ /** Design tokens or resolver function */
54
+ tokens?: TokenSource;
55
+ }
56
+ export declare class HTMLRenderer extends Visitor<string> {
57
+ private tokenResolver?;
58
+ private options;
59
+ constructor(options?: HTMLRendererOptions);
60
+ setTokens(tokens?: TokenSource): void;
61
+ getTokenResolver(): TokenResolverFn | undefined;
62
+ private readonly BLOCK_TAGS;
63
+ private readonly INLINE_TAGS;
64
+ /**
65
+ * The ` data-node-id="…"` attribute, or `''` for nodes nobody looks up.
66
+ *
67
+ * ─── Why this is not emitted on everything ──────────────────────────────
68
+ *
69
+ * It used to be, carrying `node.id` — a process-global counter minted per
70
+ * RedNode, so a reparse renames every node in the document. Measured on a
71
+ * one-character edit: zero ids survive.
72
+ *
73
+ * `DOMMorpher` already noted that this rules the ids out as morph KEYS. The
74
+ * larger cost is that it also breaks the morpher's prefix/suffix trim, which
75
+ * uses `isEqualNode` — a comparison that includes attributes. An attribute
76
+ * that always differs makes every element compare unequal, so the trim never
77
+ * fires and the morpher walks the whole document. Measured on one keystroke
78
+ * in the reference document: **1665 `setAttribute` calls across 1725
79
+ * elements**, to write new numbers meaning the same thing, when the actual
80
+ * change was 10 insertions and 9 removals.
81
+ *
82
+ * ─── Why BLOCKS, and not stable ids ─────────────────────────────────────
83
+ *
84
+ * Making the id stable was the obvious repair and it does not work. Keying on
85
+ * the node's SPAN was measured: an edit at the END drops the writes to 6, but
86
+ * an insertion shifts every offset after it, so an edit near the START still
87
+ * cost 1637 — and spans are longer strings than `nN`, so the emitted HTML grew
88
+ * 10%. Stability under insertion is not something a position can have.
89
+ *
90
+ * The real observation is that no consumer ever wanted these on inline nodes.
91
+ * Both readers in the app are block-level: `usePreviewClick` walks up with
92
+ * `closest('[data-node-id]')` for *block selection*, and `useBlockHighlight`
93
+ * highlights and scrolls to a *block*. Emitting ids on the per-character
94
+ * spans of a gradient did not just cost — it made `closest()` stop at a
95
+ * character instead of the block the click meant.
96
+ *
97
+ * So the attribute goes where it is read. Inline nodes carry no id, compare
98
+ * equal, and let `isEqualNode` skip their subtrees natively; the handful of
99
+ * block containers that do carry one are few enough that their churn is
100
+ * noise.
101
+ */
102
+ /**
103
+ * Text leaves emit their content directly, with no wrapper element.
104
+ *
105
+ * They used to come wrapped in `<span class="bb-text">` — one extra DOM
106
+ * element per text leaf, which is HALF the preview's elements (measured:
107
+ * 1726 → 851 on a 19.6 KB post, 17251 → 8510 on a 196 KB one). The class
108
+ * earned none of it: it has no CSS rule anywhere in the repo, carries no
109
+ * `data-node-id` (text is not an id-bearing kind, so click mapping and
110
+ * highlighting never looked at it), and every style a text leaf can have
111
+ * still emits its own `<span style="…">` below.
112
+ *
113
+ * A/B in a production build, steady state: flush 41.4 → 39.7 ms at 19.6 KB
114
+ * and 55.4 → 46.4 ms at 196 KB, plus ~27% less HTML to serialize and parse.
115
+ */
116
+ private textWrap;
117
+ /**
118
+ * A qué elementos se les pone `data-node-id`.
119
+ *
120
+ * `'all'` (por defecto) — a todos. Es lo que permite que un clic en
121
+ * CUALQUIER punto del preview señale ese nodo exacto en el editor: en un
122
+ * degradado cada carácter es su propio nodo, y sin id no hay nada a lo que
123
+ * `closest()` pueda agarrarse.
124
+ *
125
+ * Esto estuvo desactivado por una buena razón que ya no aplica. Los ids se
126
+ * regeneraban en cada parseo, así que el atributo cambiaba en TODOS los
127
+ * elementos por pulsación: `isEqualNode` no casaba nunca, el morpher no
128
+ * podía saltarse ningún subárbol y se medían 1665 `setAttribute` sobre 1725
129
+ * elementos para escribir números nuevos que significaban lo mismo. Con la
130
+ * identidad estable entre reparseos eso desapareció: un nodo que no cambia
131
+ * conserva su id, su HTML es idéntico y el camino rápido del morpher sigue
132
+ * funcionando.
133
+ *
134
+ * El coste que queda es el tamaño: ~10 bytes por elemento, que hay que
135
+ * serializar y parsear. Medido con A/B en la misma sesión sobre el post con
136
+ * degradados (19,6 KB, 852 elementos):
137
+ *
138
+ * solo bloques (9 con id): latencia 48 ms · p95 72 ms
139
+ * todos (808 con id): latencia 56 ms · p95 104 ms
140
+ *
141
+ * Se paga a propósito: la precisión del clic es una función que se pidió, y
142
+ * 56 ms sigue holgadamente dentro de lo que se percibe como inmediato.
143
+ * `'blocks'` queda disponible para quien priorice la latencia, y `'none'`
144
+ * para los consumidores de solo lectura (foros, render estático): sin
145
+ * `data-node-id` en absoluto, el HTML es más pequeño y no hay nada que
146
+ * mantenga vivos los nodos del árbol.
147
+ */
148
+ static idMode: 'blocks' | 'all' | 'none';
149
+ /**
150
+ * Nesting depth of `[tables]` while rendering. `[col]`/`[row]`/`[th]` are
151
+ * table cells only INSIDE a `[tables]`; orphaned ones (e.g. `[col]` inside
152
+ * `[columns]`, or stray cells) render as plain inline spans — the same rule
153
+ * LYNE's forum renderer applies.
154
+ */
155
+ private tableDepth;
156
+ private idAttr;
157
+ /**
158
+ * Kinds that carry `data-node-id`.
159
+ *
160
+ * The block containers a user can select or be scrolled to, plus the media
161
+ * nodes, which are atomic and clickable in their own right. Deliberately NOT
162
+ * `text` or any inline formatting kind — see {@link idAttr}.
163
+ */
164
+ private static readonly ID_BEARING_KINDS;
165
+ visit(node: RedNode): string;
166
+ /**
167
+ * Ancho de cada tabla abierta, apilado igual que `tableDepth`.
168
+ *
169
+ * `getTableContext` corre una vez POR CELDA y recorría todas las filas de
170
+ * la tabla en cada llamada, con un `.filter()` —y su array— por fila: una
171
+ * tabla de 20×20 hacía 8.000 arrays intermedios por renderizado para
172
+ * llegar 400 veces al mismo número. Ahora se calcula una vez al entrar en
173
+ * la tabla. Va en una pila y no en un caché por nodo porque los hijos de un
174
+ * `RedNode` SON mutables (`appendChild`/`removeChild`), así que la
175
+ * identidad del nodo no garantiza que su estructura siga siendo la misma
176
+ * entre renderizados.
177
+ */
178
+ private maxColsStack;
179
+ render(root: RedNode): string;
180
+ /**
181
+ * Render every child and concatenate.
182
+ *
183
+ * Replaces the `children.map(c => this.renderNode(c)).join('')` idiom, which
184
+ * was repeated at 16 call sites and allocated a closure plus an intermediate
185
+ * array of N strings at every level of the tree. Appending to one string lets
186
+ * the engine use its rope representation instead.
187
+ */
188
+ /**
189
+ * Render the direct children of `node` to HTML (no wrapper element).
190
+ *
191
+ * Public so the incremental preview (`BlockPatcher`) can morph a block
192
+ * element's inner content without re-rendering the whole document.
193
+ */
194
+ renderChildren(node: RedNode): string;
195
+ private renderNode;
196
+ /** osu! quirk: newlines immediately preceding a [code] block are completely ignored */
197
+ private isNextCodeBlock;
198
+ /** Checks if this is the LAST empty_line right before a code block (skipping only spacing) */
199
+ private isImmediateEmptyLineBeforeCode;
200
+ private isPrevBlockBoundary;
201
+ private isTrailingBlockBoundary;
202
+ private renderError;
203
+ private wrapInline;
204
+ private wrapBlock;
205
+ /** Characters `escapeHtml` has to rewrite. Non-global on purpose: `test` must not carry `lastIndex`. */
206
+ private static readonly HTML_ESCAPE_RE;
207
+ private static readonly NAMED_COLORS;
208
+ /** Keyword-or-number CSS values (font-weight, font-style, text-decoration). */
209
+ private isCssKeyword;
210
+ /** Read a metadata field, falling back to the raw tag attribute. */
211
+ private colorStyle;
212
+ private fontSizeStyle;
213
+ private fontStyle;
214
+ private renderLink;
215
+ private renderProfile;
216
+ private renderEntity;
217
+ /**
218
+ * El aviso que ocupa el sitio de un medio que no se puede pintar.
219
+ *
220
+ * Lleva `data-bb-empty` con la etiqueta de origen porque el camino
221
+ * HTML→BBCode lee de vuelta lo que hay en el lienzo: sin la marca, el texto
222
+ * del aviso entraba al documento como contenido y se comía la etiqueta. En
223
+ * `docs/ai/hxovc.bbcode`, con cinco `[img][/img]` vacíos, cada edición del
224
+ * bloque convertía otro en la frase «[img] missing source URL».
225
+ */
226
+ private mediaError;
227
+ private renderImage;
228
+ private renderVideo;
229
+ private renderAudio;
230
+ private hexToRgba;
231
+ private renderNotice;
232
+ private renderTables;
233
+ /** Celdas directas de una fila, sin materializar un array intermedio. */
234
+ private static countCells;
235
+ /**
236
+ * Columnas de la tabla más ancha bajo `tableNode`, memoizado por tabla.
237
+ *
238
+ * `getTableContext` se llama una vez POR CELDA, y recorría todas las filas
239
+ * de la tabla en cada llamada, con un `.filter()` —y su array— por fila.
240
+ * Una tabla de 20×20 hacía 8.000 arrays intermedios en cada renderizado
241
+ * para llegar 400 veces al mismo número. El caché se vacía al empezar cada
242
+ * render, así que no retiene nodos entre documentos.
243
+ */
244
+ private static widestRow;
245
+ private getTableContext;
246
+ private renderGallery;
247
+ private renderColumns;
248
+ private renderSeparator;
249
+ private renderScroll;
250
+ private renderAbbr;
251
+ private renderTooltip;
252
+ private renderRaw;
253
+ private renderPlain;
254
+ private renderAlign;
255
+ private renderEffect;
256
+ private renderAnim;
257
+ private renderContainer;
258
+ private static readonly STYLE_PROP_WHITELIST;
259
+ private renderStyleTag;
260
+ private renderQuote;
261
+ private renderSpoilerbox;
262
+ private renderBox;
263
+ /**
264
+ * Marca un `[box]`/`[spoilerbox]` sin título propio.
265
+ *
266
+ * El `<summary>` siempre lleva texto ("Box", "Spoiler"), así que sin esta
267
+ * marca el camino HTML→BBCode leía ese relleno como si el autor lo hubiera
268
+ * escrito y devolvía `[box=Box]`.
269
+ */
270
+ private bareTitleAttr;
271
+ private renderTitle;
272
+ /**
273
+ * `[box=Title:#hex]`, `[tables=striped:#hex]`, `[columns=2:#hex]` → un
274
+ * ` style="--<suffix>-accent:#hex;"` del que el CSS deriva la paleta
275
+ * (título + chevron y borde en boxw; gradientes/header/hover en tables;
276
+ * borde + fondo en columns). Devuelve '' si no hay color válido.
277
+ */
278
+ private boxAccentStyle;
279
+ private renderList;
280
+ private renderListItem;
281
+ private renderCode;
282
+ private renderSVG;
283
+ /**
284
+ * Render an osu! BBCode imagemap.
285
+ *
286
+ * Structure:
287
+ * [imagemap]
288
+ * https://example.com/image.png ← first line = image URL
289
+ * 10 20 50 60 https://... Label ← subsequent lines = clickable areas
290
+ * [/imagemap]
291
+ *
292
+ * Each area line format: x y width height url [label]
293
+ * All values are PERCENTAGES (0–100) relative to the image dimensions.
294
+ * Uses CSS absolute positioning with percentage coordinates.
295
+ */
296
+ private renderImagemap;
297
+ private collectNodeText;
298
+ /**
299
+ * Effect parameters off a node, with every colour re-validated.
300
+ *
301
+ * `startsWith('#')` was NOT a filter: `#a" onmouseover="alert(1)` passes
302
+ * it. Every stop has to survive the full colour allowlist or be dropped,
303
+ * because these values end up inside a `style` attribute.
304
+ */
305
+ private effectParams;
306
+ /** Text inside an effect node, ignoring layout-only children. */
307
+ /**
308
+ * The plain text an effect modulates, line breaks included.
309
+ *
310
+ * A break is a childless `spacing` leaf carrying no text, so collecting
311
+ * it naively contributes nothing and the effect sees one long line.
312
+ * Every two-dimensional axis then collapses: `line` reports zero for
313
+ * the whole block, `column` stretches one row across the paragraph, and
314
+ * `radial` measures a rectangle one line tall. The break was being
315
+ * dropped from the OUTPUT as well, so a multi-line effect previewed as
316
+ * a single run-on line.
317
+ *
318
+ * The document's own plain text spells a break `\n` — that is what
319
+ * `stripColorWrappers` and the studio's sampler both count — so this
320
+ * agrees with them rather than inventing a third answer.
321
+ */
322
+ private effectText;
323
+ private effectChildText;
324
+ /**
325
+ * Paint an effect's computed segments as spans.
326
+ *
327
+ * Shared by rainbow, grow and any modulated gradient, so the preview
328
+ * shows exactly the colours and sizes the BBCode export will contain
329
+ * rather than an approximation of them.
330
+ */
331
+ private renderEffectSegments;
332
+ /**
333
+ * A gradient previews as a real CSS gradient when it is a plain
334
+ * left-to-right ramp, and as computed per-character spans otherwise.
335
+ *
336
+ * The CSS path is genuinely smoother — it interpolates per pixel rather
337
+ * than per glyph — but it can only express a linear sweep. A gradient
338
+ * with a waveform, a non-index axis or quantisation steps looked
339
+ * nothing like its own export while this was the only path.
340
+ */
341
+ private renderGradient;
342
+ /**
343
+ * Escape for both text content and double-quoted attribute values.
344
+ *
345
+ * `'` is included because attribute values elsewhere in the codebase (and in
346
+ * consumer-supplied tag handlers) may be single-quoted; leaving it raw makes
347
+ * the escaping context-dependent, which is how injections get reintroduced.
348
+ */
349
+ /**
350
+ * Escape the five HTML-significant characters.
351
+ *
352
+ * Was five chained `.replace(/x/g, …)` calls: five full scans of the string
353
+ * and up to five intermediate allocations for *every* text node, even though
354
+ * ordinary prose contains none of these characters. This tests once and
355
+ * returns the input untouched in that common case, then does a single pass
356
+ * when there is actually something to escape.
357
+ */
358
+ /** Resolver configured at construction (read through a getter for brevity). */
359
+ private get mentionResolver();
360
+ /**
361
+ * `@mention` shape used by forum hosts: 3-15 word chars, boundaries that
362
+ * exclude longer usernames (same rule LYNE's forum uses). Non-global so
363
+ * `lastIndex` never leaks between calls.
364
+ */
365
+ private static readonly MENTION_RE;
366
+ /**
367
+ * Linkify a plain-text leaf: `@mention` runs and (optionally) timestamp
368
+ * chips. Both run through their resolver; null leaves the run as text.
369
+ * Every interpolated string is HTML-escaped, and hrefs with dangerous
370
+ * protocols (javascript:, data:, vbscript:) are rejected (defence in depth;
371
+ * the resolvers themselves should never produce one).
372
+ */
373
+ private linkifyText;
374
+ /** `1:23`, `01:23.456`, `01:23:456` — the editor deep-link shape. */
375
+ private static readonly TS_RE;
376
+ private linkifyTimestamps;
377
+ /** Root-relative, http(s), mailto and line:// hrefs are the only safe shapes. */
378
+ private isSafeHref;
379
+ private escapeHtml;
380
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * DocumentEngine — JSONExporter
3
+ *
4
+ * Exports the Document Model to JSON.
5
+ * Useful for:
6
+ * - Saving/loading documents in a structured format
7
+ * - Sending over the wire (collaboration)
8
+ * - Debugging and inspecting the document tree
9
+ * - Import/export between different tools
10
+ */
11
+ import { RedNode } from '../Syntax/RedNode';
12
+ import { Visitor } from './Visitor';
13
+ export interface JSONDocument {
14
+ version: number;
15
+ kind: string;
16
+ nodes: JSONNode[];
17
+ metadata?: Record<string, unknown>;
18
+ }
19
+ export interface JSONNode {
20
+ id: string;
21
+ kind: string;
22
+ text: string;
23
+ version: number;
24
+ children: JSONNode[];
25
+ attributes?: Record<string, unknown>;
26
+ metadata?: Record<string, unknown>;
27
+ range?: {
28
+ start: number;
29
+ end: number;
30
+ };
31
+ }
32
+ export declare class JSONExporter extends Visitor<string> {
33
+ /**
34
+ * Export a RedNode tree to a JSON string.
35
+ */
36
+ visit(node: RedNode): string;
37
+ /**
38
+ * Export to a JSON object.
39
+ */
40
+ toJSON(node: RedNode): JSONDocument;
41
+ private serializeNode;
42
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * DocumentEngine — MarkdownExporter
3
+ *
4
+ * Exports the Document Model to Markdown with hybrid BBCode embedding
5
+ * for features not natively supported in standard CommonMark (color, size,
6
+ * underline, alignment, collapsible boxes).
7
+ */
8
+ import { RedNode } from '../Syntax/RedNode';
9
+ import { Visitor } from './Visitor';
10
+ export declare class MarkdownExporter extends Visitor<string> {
11
+ visit(node: RedNode): string;
12
+ export(node: RedNode): string;
13
+ private exportNode;
14
+ private renderTitleMarkdown;
15
+ /**
16
+ * Read a BBCode tag's `=VALUE` attribute.
17
+ */
18
+ /**
19
+ * El atributo del nodo, o `undefined` si no lleva.
20
+ *
21
+ * El recorte lo hace `nodeAttrValue`, el mismo lector del `HTMLRenderer`;
22
+ * aquí sólo se cambia el contrato del caso vacío. Este exportador encadena
23
+ * `metadata ?? atributo ?? contenido`, así que necesita un `undefined`
24
+ * explícito donde `nodeAttrValue` devuelve el texto crudo — es la única
25
+ * diferencia, y por eso no se llama directamente.
26
+ */
27
+ private extractValue;
28
+ }
@@ -0,0 +1,16 @@
1
+ import { RedNode } from '../Syntax/RedNode';
2
+ import { Visitor } from './Visitor';
3
+ import type { VisitorContext } from './Visitor';
4
+ /**
5
+ * SVGRenderer (Runtime ForeignObject Strategy)
6
+ *
7
+ * Instead of attempting to calculate CSS layouts manually,
8
+ * this runtime renderer wraps the exact HTML output in an SVG <foreignObject>.
9
+ *
10
+ * This gives 100% precision with zero DOM scanning overhead for real-time previews.
11
+ */
12
+ export declare class SVGRenderer extends Visitor<string> {
13
+ private htmlRenderer;
14
+ visit(node: RedNode, context?: VisitorContext): string;
15
+ render(root: RedNode): string;
16
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * DocumentEngine — TiptapExporter
3
+ *
4
+ * Exports the Document Model to Tiptap (ProseMirror) JSON format.
5
+ * This proves how easily Quasar's AST bridges to visual WYSIWYG editors.
6
+ *
7
+ * Tiptap uses a JSON format structured like:
8
+ * {
9
+ * type: "doc",
10
+ * content: [
11
+ * {
12
+ * type: "paragraph",
13
+ * content: [
14
+ * { type: "text", text: "Hello ", marks: [{ type: "bold" }] }
15
+ * ]
16
+ * }
17
+ * ]
18
+ * }
19
+ */
20
+ import { RedNode } from '../Syntax/RedNode';
21
+ import { Visitor } from './Visitor';
22
+ export interface TiptapNode {
23
+ type: string;
24
+ attrs?: Record<string, any>;
25
+ content?: TiptapNode[];
26
+ marks?: TiptapMark[];
27
+ text?: string;
28
+ }
29
+ export interface TiptapMark {
30
+ type: string;
31
+ attrs?: Record<string, any>;
32
+ }
33
+ export declare class TiptapExporter extends Visitor<string> {
34
+ visit(node: RedNode): string;
35
+ toTiptap(node: RedNode): TiptapNode;
36
+ private processChildren;
37
+ private processNode;
38
+ private kindToMark;
39
+ private kindToBlockType;
40
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * DocumentEngine — Visitor
3
+ *
4
+ * Base visitor pattern for traversing the Red Tree.
5
+ * All exporters and renderers extend this.
6
+ *
7
+ * Instead of switch statements on tag names, use the visitor pattern.
8
+ * New languages/exporters don't modify existing code.
9
+ *
10
+ * Inspired by SwiftSyntax's Visitor pattern.
11
+ */
12
+ import { RedNode } from '../Syntax/RedNode';
13
+ export interface VisitorContext {
14
+ source: string;
15
+ options: Record<string, unknown>;
16
+ }
17
+ export declare abstract class Visitor<T = string> {
18
+ protected context: VisitorContext;
19
+ /**
20
+ * Visit a node and produce an output value.
21
+ */
22
+ abstract visit(node: RedNode, context?: VisitorContext): T;
23
+ /**
24
+ * Visit all children and combine results.
25
+ */
26
+ visitChildren(node: RedNode, separator?: string): T[];
27
+ /**
28
+ * Get the text content of a node.
29
+ */
30
+ getText(node: RedNode): string;
31
+ /**
32
+ * Set the visitor context.
33
+ */
34
+ setContext(context: VisitorContext): void;
35
+ }
@@ -0,0 +1,9 @@
1
+ export { Visitor } from './Visitor';
2
+ export type { VisitorContext } from './Visitor';
3
+ export { BBCodeExporter } from './BBCodeExporter';
4
+ export { HTMLRenderer } from './HTMLRenderer';
5
+ export { morphHTML } from './DOMMorpher';
6
+ export { MarkdownExporter } from './MarkdownExporter';
7
+ export { JSONExporter } from './JSONExporter';
8
+ export { SVGRenderer } from './SVGRenderer';
9
+ export type { JSONDocument, JSONNode } from './JSONExporter';
@@ -0,0 +1,49 @@
1
+ /**
2
+ * DocumentEngine — BoxDrawer
3
+ *
4
+ * Drawer-style open/close animation for the `<details>` elements that
5
+ * HTMLRenderer emits for `[box]` and `[spoilerbox]`. The box grows downwards
6
+ * and progressively uncovers its content, which stays anchored under the
7
+ * summary instead of popping in all at once.
8
+ *
9
+ * This lives in Quasar rather than in a consumer because Quasar is what emits
10
+ * the `<details>` in the first place — the same reason `morphHTML` lives here.
11
+ * Any host that renders Quasar output gets the behaviour by binding it once.
12
+ *
13
+ * Why not CSS: while a `<details>` is closed its content is not rendered, so
14
+ * there is no height to interpolate between. `::details-content` combined with
15
+ * `interpolate-size: allow-keywords` would solve it, but it is Chromium-only in
16
+ * practice and hosts running on WebKit would silently lose the animation.
17
+ * Measuring the real height and driving it with the Web Animations API works on
18
+ * every engine.
19
+ *
20
+ * Heights are measured, not computed from summary + padding + borders, because
21
+ * that arithmetic breaks as soon as a theme puts margins on the summary or the
22
+ * boxes are nested. Toggling `open` and reading the rect yields the exact
23
+ * height the element will settle at, whatever the stylesheet does.
24
+ */
25
+ export interface BoxDrawerOptions {
26
+ /** Length of a full open or close, in milliseconds. Partial travel is scaled down. */
27
+ durationMs?: number;
28
+ /** CSS easing function. */
29
+ easing?: string;
30
+ }
31
+ /**
32
+ * Animate one `<details>` to its opposite state, reversing cleanly when it is
33
+ * already mid-animation.
34
+ *
35
+ * Exported so hosts that toggle boxes programmatically (a keyboard command, an
36
+ * "expand all" action) go through the same animation as a click.
37
+ */
38
+ export declare function toggleBoxWithDrawer(details: HTMLDetailsElement, options?: BoxDrawerOptions): void;
39
+ /**
40
+ * Intercept the native toggle of every box inside `root`.
41
+ *
42
+ * The listener is delegated rather than attached per box: hosts typically feed
43
+ * the preview through `morphHTML` on every keystroke, and per-element listeners
44
+ * would be torn down and re-attached continuously.
45
+ *
46
+ * @param root - container holding the rendered BBCode
47
+ * @returns disposer that removes the listener
48
+ */
49
+ export declare function bindBoxDrawer(root: HTMLElement, options?: BoxDrawerOptions): () => void;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * DocumentEngine — Visuals
3
+ *
4
+ * Catálogo de estilos visuales por plataforma para la previsualización
5
+ * de BBCode. Cada plataforma puede tener su propia apariencia sin
6
+ * modificar el renderer HTML.
7
+ *
8
+ * Uso:
9
+ * // Desde la FUENTE, que es como lo consume el monorepo (Next.js, Vite,
10
+ * // Webpack): esta ruta funciona sin haber compilado nada.
11
+ * import '@miliastry/quasar/src/Visuals/osu.css'
12
+ *
13
+ * // Desde el paquete PUBLICADO. Solo resuelve tras `npm run build`, que es
14
+ * // lo que copia los CSS a dist/Visuals (ver scripts/copy-css.mjs).
15
+ * import '@miliastry/quasar/Visuals/osu.css'
16
+ *
17
+ * // Luego en el JSX: <div className="bbcode-preview" ...
18
+ *
19
+ * Para cambiar de tema visual, solo cambia el import del CSS.
20
+ *
21
+ * Temas disponibles:
22
+ * - osu → Estilo visual inspirado en los foros de osu!
23
+ * - miliastry → Estilo por defecto de Miliastry (próximamente)
24
+ */
25
+ export { bindBoxDrawer, toggleBoxWithDrawer } from './BoxDrawer';
26
+ export type { BoxDrawerOptions } from './BoxDrawer';
27
+ export declare const visualThemes: readonly [{
28
+ readonly id: 'osu';
29
+ readonly name: 'osu! Forum Style';
30
+ readonly description: 'Estilo visual inspirado en los foros de osu!';
31
+ readonly cssFile: 'osu.css';
32
+ }, {
33
+ readonly id: 'lyne';
34
+ readonly name: 'Lyne Style';
35
+ readonly description: 'Estilo visual cyberpunk con cortes a 45° inspirado en Lyne';
36
+ readonly cssFile: 'lyne.css';
37
+ }];
38
+ export type VisualThemeId = (typeof visualThemes)[number]['id'];