@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
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
import { RedNode } from '../Syntax/RedNode'
|
|
43
43
|
import { HTMLRenderer } from './HTMLRenderer'
|
|
44
|
-
import { morphHTML } from './DOMMorpher'
|
|
44
|
+
import { morphHTML, morphElement } from './DOMMorpher'
|
|
45
45
|
import type { TextChangeRange } from '../Incremental/ChangeTracker'
|
|
46
46
|
|
|
47
47
|
export interface PatchBlocksOptions {
|
|
@@ -67,6 +67,8 @@ export interface PatchBlocksOptions {
|
|
|
67
67
|
* path on small documents.
|
|
68
68
|
*/
|
|
69
69
|
minWindowedBlocks?: number
|
|
70
|
+
/** If true, forces a full rebuild of the container instead of incremental patching. */
|
|
71
|
+
forceRebuild?: boolean
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
export interface PatchBlocksStats {
|
|
@@ -143,6 +145,53 @@ interface PatchCache {
|
|
|
143
145
|
|
|
144
146
|
const caches = new WeakMap<HTMLElement, PatchCache>()
|
|
145
147
|
|
|
148
|
+
/**
|
|
149
|
+
* ¿Está encendido el volcado verboso? (`globalThis.__BP_DEBUG__ = true`)
|
|
150
|
+
*
|
|
151
|
+
* Imprime por consola el detalle de la reconciliación por ventana. Es caro:
|
|
152
|
+
* sirve para leer un caso a mano, NO para medir con él encendido.
|
|
153
|
+
*/
|
|
154
|
+
function BP_DEBUG(): boolean {
|
|
155
|
+
return (globalThis as { __BP_DEBUG__?: boolean }).__BP_DEBUG__ === true
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Deja constancia de POR QUÉ `patchBlocksInto` tomó el camino que tomó, en
|
|
160
|
+
* `globalThis.__BP_LAST__` (`globalThis.__BP_TRACE__ = true` para encenderlo).
|
|
161
|
+
*
|
|
162
|
+
* Va aparte del volcado verboso a propósito: esta traza es UNA asignación por
|
|
163
|
+
* parcheo, así que se puede dejar encendida MIENTRAS SE MIDE sin falsear la
|
|
164
|
+
* medida, que es justo cuando hace falta.
|
|
165
|
+
*
|
|
166
|
+
* Existe porque "¿por qué esto no usó el camino incremental?" es cara de
|
|
167
|
+
* contestar desde fuera: el bundle está minificado, `patchBlocksInto` devuelve
|
|
168
|
+
* `mode` pero no el motivo, y quien lo llama (el preview) tira las
|
|
169
|
+
* estadísticas. Diagnosticar la caída a `fullRebuild` por tecla en el
|
|
170
|
+
* documento de 547 KB costó desminificar a mano una función dentro de una
|
|
171
|
+
* línea de 75.000 caracteres; con esto se lee `__BP_LAST__` y ya está.
|
|
172
|
+
*
|
|
173
|
+
* Coste apagada: una comparación de identidad por parcheo.
|
|
174
|
+
*/
|
|
175
|
+
function bpTrace(reason: Record<string, unknown>): void {
|
|
176
|
+
if ((globalThis as { __BP_TRACE__?: boolean }).__BP_TRACE__ === true) {
|
|
177
|
+
;(globalThis as { __BP_LAST__?: unknown }).__BP_LAST__ = reason
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Evict `container` from the patch cache so the next patch rebuilds afresh.
|
|
183
|
+
*/
|
|
184
|
+
export function clearPatchCache(container: HTMLElement): void {
|
|
185
|
+
// Contador de invalidaciones bajo la misma traza barata: "cuántas veces se
|
|
186
|
+
// tira la caché por pulsación" es la pregunta que destapó el fullRebuild por
|
|
187
|
+
// tecla, y no se puede contestar desde fuera del módulo.
|
|
188
|
+
if ((globalThis as { __BP_TRACE__?: boolean }).__BP_TRACE__ === true) {
|
|
189
|
+
const g = globalThis as { __BP_CLEARS__?: number }
|
|
190
|
+
g.__BP_CLEARS__ = (g.__BP_CLEARS__ ?? 0) + 1
|
|
191
|
+
}
|
|
192
|
+
caches.delete(container)
|
|
193
|
+
}
|
|
194
|
+
|
|
146
195
|
const defaultRenderer = new HTMLRenderer()
|
|
147
196
|
|
|
148
197
|
function getCache(container: HTMLElement): PatchCache {
|
|
@@ -166,6 +215,21 @@ function blockKey(node: RedNode, index: number): string {
|
|
|
166
215
|
return node.id ?? `__block_${index}`
|
|
167
216
|
}
|
|
168
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Blocks whose HTML is NOT a function of the node alone.
|
|
220
|
+
*
|
|
221
|
+
* A newline leaf renders `<br>` or nothing depending on which tags surround
|
|
222
|
+
* it — `[/box]\n` loses the newline, `text\n` keeps it (see
|
|
223
|
+
* `HTMLRenderer.isNewlineSwallowed`). The block cache is keyed by node
|
|
224
|
+
* identity, so reordering blocks without touching them keeps every id and
|
|
225
|
+
* every cached fragment while changing what the newlines between them mean.
|
|
226
|
+
* Re-rendering them unconditionally costs a couple of sibling hops and keeps
|
|
227
|
+
* the patched DOM equal to a full render, which is the whole contract.
|
|
228
|
+
*/
|
|
229
|
+
function isContextSensitive(node: RedNode): boolean {
|
|
230
|
+
return node.kind === 'spacing' || node.kind === 'empty_line'
|
|
231
|
+
}
|
|
232
|
+
|
|
169
233
|
/** Build a DOM node from an HTML fragment (first child), decoding entities. */
|
|
170
234
|
function nodeFromHtml(html: string): Node {
|
|
171
235
|
const t = document.createElement('template')
|
|
@@ -230,7 +294,6 @@ function shouldMorphInPlace(
|
|
|
230
294
|
return (
|
|
231
295
|
run.kind === 'element' &&
|
|
232
296
|
!!run.node.id &&
|
|
233
|
-
run.node.children.length > 0 &&
|
|
234
297
|
canMorphInPlace(run.node) &&
|
|
235
298
|
renderedTag(run.html) === element.tagName
|
|
236
299
|
)
|
|
@@ -352,7 +415,7 @@ function reconcileKeyed(
|
|
|
352
415
|
let patched = 0
|
|
353
416
|
try {
|
|
354
417
|
runs = buildRuns(blocks, keys, (node, key) => {
|
|
355
|
-
if (cache.lastNode.get(key) === node) {
|
|
418
|
+
if (cache.lastNode.get(key) === node && !isContextSensitive(node)) {
|
|
356
419
|
return { html: cache.lastHtml.get(key) ?? '', kind: cache.lastClass.get(key) ?? 'none' }
|
|
357
420
|
}
|
|
358
421
|
const html = renderer.render(node)
|
|
@@ -420,13 +483,19 @@ function reconcileKeyed(
|
|
|
420
483
|
}
|
|
421
484
|
|
|
422
485
|
if (element.nodeType === 1 && shouldMorphInPlace(element as Element, run)) {
|
|
423
|
-
// Element with
|
|
424
|
-
// place, preserving the element
|
|
425
|
-
//
|
|
426
|
-
//
|
|
427
|
-
|
|
486
|
+
// Element with a matching tag: morph its attributes and children in
|
|
487
|
+
// place via morphElement, preserving the element identity and runtime state
|
|
488
|
+
// (such as an open <details>, its <summary> and title, or media playback).
|
|
489
|
+
// Tag mismatch (kind change at a stable slot) or wrapper kinds
|
|
490
|
+
// (code/svg/imagemap) go the replace path below.
|
|
491
|
+
const newEl = nodeFromHtml(run.html)
|
|
492
|
+
if (newEl && newEl.nodeType === 1 && (newEl as Element).tagName === (element as Element).tagName) {
|
|
493
|
+
morphElement(element as Element, newEl as Element)
|
|
494
|
+
} else {
|
|
495
|
+
container.replaceChild(newEl, element)
|
|
496
|
+
}
|
|
428
497
|
} else {
|
|
429
|
-
// Text run, an element leaf (img/video/audio
|
|
498
|
+
// Text run, an element leaf (img/video/audio/<br>), a kind whose tag
|
|
430
499
|
// changed, or a wrapper renderer kind: the node's own structure must
|
|
431
500
|
// change — replace outright.
|
|
432
501
|
container.replaceChild(nodeFromHtml(run.html), element)
|
|
@@ -590,7 +659,7 @@ function reconcileWindowed(
|
|
|
590
659
|
let runs: PatchRun[]
|
|
591
660
|
try {
|
|
592
661
|
runs = buildRuns(blocks, keys, (node, key) => {
|
|
593
|
-
if (cache.lastNode.get(key) === node) {
|
|
662
|
+
if (cache.lastNode.get(key) === node && !isContextSensitive(node)) {
|
|
594
663
|
return { html: cache.lastHtml.get(key) ?? '', kind: cache.lastClass.get(key) ?? 'none' }
|
|
595
664
|
}
|
|
596
665
|
const html = renderer.render(node)
|
|
@@ -607,10 +676,15 @@ function reconcileWindowed(
|
|
|
607
676
|
|
|
608
677
|
const oldRuns = cache.lastRuns
|
|
609
678
|
|
|
610
|
-
const DBG = (
|
|
679
|
+
const DBG = BP_DEBUG()
|
|
611
680
|
const dbg = (...a: unknown[]): void => {
|
|
612
681
|
if (DBG) console.log('[BP]', ...a)
|
|
613
682
|
}
|
|
683
|
+
// OJO: `dbg(...)` evalúa sus argumentos ANTES de entrar, así que las
|
|
684
|
+
// llamadas que construyen algo —`.map()`, plantillas, `slice`— van dentro
|
|
685
|
+
// de un `if (DBG)`. Este es el camino de cada pulsación: los cuatro
|
|
686
|
+
// volcados de abajo recorrían la ventana entera y construían una cadena por
|
|
687
|
+
// ejecución, con la depuración apagada, para tirarlo todo.
|
|
614
688
|
|
|
615
689
|
|
|
616
690
|
// ── Old window (OLD coordinates). ──────────────────────────────────────
|
|
@@ -749,14 +823,22 @@ function reconcileWindowed(
|
|
|
749
823
|
const oldWinRunsFinal = oldRuns.slice(oldWinFrom2, oldWinTo)
|
|
750
824
|
const newWinRunsFinal = newWinRuns
|
|
751
825
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
826
|
+
if (DBG) {
|
|
827
|
+
dbg('change', { start: change.start, end: change.end, endOld: change.endOld })
|
|
828
|
+
dbg('oldWin', oldWinFrom2, oldWinTo, 'keys', oldWinRunsFinal.map((r) => `${r.key}:${r.node.kind}`).slice(0, 40))
|
|
829
|
+
dbg('newWin', fromNew, toNew, 'keys', newWinRunsFinal.map((r) => `${r.key}:${r.node.kind}`).slice(0, 40))
|
|
830
|
+
dbg('newWinKeySet', [...newWinRunsFinal.map((r) => r.key).slice(0, 40)])
|
|
831
|
+
}
|
|
756
832
|
|
|
757
833
|
// DOM anchor at the window start, and the window's old nodes.
|
|
758
|
-
|
|
759
|
-
for
|
|
834
|
+
// Indexed, not walked. `childNodes` is a live NodeList with indexed access,
|
|
835
|
+
// so asking for the window's first node is one lookup; stepping to it one
|
|
836
|
+
// `nextSibling` at a time was O(blocks before the edit) and, on a document
|
|
837
|
+
// of 30.000 top-level blocks, the single largest term left in this
|
|
838
|
+
// function's bookkeeping — 11 ms per keystroke, more than the reconcile it
|
|
839
|
+
// was preparing for. The two are exactly equivalent: the k-th child is the
|
|
840
|
+
// k-th child.
|
|
841
|
+
const anchorNode: Node | null = container.childNodes[oldWinFrom2] ?? null
|
|
760
842
|
const oldWinNodes: Node[] = []
|
|
761
843
|
let node: Node | null = anchorNode
|
|
762
844
|
for (let i = oldWinFrom2; i < oldWinTo; i++) {
|
|
@@ -800,7 +882,7 @@ function reconcileWindowed(
|
|
|
800
882
|
const k = oldWinRunsFinal[i].key
|
|
801
883
|
if (newKeySet.has(k) || newByNode.has(oldWinRunsFinal[i].node)) continue
|
|
802
884
|
const el = oldWinNodes[i]
|
|
803
|
-
dbg('orphan?', k, oldWinRunsFinal[i].node.kind, 'el=', el ? (el.nodeType === 1 ? (el as Element).tagName : `text:'${String((el as Text).data ?? '').slice(0, 20)}'`) : 'null')
|
|
885
|
+
if (DBG) dbg('orphan?', k, oldWinRunsFinal[i].node.kind, 'el=', el ? (el.nodeType === 1 ? (el as Element).tagName : `text:'${String((el as Text).data ?? '').slice(0, 20)}'`) : 'null')
|
|
804
886
|
if (el && !removed.has(el)) {
|
|
805
887
|
removed.add(el)
|
|
806
888
|
container.removeChild(el)
|
|
@@ -828,7 +910,7 @@ function reconcileWindowed(
|
|
|
828
910
|
const prev = oldRunByKey.get(run.key) ?? oldRunByNode.get(run.node)
|
|
829
911
|
const element = oldByKey.get(run.key) ?? oldByNode.get(run.node)
|
|
830
912
|
|
|
831
|
-
dbg('WALK', run.key, run.node.kind, 'element=', element ? (element.nodeType === 1 ? (element as Element).tagName : 'text') : 'NEW', 'cursor=', cursor ? (cursor.nodeType === 1 ? (cursor as Element).tagName : 'text') : 'null')
|
|
913
|
+
if (DBG) dbg('WALK', run.key, run.node.kind, 'element=', element ? (element.nodeType === 1 ? (element as Element).tagName : 'text') : 'NEW', 'cursor=', cursor ? (cursor.nodeType === 1 ? (cursor as Element).tagName : 'text') : 'null')
|
|
832
914
|
|
|
833
915
|
if (!element) {
|
|
834
916
|
container.insertBefore(nodeFromHtml(run.html), cursor)
|
|
@@ -847,7 +929,12 @@ function reconcileWindowed(
|
|
|
847
929
|
}
|
|
848
930
|
|
|
849
931
|
if (element.nodeType === 1 && shouldMorphInPlace(element as Element, run)) {
|
|
850
|
-
|
|
932
|
+
const newEl = nodeFromHtml(run.html)
|
|
933
|
+
if (newEl && newEl.nodeType === 1 && (newEl as Element).tagName === (element as Element).tagName) {
|
|
934
|
+
morphElement(element as Element, newEl as Element)
|
|
935
|
+
} else {
|
|
936
|
+
container.replaceChild(newEl, element)
|
|
937
|
+
}
|
|
851
938
|
} else {
|
|
852
939
|
container.replaceChild(nodeFromHtml(run.html), element)
|
|
853
940
|
}
|
|
@@ -858,7 +945,7 @@ function reconcileWindowed(
|
|
|
858
945
|
// the first suffix node. Leftovers of runs that rendered to a different
|
|
859
946
|
// node count sit between the walk's end and the suffix — drop them.
|
|
860
947
|
let tail = cursor
|
|
861
|
-
dbg('tail-safety: cursor=', cursor ? (cursor.nodeType === 1 ? (cursor as Element).tagName : `text:'${String((cursor as Text).data ?? '').slice(0, 15)}'`) : 'null', 'afterWindow=', afterWindow ? (afterWindow.nodeType === 1 ? (afterWindow as Element).tagName : `text:'${String((afterWindow as Text).data ?? '').slice(0, 15)}'`) : 'null')
|
|
948
|
+
if (DBG) dbg('tail-safety: cursor=', cursor ? (cursor.nodeType === 1 ? (cursor as Element).tagName : `text:'${String((cursor as Text).data ?? '').slice(0, 15)}'`) : 'null', 'afterWindow=', afterWindow ? (afterWindow.nodeType === 1 ? (afterWindow as Element).tagName : `text:'${String((afterWindow as Text).data ?? '').slice(0, 15)}'`) : 'null')
|
|
862
949
|
let tailCount = 0
|
|
863
950
|
while (tail !== afterWindow) {
|
|
864
951
|
if (!tail) break
|
|
@@ -923,9 +1010,15 @@ export function patchBlocksInto(
|
|
|
923
1010
|
return { mode: 'full', total: 0, patched: 0 }
|
|
924
1011
|
}
|
|
925
1012
|
|
|
1013
|
+
if (options.forceRebuild) {
|
|
1014
|
+
bpTrace({ why: 'forceRebuild' })
|
|
1015
|
+
return fullRebuild(container, rootNode, renderer, cache)
|
|
1016
|
+
}
|
|
1017
|
+
|
|
926
1018
|
// DOM out of sync with the cache (a host replaced the innerHTML behind our
|
|
927
1019
|
// back, or a run rendered to a different node count than last time).
|
|
928
1020
|
if (container.childNodes.length !== cache.lastRuns.length) {
|
|
1021
|
+
bpTrace({ why: 'desync', kids: container.childNodes.length, runs: cache.lastRuns.length })
|
|
929
1022
|
return fullRebuild(container, rootNode, renderer, cache)
|
|
930
1023
|
}
|
|
931
1024
|
|
|
@@ -938,7 +1031,13 @@ export function patchBlocksInto(
|
|
|
938
1031
|
undefined
|
|
939
1032
|
if (change) {
|
|
940
1033
|
const windowed = reconcileWindowed(container, rootNode, change, renderer, cache, options)
|
|
941
|
-
if (windowed)
|
|
1034
|
+
if (windowed) {
|
|
1035
|
+
bpTrace({ why: 'windowed' })
|
|
1036
|
+
return windowed
|
|
1037
|
+
}
|
|
1038
|
+
bpTrace({ why: 'windowed-declinado' })
|
|
1039
|
+
} else {
|
|
1040
|
+
bpTrace({ why: 'sin-changeRange' })
|
|
942
1041
|
}
|
|
943
1042
|
|
|
944
1043
|
const blocks = rootNode.children
|
|
@@ -956,8 +1055,10 @@ export function patchBlocksInto(
|
|
|
956
1055
|
oldSet.add(k)
|
|
957
1056
|
}
|
|
958
1057
|
if (added >= cache.lastKeys.length) {
|
|
1058
|
+
bpTrace({ why: 'added>=lastKeys', added, lastKeys: cache.lastKeys.length })
|
|
959
1059
|
return fullRebuild(container, rootNode, renderer, cache)
|
|
960
1060
|
}
|
|
961
1061
|
|
|
1062
|
+
bpTrace({ why: 'keyed' })
|
|
962
1063
|
return reconcileKeyed(container, rootNode, keys, renderer, cache, options)
|
|
963
1064
|
}
|
|
@@ -97,34 +97,7 @@ function morphNodes(parent: Node, newParent: Node): void {
|
|
|
97
97
|
newNode.nodeType === 1 &&
|
|
98
98
|
(oldNode as Element).tagName === (newNode as Element).tagName
|
|
99
99
|
) {
|
|
100
|
-
|
|
101
|
-
const newEl = newNode as Element
|
|
102
|
-
|
|
103
|
-
// Preserve interactive runtime states (such as <details open> for spoilerboxes and boxes)
|
|
104
|
-
if (oldEl.tagName === 'DETAILS' && oldEl.hasAttribute('open')) {
|
|
105
|
-
newEl.setAttribute('open', '')
|
|
106
|
-
}
|
|
107
|
-
if (oldEl.classList.contains('open')) {
|
|
108
|
-
newEl.classList.add('open')
|
|
109
|
-
}
|
|
110
|
-
if (oldEl.classList.contains('is-open')) {
|
|
111
|
-
newEl.classList.add('is-open')
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Sync attributes in-place
|
|
115
|
-
for (const attr of Array.from(newEl.attributes)) {
|
|
116
|
-
if (oldEl.getAttribute(attr.name) !== attr.value) {
|
|
117
|
-
oldEl.setAttribute(attr.name, attr.value)
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
for (const attr of Array.from(oldEl.attributes)) {
|
|
121
|
-
if (!newEl.hasAttribute(attr.name)) {
|
|
122
|
-
oldEl.removeAttribute(attr.name)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Morph child nodes recursively
|
|
127
|
-
morphNodes(oldEl, newEl)
|
|
100
|
+
morphElement(oldNode as Element, newNode as Element)
|
|
128
101
|
} else {
|
|
129
102
|
// Different tag or nodeType: replace node
|
|
130
103
|
oldNode.parentNode!.replaceChild(newNode.cloneNode(true), oldNode)
|
|
@@ -132,3 +105,46 @@ function morphNodes(parent: Node, newParent: Node): void {
|
|
|
132
105
|
}
|
|
133
106
|
}
|
|
134
107
|
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Reconcile two HTML elements of the same tag in-place.
|
|
111
|
+
* Syncs attributes, preserves interactive states (<details open>, .open classes),
|
|
112
|
+
* and recursively morphs child nodes.
|
|
113
|
+
*/
|
|
114
|
+
export function morphElement(oldEl: Element, newEl: Element): void {
|
|
115
|
+
// Preserve interactive runtime states (such as <details open> for spoilerboxes and boxes)
|
|
116
|
+
if (oldEl.tagName === 'DETAILS' && oldEl.hasAttribute('open')) {
|
|
117
|
+
newEl.setAttribute('open', '')
|
|
118
|
+
}
|
|
119
|
+
if (oldEl.classList.contains('open')) {
|
|
120
|
+
newEl.classList.add('open')
|
|
121
|
+
}
|
|
122
|
+
if (oldEl.classList.contains('is-open')) {
|
|
123
|
+
newEl.classList.add('is-open')
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Sync attributes in-place.
|
|
127
|
+
//
|
|
128
|
+
// `attributes` se recorre por índice: `Array.from` asignaba un array
|
|
129
|
+
// por elemento y por sentido, dos por pareja morfada, y esto corre
|
|
130
|
+
// sobre la ventana cambiada en cada pulsación.
|
|
131
|
+
const newAttrs = newEl.attributes
|
|
132
|
+
for (let i = 0; i < newAttrs.length; i++) {
|
|
133
|
+
const attr = newAttrs[i]
|
|
134
|
+
if (oldEl.getAttribute(attr.name) !== attr.value) {
|
|
135
|
+
oldEl.setAttribute(attr.name, attr.value)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Hacia atrás: quitar un atributo compacta la colección viva, y
|
|
139
|
+
// recorrerla hacia delante mientras se borra se salta el siguiente.
|
|
140
|
+
const oldAttrs = oldEl.attributes
|
|
141
|
+
for (let i = oldAttrs.length - 1; i >= 0; i--) {
|
|
142
|
+
const name = oldAttrs[i].name
|
|
143
|
+
if (!newEl.hasAttribute(name)) {
|
|
144
|
+
oldEl.removeAttribute(name)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Morph child nodes recursively
|
|
149
|
+
morphNodes(oldEl, newEl)
|
|
150
|
+
}
|