@miliastry/quasar 1.0.5 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/dist/Visuals/lyne.css +191 -21
  2. package/dist/Visuals/osu.css +36 -5
  3. package/dist/index.js +9619 -3135
  4. package/dist/index.mjs +9490 -3136
  5. package/dist/types/Analysis/Contracts/AnalysisReport.d.ts +22 -0
  6. package/dist/types/Analysis/Contracts/Contribution.d.ts +68 -0
  7. package/dist/types/Analysis/Contracts/Pass.d.ts +59 -0
  8. package/dist/types/Analysis/Contracts/PipelineContext.d.ts +34 -0
  9. package/dist/types/Analysis/Passes/Analysis/ColorUsageAnalyzer.d.ts +56 -0
  10. package/dist/types/Analysis/Passes/Analysis/GradientAnalyzer.d.ts +111 -0
  11. package/dist/types/Analysis/Passes/Analysis/MergeableColorAnalyzer.d.ts +26 -0
  12. package/dist/types/Analysis/Passes/Analysis/RainbowAnalyzer.d.ts +47 -0
  13. package/dist/types/Analysis/Passes/Analysis/SymbolAnalyzer.d.ts +89 -0
  14. package/dist/types/Analysis/Passes/Analysis/WaveAnalyzer.d.ts +44 -0
  15. package/dist/types/Analysis/Passes/Analysis/__tests__/GradientAnalyzer.test.d.ts +4 -0
  16. package/dist/types/Analysis/Passes/Analysis/__tests__/MergeableColorAnalyzer.test.d.ts +4 -0
  17. package/dist/types/Analysis/Passes/Analysis/__tests__/RainbowAnalyzer.test.d.ts +4 -0
  18. package/dist/types/Analysis/Passes/Analysis/__tests__/WaveAnalyzer.test.d.ts +4 -0
  19. package/dist/types/Analysis/Passes/Decision/DefaultDecision.d.ts +30 -0
  20. package/dist/types/Analysis/Passes/Decision/PaletteRemapDecision.d.ts +169 -0
  21. package/dist/types/Analysis/Passes/Decision/__tests__/DefaultDecision.test.d.ts +4 -0
  22. package/dist/types/Analysis/Passes/Transform/CollapseGradientTransform.d.ts +37 -0
  23. package/dist/types/Analysis/Passes/Transform/MergeColorsTransform.d.ts +28 -0
  24. package/dist/types/Analysis/Passes/Transform/RainbowCollapseTransform.d.ts +19 -0
  25. package/dist/types/Analysis/Passes/Transform/WaveCollapseTransform.d.ts +20 -0
  26. package/dist/types/Analysis/Passes/Utility/CharacterCountAnalyzer.d.ts +19 -0
  27. package/dist/types/Analysis/Pipeline/Pipeline.d.ts +69 -0
  28. package/dist/types/Analysis/Pipeline/PipelineBuilder.d.ts +31 -0
  29. package/dist/types/Analysis/Pipeline/PipelineStage.d.ts +17 -0
  30. package/dist/types/Analysis/Utils/color-utils.d.ts +60 -0
  31. package/dist/types/Analysis/__tests__/Integration.test.d.ts +8 -0
  32. package/dist/types/Analysis/__tests__/PaletteRemap.test.d.ts +14 -0
  33. package/dist/types/Analysis/__tests__/Pipeline.test.d.ts +10 -0
  34. package/dist/types/Analysis/__tests__/RoundTrip.test.d.ts +30 -0
  35. package/dist/types/Analysis/__tests__/SymbolAnalyzer.test.d.ts +11 -0
  36. package/dist/types/Analysis/index.d.ts +40 -0
  37. package/dist/types/BBCode/BBCodeDocumentModel.d.ts +92 -0
  38. package/dist/types/BBCode/BBCodeToGreenNode.d.ts +111 -0
  39. package/dist/types/BBCode/Parser.d.ts +78 -0
  40. package/dist/types/BBCode/index.d.ts +4 -0
  41. package/dist/types/Collab/positions.d.ts +55 -0
  42. package/dist/types/Commands/Command.d.ts +37 -0
  43. package/dist/types/Commands/CommandRegistry.d.ts +48 -0
  44. package/dist/types/Commands/DeleteNode.d.ts +7 -0
  45. package/dist/types/Commands/InsertText.d.ts +8 -0
  46. package/dist/types/Commands/SplitMerge.d.ts +9 -0
  47. package/dist/types/Commands/WrapInTag.d.ts +8 -0
  48. package/dist/types/Commands/index.d.ts +6 -0
  49. package/dist/types/Diff/TreeDiffer.d.ts +52 -0
  50. package/dist/types/Diff/__tests__/TreeDiffer.test.d.ts +1 -0
  51. package/dist/types/Diff/index.d.ts +2 -0
  52. package/dist/types/Edits/EditPlan.d.ts +144 -0
  53. package/dist/types/Edits/Optimizer.d.ts +78 -0
  54. package/dist/types/Edits/Rules/Rule.d.ts +91 -0
  55. package/dist/types/Edits/Rules/dropEmptyTags.d.ts +40 -0
  56. package/dist/types/Edits/Rules/dropRedundantNesting.d.ts +59 -0
  57. package/dist/types/Edits/Rules/index.d.ts +9 -0
  58. package/dist/types/Edits/Rules/mergeAdjacent.d.ts +93 -0
  59. package/dist/types/Edits/Rules/reorderWrappers.d.ts +42 -0
  60. package/dist/types/Edits/Rules/shortenHex.d.ts +30 -0
  61. package/dist/types/Edits/Rules/tagValue.d.ts +56 -0
  62. package/dist/types/Edits/Rules/unwrapInvisibleColor.d.ts +47 -0
  63. package/dist/types/Edits/applyEdits.d.ts +36 -0
  64. package/dist/types/Edits/index.d.ts +6 -0
  65. package/dist/types/Events/EventBus.d.ts +103 -0
  66. package/dist/types/Events/index.d.ts +2 -0
  67. package/dist/types/Formatter/Formatter.d.ts +32 -0
  68. package/dist/types/Formatter/index.d.ts +2 -0
  69. package/dist/types/HTML/HTMLDocumentModel.d.ts +13 -0
  70. package/dist/types/HTML/HTMLToGreenNode.d.ts +4 -0
  71. package/dist/types/Incremental/BracketIndex.d.ts +101 -0
  72. package/dist/types/Incremental/ChangeTracker.d.ts +69 -0
  73. package/dist/types/Incremental/IncrementalParser.d.ts +246 -0
  74. package/dist/types/Incremental/__tests__/BracketIndex.test.d.ts +1 -0
  75. package/dist/types/Incremental/__tests__/IncrementalParser.test.d.ts +1 -0
  76. package/dist/types/Incremental/index.d.ts +4 -0
  77. package/dist/types/Lexer/BBCodeLexer.d.ts +74 -0
  78. package/dist/types/Lexer/Lexer.d.ts +54 -0
  79. package/dist/types/Lexer/index.d.ts +4 -0
  80. package/dist/types/Linter/Linter.d.ts +58 -0
  81. package/dist/types/Linter/index.d.ts +2 -0
  82. package/dist/types/Markdown/MarkdownAST.d.ts +122 -0
  83. package/dist/types/Markdown/MarkdownDocumentModel.d.ts +15 -0
  84. package/dist/types/Markdown/MarkdownLexer.d.ts +155 -0
  85. package/dist/types/Markdown/MarkdownParser.d.ts +42 -0
  86. package/dist/types/Markdown/MarkdownToGreenNode.d.ts +6 -0
  87. package/dist/types/MilHibri/MilHibriDocumentModel.d.ts +21 -0
  88. package/dist/types/Model/DocumentModel.d.ts +318 -0
  89. package/dist/types/Model/NodeFactory.d.ts +29 -0
  90. package/dist/types/Model/TagRegistry.d.ts +165 -0
  91. package/dist/types/Model/index.d.ts +5 -0
  92. package/dist/types/Plugins/PluginAPI.d.ts +44 -0
  93. package/dist/types/Plugins/PluginRegistry.d.ts +81 -0
  94. package/dist/types/Plugins/index.d.ts +3 -0
  95. package/dist/types/Queries/QueryEngine.d.ts +32 -0
  96. package/dist/types/Queries/index.d.ts +1 -0
  97. package/dist/types/Reconciler/SurgicalReconciler.d.ts +23 -0
  98. package/dist/types/RenderPipeline/RenderPipeline.d.ts +46 -0
  99. package/dist/types/RenderPipeline/RenderTree.d.ts +55 -0
  100. package/dist/types/RenderPipeline/index.d.ts +4 -0
  101. package/dist/types/Repair/NestingRepair.d.ts +48 -0
  102. package/dist/types/Repair/index.d.ts +1 -0
  103. package/dist/types/Semantic/SemanticAnalyzer.d.ts +321 -0
  104. package/dist/types/Semantic/index.d.ts +2 -0
  105. package/dist/types/Symbols/SymbolTable.d.ts +47 -0
  106. package/dist/types/Symbols/index.d.ts +1 -0
  107. package/dist/types/Syntax/GreenNode.d.ts +154 -0
  108. package/dist/types/Syntax/GreenNodePool.d.ts +134 -0
  109. package/dist/types/Syntax/NodeMatcher.d.ts +75 -0
  110. package/dist/types/Syntax/RedNode.d.ts +280 -0
  111. package/dist/types/Syntax/RedNodeStore.d.ts +116 -0
  112. package/dist/types/Syntax/TreeBuilder.d.ts +54 -0
  113. package/dist/types/Syntax/__tests__/GreenNode.test.d.ts +1 -0
  114. package/dist/types/Syntax/__tests__/RedNode.test.d.ts +1 -0
  115. package/dist/types/Syntax/__tests__/RedNodeStore.test.d.ts +1 -0
  116. package/dist/types/Syntax/greenEdit.d.ts +64 -0
  117. package/dist/types/Syntax/hash.d.ts +17 -0
  118. package/dist/types/Syntax/index.d.ts +8 -0
  119. package/dist/types/Syntax/nodeAttr.d.ts +63 -0
  120. package/dist/types/Syntax/partition.d.ts +67 -0
  121. package/dist/types/Syntax/preserveNodeIds.d.ts +47 -0
  122. package/dist/types/Tokens/TokenResolver.d.ts +25 -0
  123. package/dist/types/Tokens/index.d.ts +1 -0
  124. package/dist/types/Transactions/Transaction.d.ts +36 -0
  125. package/dist/types/Transactions/UndoManager.d.ts +63 -0
  126. package/dist/types/Transactions/index.d.ts +3 -0
  127. package/dist/types/Transformers/ASTOptimizer.d.ts +34 -0
  128. package/dist/types/Transformers/GradientTransformer.d.ts +18 -0
  129. package/dist/types/Transformers/GrowTransformer.d.ts +16 -0
  130. package/dist/types/Transformers/RainbowTransformer.d.ts +17 -0
  131. package/dist/types/Transformers/SineWaveTransformer.d.ts +17 -0
  132. package/dist/types/Transformers/Transformer.d.ts +17 -0
  133. package/dist/types/Types/core.d.ts +104 -0
  134. package/dist/types/Types/diagnostics.d.ts +85 -0
  135. package/dist/types/Types/index.d.ts +11 -0
  136. package/dist/types/Types/operations.d.ts +109 -0
  137. package/dist/types/Types/queries.d.ts +67 -0
  138. package/dist/types/Types/symbols.d.ts +42 -0
  139. package/dist/types/Types/tokens.d.ts +75 -0
  140. package/dist/types/Utils/BBCodeGenerator.d.ts +21 -0
  141. package/dist/types/Utils/ColorMath.d.ts +92 -0
  142. package/dist/types/Utils/EffectMath.d.ts +643 -0
  143. package/dist/types/Utils/TagVocabulary.d.ts +104 -0
  144. package/dist/types/Utils/TagVocabulary.test.d.ts +1 -0
  145. package/dist/types/Utils/color.d.ts +29 -0
  146. package/dist/types/Utils/dom-to-svg.d.ts +28 -0
  147. package/dist/types/Utils/dom-to-svg.test.d.ts +1 -0
  148. package/dist/types/Utils/treeTransformers.d.ts +160 -0
  149. package/dist/types/Visitors/BBBlocksExporter.d.ts +27 -0
  150. package/dist/types/Visitors/BBCodeExporter.d.ts +90 -0
  151. package/dist/types/Visitors/BlockPatcher.d.ts +91 -0
  152. package/dist/types/Visitors/DOMMorpher.d.ts +13 -0
  153. package/dist/types/Visitors/HTMLRenderer.d.ts +380 -0
  154. package/dist/types/Visitors/JSONExporter.d.ts +42 -0
  155. package/dist/types/Visitors/MarkdownExporter.d.ts +28 -0
  156. package/dist/types/Visitors/SVGRenderer.d.ts +16 -0
  157. package/dist/types/Visitors/TiptapExporter.d.ts +40 -0
  158. package/dist/types/Visitors/Visitor.d.ts +35 -0
  159. package/dist/types/Visitors/index.d.ts +9 -0
  160. package/dist/types/Visuals/BoxDrawer.d.ts +49 -0
  161. package/dist/types/Visuals/index.d.ts +38 -0
  162. package/dist/types/index.d.ts +154 -0
  163. package/package.json +14 -10
  164. package/src/Analysis/Passes/Analysis/ColorUsageAnalyzer.ts +103 -0
  165. package/src/Analysis/Passes/Analysis/GradientAnalyzer.ts +294 -20
  166. package/src/Analysis/Passes/Analysis/SymbolAnalyzer.ts +257 -0
  167. package/src/Analysis/Passes/Decision/PaletteRemapDecision.ts +425 -0
  168. package/src/Analysis/Pipeline/Pipeline.ts +15 -2
  169. package/src/Analysis/index.ts +8 -2
  170. package/src/BBCode/BBCodeDocumentModel.ts +3 -0
  171. package/src/BBCode/BBCodeToGreenNode.ts +34 -11
  172. package/src/BBCode/Parser.ts +38 -1
  173. package/src/Edits/EditPlan.ts +290 -0
  174. package/src/Edits/Optimizer.ts +185 -0
  175. package/src/Edits/Rules/Rule.ts +145 -0
  176. package/src/Edits/Rules/dropEmptyTags.ts +110 -0
  177. package/src/Edits/Rules/dropRedundantNesting.ts +120 -0
  178. package/src/Edits/Rules/index.ts +28 -0
  179. package/src/Edits/Rules/mergeAdjacent.ts +224 -0
  180. package/src/Edits/Rules/reorderWrappers.ts +154 -0
  181. package/src/Edits/Rules/shortenHex.ts +79 -0
  182. package/src/Edits/Rules/tagValue.ts +105 -0
  183. package/src/Edits/Rules/unwrapInvisibleColor.ts +93 -0
  184. package/src/Edits/applyEdits.ts +54 -0
  185. package/src/Edits/index.ts +22 -0
  186. package/src/Events/EventBus.ts +18 -0
  187. package/src/HTML/HTMLToGreenNode.ts +185 -37
  188. package/src/Incremental/BracketIndex.ts +249 -0
  189. package/src/Incremental/IncrementalParser.ts +464 -41
  190. package/src/Lexer/BBCodeLexer.ts +25 -3
  191. package/src/Lexer/index.ts +1 -1
  192. package/src/Markdown/MarkdownAST.ts +65 -2
  193. package/src/Markdown/MarkdownLexer.ts +69 -14
  194. package/src/Markdown/MarkdownParser.ts +749 -62
  195. package/src/Markdown/MarkdownToGreenNode.ts +148 -27
  196. package/src/MilHibri/MilHibriDocumentModel.ts +56 -0
  197. package/src/Model/DocumentModel.ts +138 -5
  198. package/src/Model/TagRegistry.ts +132 -134
  199. package/src/Reconciler/SurgicalReconciler.ts +460 -0
  200. package/src/Repair/NestingRepair.ts +116 -0
  201. package/src/Repair/index.ts +1 -0
  202. package/src/Semantic/SemanticAnalyzer.ts +1802 -65
  203. package/src/Syntax/GreenNodePool.ts +12 -12
  204. package/src/Syntax/RedNode.ts +17 -3
  205. package/src/Syntax/RedNodeStore.ts +9 -1
  206. package/src/Syntax/nodeAttr.ts +172 -0
  207. package/src/Tokens/TokenResolver.ts +55 -0
  208. package/src/Tokens/index.ts +1 -0
  209. package/src/Types/core.ts +15 -0
  210. package/src/Utils/ColorMath.ts +276 -55
  211. package/src/Utils/EffectMath.ts +2462 -0
  212. package/src/Utils/TagVocabulary.ts +141 -0
  213. package/src/Utils/dom-to-svg.ts +28 -12
  214. package/src/Visitors/BBBlocksExporter.ts +15 -1
  215. package/src/Visitors/BBCodeExporter.ts +319 -32
  216. package/src/Visitors/BlockPatcher.ts +87 -11
  217. package/src/Visitors/DOMMorpher.ts +15 -5
  218. package/src/Visitors/HTMLRenderer.ts +452 -142
  219. package/src/Visitors/MarkdownExporter.ts +98 -41
  220. package/src/Visitors/TiptapExporter.ts +1 -0
  221. package/src/Visuals/index.ts +7 -4
  222. package/src/Visuals/lyne.css +191 -21
  223. package/src/Visuals/osu.css +36 -5
  224. package/src/index.ts +172 -5
  225. package/dist/index.d.mts +0 -4609
  226. package/dist/index.d.ts +0 -4609
  227. package/src/Analysis/Passes/Analysis/__tests__/GradientAnalyzer.test.ts +0 -135
  228. package/src/Analysis/Passes/Analysis/__tests__/MergeableColorAnalyzer.test.ts +0 -84
  229. package/src/Analysis/Passes/Analysis/__tests__/RainbowAnalyzer.test.ts +0 -99
  230. package/src/Analysis/Passes/Analysis/__tests__/WaveAnalyzer.test.ts +0 -119
  231. package/src/Analysis/Passes/Decision/__tests__/DefaultDecision.test.ts +0 -179
  232. package/src/Analysis/__tests__/Integration.test.ts +0 -162
  233. package/src/Analysis/__tests__/Pipeline.test.ts +0 -133
  234. package/src/Diff/__tests__/TreeDiffer.test.ts +0 -65
  235. package/src/Incremental/__tests__/IncrementalParser.test.ts +0 -164
  236. package/src/Syntax/__tests__/GreenNode.test.ts +0 -33
  237. package/src/Syntax/__tests__/RedNode.test.ts +0 -81
  238. package/src/Syntax/__tests__/RedNodeStore.test.ts +0 -104
  239. package/src/Tests/ASTOptimizerIdempotence.test.ts +0 -77
  240. package/src/Tests/BlockPatcher.test.ts +0 -437
  241. package/src/Tests/BlockPatcherWindowed.test.ts +0 -364
  242. package/src/Tests/BoxDrawer.test.ts +0 -217
  243. package/src/Tests/BoxRichTitle.test.ts +0 -105
  244. package/src/Tests/Chars500kBenchmark.test.ts +0 -151
  245. package/src/Tests/Chars500kEdits.test.ts +0 -321
  246. package/src/Tests/CollabPositions.test.ts +0 -146
  247. package/src/Tests/CompilerPathProfiling.test.ts +0 -186
  248. package/src/Tests/DOMMorpher.test.ts +0 -142
  249. package/src/Tests/DomPatchPerf.test.ts +0 -60
  250. package/src/Tests/EffectSegments.snapshot.json +0 -616
  251. package/src/Tests/EffectSegments.test.ts +0 -68
  252. package/src/Tests/FindNodeAtOffset.test.ts +0 -65
  253. package/src/Tests/Fuzzer.test.ts +0 -166
  254. package/src/Tests/GreenNodePool.test.ts +0 -153
  255. package/src/Tests/Lexer.test.ts +0 -238
  256. package/src/Tests/LyneMode.test.ts +0 -291
  257. package/src/Tests/ModelCoherence.test.ts +0 -180
  258. package/src/Tests/Partition.test.ts +0 -238
  259. package/src/Tests/PluginTags.test.ts +0 -150
  260. package/src/Tests/ProblematicSection.test.ts +0 -46
  261. package/src/Tests/ProblematicSectionHTML.test.ts +0 -58
  262. package/src/Tests/RedReuse.test.ts +0 -134
  263. package/src/Tests/ReproDelete20k.test.ts +0 -62
  264. package/src/Tests/SemanticValidators.test.ts +0 -136
  265. package/src/Tests/StableNodeIds.test.ts +0 -210
  266. package/src/Tests/StudioColorBloat.test.ts +0 -25
  267. package/src/Tests/StudioDebugText.test.ts +0 -27
  268. package/src/Tests/StudioTrailingChar.test.ts +0 -25
  269. package/src/Tests/StudioValidText.test.ts +0 -25
  270. package/src/Tests/UrlImgBug.test.ts +0 -23
  271. package/src/Tests/VisualBuilderFidelity.test.ts +0 -105
  272. package/src/Tests/referenceDocument.ts +0 -119
  273. package/src/Utils/dom-to-svg.test.ts +0 -86
@@ -1,14 +1,13 @@
1
1
  /**
2
2
  * DocumentEngine — MarkdownExporter
3
3
  *
4
- * Exports the Document Model to Markdown.
5
- * Demonstrates the format-agnostic power of the engine:
6
- * BBCode in DocumentModel → Markdown out.
7
- *
8
- * A single visitor, no parser changes needed.
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).
9
7
  */
10
8
 
11
9
  import { RedNode } from '../Syntax/RedNode'
10
+ import { nodeAttrValue } from '../Syntax/nodeAttr'
12
11
  import { Visitor } from './Visitor'
13
12
 
14
13
  export class MarkdownExporter extends Visitor<string> {
@@ -34,35 +33,99 @@ export class MarkdownExporter extends Visitor<string> {
34
33
  switch (node.kind) {
35
34
  case 'bold': return `**${content}**`
36
35
  case 'italic': return `*${content}*`
37
- case 'underline': return content
36
+ case 'underline': return `++${content}++`
38
37
  case 'strikethrough': return `~~${content}~~`
39
38
  case 'inline_code': return `\`${content}\``
40
39
  case 'code': return `\`\`\`\n${content}\n\`\`\``
41
40
  case 'spoiler': return `||${content}||`
42
- case 'color': return content
43
- case 'font_size': return content
44
- case 'font': return content
45
- case 'heading': return `${'#'.repeat(Math.min(depth + 1, 6))} ${content}`
46
- case 'center': return content
47
- case 'right': return content
48
- case 'left': return content
49
- case 'url': return `[${content}](${this.extractValue(node) || content})`
50
- case 'email': return `[${content}](mailto:${this.extractValue(node) || content})`
51
- case 'profile': return `[${content}](https://osu.ppy.sh/users/${this.extractValue(node) || content})`
52
- case 'image': return `![Image](${node.text || ''})`
41
+ case 'color': {
42
+ const c = (node.metadata?.color as string) || this.extractValue(node) || '#ffffff'
43
+ return `[${content}]{${c}}`
44
+ }
45
+ case 'font_size': {
46
+ const s = (node.metadata?.size as string) || this.extractValue(node) || '100'
47
+ return `[${content}]{size="${s}"}`
48
+ }
49
+ case 'font': {
50
+ const f = (node.metadata?.font as string) || this.extractValue(node) || 'sans-serif'
51
+ return `[${content}]{font="${f}"}`
52
+ }
53
+ case 'heading': {
54
+ const level = (node.metadata?.level as number) || depth + 1
55
+ return `${'#'.repeat(Math.min(level, 6))} ${content}`
56
+ }
57
+ case 'center': return `-> ${content.trim()} <-`
58
+ case 'right': return `-> ${content.trim()} ->`
59
+ case 'left': return `[left]${content}[/left]`
60
+ case 'align': {
61
+ const a = (node.metadata?.align as string) || this.extractValue(node) || 'center'
62
+ if (a === 'center') return `-> ${content.trim()} <-`
63
+ if (a === 'right') return `-> ${content.trim()} ->`
64
+ return `[align=${a}]${content}[/align]`
65
+ }
66
+ case 'url': return `[${content}](${node.metadata?.href || this.extractValue(node) || content})`
67
+ case 'email': {
68
+ const addr = (node.metadata?.href as string)?.replace('mailto:', '') || this.extractValue(node) || content
69
+ return `[${content}](mailto:${addr})`
70
+ }
71
+ case 'profile': return `[${content}](https://osu.ppy.sh/users/${node.metadata?.username || this.extractValue(node) || content})`
72
+ case 'image': {
73
+ const src = (node.metadata?.src as string) || (node.text && !node.text.startsWith('=') ? node.text : '') || content
74
+ const alt = (node.metadata?.alt as string) || (content && content !== src ? content : 'Image')
75
+ return `![${alt}](${src})`
76
+ }
53
77
  case 'video': return `[🎥 YouTube Video](https://youtube.com/watch?v=${node.text || ''})`
54
78
  case 'audio': return `[🎵 Audio](${node.text || ''})`
55
- case 'quote': return `> **${this.extractValue(node) || 'Quote'}**\n> ${content.replace(/\\n/g, '\n> ')}`
56
- case 'notice': return `> [!NOTE]\n> ${content.replace(/\\n/g, '\n> ')}`
57
- case 'spoilerbox': return `**${this.renderTitleMarkdown(node) || 'Spoiler'}**\n${content}`
79
+ case 'quote': {
80
+ const source = (node.metadata?.source as string) || this.extractValue(node)
81
+ const header = source ? `**${source}**\n> ` : ''
82
+ return `> ${header}${content.replace(/\n/g, '\n> ')}`
83
+ }
84
+ case 'notice': {
85
+ const color = (node.metadata?.color as string) || this.extractValue(node)
86
+ const colorAttr = color ? `=${color}` : ''
87
+ return `[notice${colorAttr}]\n${content}\n[/notice]`
88
+ }
89
+ case 'wnotice': {
90
+ const color = (node.metadata?.color as string) || this.extractValue(node)
91
+ const colorAttr = color ? `=${color}` : ''
92
+ return `[wnotice${colorAttr}]\n${content}\n[/wnotice]`
93
+ }
94
+ case 'spoilerbox':
58
95
  case 'box':
59
- case 'boxw': return `**${this.renderTitleMarkdown(node) || 'Box'}**\n${content}`
60
- case 'list': return content
61
- case 'list_item': return `${' '.repeat(Math.max(0, depth - 1))}- ${content.trim()}`
96
+ case 'boxw': {
97
+ const title = this.renderTitleMarkdown(node)
98
+ const header = title ? `::: details ${title}` : '::: details'
99
+ return `${header}\n${content}\n:::`
100
+ }
101
+ case 'list': {
102
+ const lines: string[] = []
103
+ let orderIdx = 1
104
+ for (const child of node.children) {
105
+ if (child.kind === 'list_item') {
106
+ const itemContent = child.children.map(c => this.exportNode(c, depth)).join('')
107
+ const isOrdered = node.metadata?.ordered
108
+ const prefix = isOrdered ? `${orderIdx++}. ` : '- '
109
+ lines.push(`${' '.repeat(Math.max(0, depth))}${prefix}${itemContent.trim()}`)
110
+ }
111
+ }
112
+ return lines.length > 0 ? lines.join('\n') + '\n' : content
113
+ }
114
+ case 'list_item': {
115
+ const isOrdered = node.parent?.metadata?.ordered
116
+ const prefix = isOrdered ? '1. ' : '- '
117
+ return `${' '.repeat(Math.max(0, depth - 1))}${prefix}${content.trim()}`
118
+ }
119
+ case 'separator': return '---'
62
120
  case 'spacing': return '\n'
63
121
  case 'empty_line': return '\n'
64
- case 'text':
65
- default: return content || node.text || ''
122
+ case 'paragraph': return content
123
+ case 'text': return content || node.text || ''
124
+ default: {
125
+ const val = this.extractValue(node) || (node.metadata?.value as string) || (node.metadata?.raw as string)
126
+ const attr = val !== undefined && val !== null && String(val) !== '' ? `=${val}` : ''
127
+ return `[${node.kind}${attr}]${content}[/${node.kind}]`
128
+ }
66
129
  }
67
130
  }
68
131
 
@@ -76,24 +139,18 @@ export class MarkdownExporter extends Visitor<string> {
76
139
 
77
140
  /**
78
141
  * Read a BBCode tag's `=VALUE` attribute.
142
+ */
143
+ /**
144
+ * El atributo del nodo, o `undefined` si no lleva.
79
145
  *
80
- * This previously read `node.properties` / `node.attributes`, neither of
81
- * which exists on RedNode so it silently returned `undefined` for every
82
- * node and every call site fell through to its placeholder. That is why
83
- * Markdown export dropped link targets and quote/box titles.
146
+ * El recorte lo hace `nodeAttrValue`, el mismo lector del `HTMLRenderer`;
147
+ * aquí sólo se cambia el contrato del caso vacío. Este exportador encadena
148
+ * `metadata ?? atributo ?? contenido`, así que necesita un `undefined`
149
+ * explícito donde `nodeAttrValue` devuelve el texto crudo — es la única
150
+ * diferencia, y por eso no se llama directamente.
84
151
  */
85
152
  private extractValue(node: RedNode): string | undefined {
86
- const text = node.text || ''
87
- const eqIdx = text.indexOf('=')
88
- if (eqIdx < 0) return undefined
89
-
90
- let value = text.slice(eqIdx + 1)
91
- if (
92
- (value.startsWith('"') && value.endsWith('"')) ||
93
- (value.startsWith("'") && value.endsWith("'"))
94
- ) {
95
- value = value.slice(1, -1)
96
- }
97
- return value || undefined
153
+ if (!(node.text || '').includes('=')) return undefined
154
+ return nodeAttrValue(node) || undefined
98
155
  }
99
156
  }
@@ -121,6 +121,7 @@ export class TiptapExporter extends Visitor<string> {
121
121
  case 'rainbow':
122
122
  case 'grow':
123
123
  case 'sinewave':
124
+ case 'paint':
124
125
  return { type: 'miliastryEffect', attrs: { effect: node.kind, ...node.metadata } }
125
126
  }
126
127
  return null
@@ -6,11 +6,14 @@
6
6
  * modificar el renderer HTML.
7
7
  *
8
8
  * Uso:
9
- * // Importar como CSS directo (soportado por Next.js, Vite, Webpack):
10
- * import 'MiliastryPlatform/DocumentEngine/Visuals/osu.css'
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'
11
16
  *
12
- * // O desde un componente React:
13
- * import 'MiliastryPlatform/DocumentEngine/Visuals/osu.css'
14
17
  * // Luego en el JSX: <div className="bbcode-preview" ...
15
18
  *
16
19
  * Para cambiar de tema visual, solo cambia el import del CSS.
@@ -6,11 +6,28 @@
6
6
  *
7
7
  * Scoped under `.bbcode-preview-lyne` and `.theme-lyne`.
8
8
  * All rules strictly override default/osu.css styles to ensure 0% pink bleed.
9
+ *
10
+ * No `@import` of Google Fonts here — the host supplies the faces.
11
+ *
12
+ * This file used to open with
13
+ * @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono…&family=Saira+Condensed…&family=Saira…')
14
+ * and that line broke the desktop app. Chromium reports a stylesheet as FAILED
15
+ * when any of its @imports fails to load, so under a CSP whose `style-src` does
16
+ * not list fonts.googleapis.com (Tauri's) — or simply with no network — the CSS
17
+ * chunk carrying this file rejected, the dynamic import of the whole BBCode
18
+ * feature rejected with it, and the kernel booted without an editor.
19
+ *
20
+ * Miliastry self-hosts these families with next/font and publishes them as
21
+ * `--font-syne-display` / `--font-syne-body` / `--font-syne-mono` (app/layout.tsx;
22
+ * `[data-theme='syne']` in globals.css reads the same variables). They are
23
+ * used when present; the plain family names remain as the fallback for a host
24
+ * that installs the fonts by other means.
9
25
  */
10
26
 
11
- @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=Saira+Condensed:wght@600;700&family=Saira:wght@400;600&display=swap');
12
-
13
- :root {
27
+ .theme-lyne,
28
+ .bbcode-preview-lyne,
29
+ [data-theme='syne'],
30
+ [data-dialect='lyne'] {
14
31
  --color-accent: #2EE6E2;
15
32
  --color-accent-bright: #68FFF8;
16
33
  --color-warning: #FFC34D;
@@ -18,9 +35,9 @@
18
35
  --color-text-mid: #9EACD4;
19
36
  --color-text-dim: #606C8F;
20
37
  --color-hairline: rgba(255, 255, 255, 0.12);
21
- --font-display: "Saira Condensed", sans-serif;
22
- --font-body: "Saira", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
23
- --font-numeric: "IBM Plex Mono", monospace;
38
+ --font-display: var(--font-syne-display, "Saira Condensed"), "Saira Condensed", sans-serif;
39
+ --font-body: var(--font-syne-body, "Saira"), "Saira", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
40
+ --font-numeric: var(--font-syne-mono, "IBM Plex Mono"), "IBM Plex Mono", monospace;
24
41
  --lyne-cut: 8px;
25
42
  }
26
43
 
@@ -52,6 +69,41 @@
52
69
  border-bottom-color: var(--color-accent, #2EE6E2) !important;
53
70
  }
54
71
 
72
+ .bbcode-preview-lyne a:has(img),
73
+ .bbcode-preview-lyne a:has(.bb-img),
74
+ .bbcode-preview-lyne a:has(svg),
75
+ .bbcode-preview-lyne a.bb-link-img,
76
+ .bbcode-preview.theme-lyne a:has(img),
77
+ .bbcode-preview.theme-lyne a:has(.bb-img),
78
+ .bbcode-preview.theme-lyne a:has(svg),
79
+ .bbcode-preview.theme-lyne a.bb-link-img {
80
+ border-bottom: none !important;
81
+ text-decoration: none !important;
82
+ display: inline-block;
83
+ vertical-align: middle;
84
+ }
85
+
86
+ .bbcode-preview-lyne a:has(img):hover,
87
+ .bbcode-preview-lyne a:has(.bb-img):hover,
88
+ .bbcode-preview-lyne a:has(svg):hover,
89
+ .bbcode-preview-lyne a.bb-link-img:hover,
90
+ .bbcode-preview.theme-lyne a:has(img):hover,
91
+ .bbcode-preview.theme-lyne a:has(.bb-img):hover,
92
+ .bbcode-preview.theme-lyne a:has(svg):hover,
93
+ .bbcode-preview.theme-lyne a.bb-link-img:hover {
94
+ border-bottom: none !important;
95
+ text-decoration: none !important;
96
+ }
97
+
98
+ .bbcode-preview-lyne a img,
99
+ .bbcode-preview-lyne a .bb-img,
100
+ .bbcode-preview.theme-lyne a img,
101
+ .bbcode-preview.theme-lyne a .bb-img {
102
+ vertical-align: middle;
103
+ border: none;
104
+ text-decoration: none;
105
+ }
106
+
55
107
  /* Forum @mentions — inherit the accent link look, slightly bolder so they
56
108
  read as handles rather than plain links. */
57
109
  .bbcode-preview-lyne a.bb-mention,
@@ -249,8 +301,8 @@
249
301
  * del autor ([boxw=Title:#hex]) si lo hay; si no, el hairline del tema. */
250
302
  .bbcode-preview-lyne details.boxw,
251
303
  .bbcode-preview.theme-lyne details.boxw {
252
- background: rgba(11, 16, 36, 0.5) !important;
253
- border: 1px solid var(--box-accent, var(--card-accent, var(--glass-accent, var(--color-hairline, rgba(255, 255, 255, 0.12))))) !important;
304
+ background: var(--box-bg, rgba(11, 16, 36, 0.5)) !important;
305
+ border: 1px solid var(--box-border, var(--box-accent, var(--card-accent, var(--glass-accent, var(--color-hairline, rgba(255, 255, 255, 0.12)))))) !important;
254
306
  border-radius: 0 !important;
255
307
  clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px) !important;
256
308
  padding: 0 !important;
@@ -331,8 +383,8 @@
331
383
  width: 7px !important;
332
384
  height: 7px !important;
333
385
  flex: 0 0 7px !important;
334
- border-right: 1.5px solid currentColor !important;
335
- border-bottom: 1.5px solid currentColor !important;
386
+ border-right: 1px solid currentColor !important;
387
+ border-bottom: 1px solid currentColor !important;
336
388
  transform: rotate(-45deg) !important;
337
389
  transition: transform 0.15s ease !important;
338
390
  }
@@ -360,7 +412,7 @@
360
412
 
361
413
  .bbcode-preview-lyne details.boxw[open] > summary,
362
414
  .bbcode-preview.theme-lyne details.boxw[open] > summary {
363
- border-bottom: 1px solid var(--box-accent, var(--card-accent, var(--glass-accent, var(--color-hairline, rgba(255, 255, 255, 0.12))))) !important;
415
+ border-bottom: 1px solid var(--box-border, var(--box-accent, var(--card-accent, var(--glass-accent, var(--color-hairline, rgba(255, 255, 255, 0.12)))))) !important;
364
416
  }
365
417
 
366
418
  .bbcode-preview-lyne details[open] > summary::before,
@@ -370,7 +422,8 @@
370
422
 
371
423
  .bbcode-preview-lyne details > summary:hover,
372
424
  .bbcode-preview.theme-lyne details > summary:hover {
373
- color: var(--color-accent-bright, #68FFF8) !important;
425
+ color: var(--box-accent, var(--card-accent, var(--glass-accent, var(--color-accent-bright, #68FFF8)))) !important;
426
+ filter: brightness(1.25) !important;
374
427
  }
375
428
 
376
429
  /* ─── Lists & Markers ───────────────────────────────────────────────────── */
@@ -469,6 +522,7 @@
469
522
  .bb-table {
470
523
  width: 100% !important;
471
524
  min-width: 380px !important;
525
+ table-layout: fixed !important;
472
526
  border-collapse: separate !important;
473
527
  border-spacing: 0 !important;
474
528
  margin: 0 !important;
@@ -518,6 +572,64 @@
518
572
  line-height: 1.3 !important;
519
573
  text-transform: uppercase !important;
520
574
  padding: 8px 10px !important;
575
+ text-align: left !important;
576
+ }
577
+
578
+ .bb-table .bb-th.bb-th-full {
579
+ width: 100% !important;
580
+ }
581
+
582
+ /* Table header alignments */
583
+ .bb-table .bb-th.bb-th-left,
584
+ .bb-table .bb-th:has(.bb-left),
585
+ .bb-table .bb-th:has([style*="text-align:left"]),
586
+ .bb-table .bb-th:has([style*="text-align: left"]),
587
+ .bb-left .bb-table .bb-th {
588
+ text-align: left !important;
589
+ }
590
+
591
+ .bb-table .bb-th.bb-th-left .bb-table-badge,
592
+ .bb-table .bb-th:has(.bb-left) .bb-table-badge,
593
+ .bb-table .bb-th:has([style*="text-align:left"]) .bb-table-badge,
594
+ .bb-table .bb-th:has([style*="text-align: left"]) .bb-table-badge,
595
+ .bb-left .bb-table .bb-th .bb-table-badge {
596
+ margin-right: auto !important;
597
+ margin-left: 0 !important;
598
+ }
599
+
600
+ .bb-table .bb-th.bb-th-centered,
601
+ .bb-table .bb-th.bb-th-center,
602
+ .bb-table .bb-th:has(.bb-centre),
603
+ .bb-table .bb-th:has([style*="text-align:center"]),
604
+ .bb-table .bb-th:has([style*="text-align: center"]),
605
+ .bb-centre .bb-table .bb-th {
606
+ text-align: center !important;
607
+ }
608
+
609
+ .bb-table .bb-th.bb-th-centered .bb-table-badge,
610
+ .bb-table .bb-th.bb-th-center .bb-table-badge,
611
+ .bb-table .bb-th:has(.bb-centre) .bb-table-badge,
612
+ .bb-table .bb-th:has([style*="text-align:center"]) .bb-table-badge,
613
+ .bb-table .bb-th:has([style*="text-align: center"]) .bb-table-badge,
614
+ .bb-centre .bb-table .bb-th .bb-table-badge {
615
+ margin: 0 auto !important;
616
+ }
617
+
618
+ .bb-table .bb-th.bb-th-right,
619
+ .bb-table .bb-th:has(.bb-right),
620
+ .bb-table .bb-th:has([style*="text-align:right"]),
621
+ .bb-table .bb-th:has([style*="text-align: right"]),
622
+ .bb-right .bb-table .bb-th {
623
+ text-align: right !important;
624
+ }
625
+
626
+ .bb-table .bb-th.bb-th-right .bb-table-badge,
627
+ .bb-table .bb-th:has(.bb-right) .bb-table-badge,
628
+ .bb-table .bb-th:has([style*="text-align:right"]) .bb-table-badge,
629
+ .bb-table .bb-th:has([style*="text-align: right"]) .bb-table-badge,
630
+ .bb-right .bb-table .bb-th .bb-table-badge {
631
+ margin-left: auto !important;
632
+ margin-right: 0 !important;
521
633
  }
522
634
 
523
635
  .bb-table-badge {
@@ -733,7 +845,7 @@
733
845
  .bb-neon-box {
734
846
  padding: 16px !important;
735
847
  margin: 0 !important;
736
- border: 2px solid var(--neon-color, var(--color-accent, #2EE6E2)) !important;
848
+ border: 1px solid var(--neon-color, var(--color-accent, #2EE6E2)) !important;
737
849
  background: color-mix(in srgb, var(--neon-color, var(--color-accent, #2EE6E2)) 4%, rgba(7, 10, 23, 0.55)) !important;
738
850
  border-radius: 0 !important;
739
851
  box-shadow: none !important;
@@ -758,10 +870,9 @@
758
870
  align-items: center !important;
759
871
  }
760
872
 
761
- /* Circle: replica el .bbCircle original de Lyne (Main Bridge/line/web/
762
- * components/bbcode.module.css). aspect-ratio:1 + centrado convierten el div
763
- * en un círculo real con el contenido al centro; fondo y borde lo pintan como
764
- * badge. clip-path:none evita que herede los cortes de esquina de los paneles. */
873
+ /* Square: cuadradito badge con las muescas angulares características de Lyne.
874
+ * aspect-ratio: 1/1, centrado, borde hairline y corte angular de esquina. */
875
+ .bb-square,
765
876
  .bb-circle {
766
877
  display: inline-flex !important;
767
878
  align-items: center !important;
@@ -771,11 +882,11 @@
771
882
  min-height: 1.85em !important;
772
883
  padding: 0.2em 0.35em !important;
773
884
  box-sizing: border-box !important;
774
- border-radius: 50% !important;
885
+ border-radius: 0 !important;
775
886
  overflow: hidden !important;
776
- background: rgb(11 16 36 / 0.5) !important;
777
- border: 1px solid var(--color-hairline, rgba(255, 255, 255, 0.12)) !important;
778
- clip-path: none !important;
887
+ background: var(--square-bg, rgb(11 16 36 / 0.5)) !important;
888
+ border: 1px solid var(--square-border, var(--square-accent, var(--color-hairline, rgba(255, 255, 255, 0.12)))) !important;
889
+ clip-path: polygon(4px 0, 100% 0, 100% calc(100% - 4px), calc(100% - 4px) 100%, 0 100%, 0 4px) !important;
779
890
  text-align: center !important;
780
891
  line-height: 1 !important;
781
892
  vertical-align: middle !important;
@@ -1062,3 +1173,62 @@
1062
1173
  background-clip: text !important;
1063
1174
  color: transparent !important;
1064
1175
  }
1176
+
1177
+ /* ─── Lyne (SYNE) Preview Highlight Overrides ────────────────────────────── */
1178
+
1179
+ .theme-lyne [data-bbid]:hover:not(:has([data-bbid]:hover, [data-node-id]:hover)),
1180
+ .bbcode-preview-lyne [data-bbid]:hover:not(:has([data-bbid]:hover, [data-node-id]:hover)) {
1181
+ outline: 1.5px solid #2EE6E2 !important;
1182
+ background-color: rgba(46, 230, 226, 0.1) !important;
1183
+ box-shadow: 0 0 8px rgba(46, 230, 226, 0.25) !important;
1184
+ }
1185
+
1186
+ .theme-lyne[data-hover-highlight="on"] [data-bbid]:hover:not(:has([data-bbid]:hover, [data-node-id]:hover)),
1187
+ .bbcode-preview-lyne[data-hover-highlight="on"] [data-bbid]:hover:not(:has([data-bbid]:hover, [data-node-id]:hover)),
1188
+ .theme-lyne[data-hover-highlight="on"] [data-node-id]:hover:not(:has([data-bbid]:hover, [data-node-id]:hover)),
1189
+ .bbcode-preview-lyne[data-hover-highlight="on"] [data-node-id]:hover:not(:has([data-bbid]:hover, [data-node-id]:hover)) {
1190
+ outline: 1px dashed rgba(46, 230, 226, 0.55) !important;
1191
+ box-shadow: 0 0 6px rgba(46, 230, 226, 0.2) !important;
1192
+ }
1193
+
1194
+ .theme-lyne [data-node-id].cursor-highlight,
1195
+ .bbcode-preview-lyne [data-node-id].cursor-highlight,
1196
+ .theme-lyne .cursor-highlight,
1197
+ .bbcode-preview-lyne .cursor-highlight {
1198
+ outline: 2px solid #2EE6E2 !important;
1199
+ background-color: rgba(46, 230, 226, 0.18) !important;
1200
+ box-shadow: 0 0 12px rgba(46, 230, 226, 0.35) !important;
1201
+ }
1202
+
1203
+ .theme-lyne .bb-empty-line.cursor-highlight,
1204
+ .bbcode-preview-lyne .bb-empty-line.cursor-highlight {
1205
+ background-color: rgba(46, 230, 226, 0.22) !important;
1206
+ outline: 2px solid #2EE6E2 !important;
1207
+ box-shadow: 0 0 8px rgba(46, 230, 226, 0.3) !important;
1208
+ }
1209
+
1210
+ .theme-lyne[data-hover-highlight="on"] .bb-empty-line:hover,
1211
+ .bbcode-preview-lyne[data-hover-highlight="on"] .bb-empty-line:hover {
1212
+ outline: 1px dashed rgba(46, 230, 226, 0.55) !important;
1213
+ background-color: rgba(46, 230, 226, 0.1) !important;
1214
+ }
1215
+
1216
+ .theme-lyne div.bb-block.cursor-highlight::after,
1217
+ .bbcode-preview-lyne div.bb-block.cursor-highlight::after {
1218
+ box-shadow: inset 0 0 0 2px #2EE6E2, 0 0 10px rgba(46, 230, 226, 0.35) !important;
1219
+ background-color: rgba(46, 230, 226, 0.18) !important;
1220
+ }
1221
+
1222
+ .theme-lyne span.bb-block.cursor-highlight,
1223
+ .bbcode-preview-lyne span.bb-block.cursor-highlight {
1224
+ outline: 2px solid #2EE6E2 !important;
1225
+ background-color: rgba(46, 230, 226, 0.18) !important;
1226
+ box-shadow: 0 0 8px rgba(46, 230, 226, 0.3) !important;
1227
+ }
1228
+
1229
+ .theme-lyne ::selection,
1230
+ .bbcode-preview-lyne ::selection {
1231
+ background-color: rgba(46, 230, 226, 0.3) !important;
1232
+ color: #ffffff !important;
1233
+ }
1234
+
@@ -33,6 +33,28 @@
33
33
  color: #94C8EA;
34
34
  border-bottom: 1px solid rgba(126,184,224,0.7);
35
35
  }
36
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a:has(img),
37
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a:has(.bb-img),
38
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a:has(svg),
39
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a.bb-link-img {
40
+ border-bottom: none !important;
41
+ text-decoration: none !important;
42
+ display: inline-block;
43
+ vertical-align: middle;
44
+ }
45
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a:has(img):hover,
46
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a:has(.bb-img):hover,
47
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a:has(svg):hover,
48
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a.bb-link-img:hover {
49
+ border-bottom: none !important;
50
+ text-decoration: none !important;
51
+ }
52
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a img,
53
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) a .bb-img {
54
+ vertical-align: middle;
55
+ border: none;
56
+ text-decoration: none;
57
+ }
36
58
 
37
59
  /* ─── Headings ───────────────────────────────────────── */
38
60
 
@@ -99,13 +121,22 @@
99
121
  /* ─── Spoiler ────────────────────────────────────────── */
100
122
 
101
123
  .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) .spoiler {
102
- background: #000;
103
- color: #000;
104
- cursor: text;
124
+ background: #111111;
125
+ color: #111111;
126
+ cursor: pointer;
127
+ padding: 0 4px;
128
+ border-radius: 3px;
129
+ user-select: text;
130
+ transition: background 0.15s ease, color 0.15s ease;
131
+ }
132
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) .spoiler:hover,
133
+ .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) .spoiler:active {
134
+ color: #ffffff;
135
+ background: #333333;
105
136
  }
106
137
  .bbcode-preview:not(.bbcode-preview-lyne):not(.theme-lyne) .spoiler::selection {
107
- color: #fff;
108
- background: #555;
138
+ color: #ffffff;
139
+ background: #555555;
109
140
  }
110
141
 
111
142
  /* ─── Box / Spoilerbox ───────────────────────────────── */