palabras(docs): atasco base64 RESUELTO — causa real (deep-compare) + fixes

Corrige la conclusión de la investigación (la hipótesis DOM-src era errónea; el
CPU profile dio samePalabrasDocument = 34%). imagenes-base64.md §Causa REAL +
handoff §2026-07-19: fast-path d96e4cde7 (20→12ms) + blob 013ef5afc (12→8ms).
Lección anotada: perfilar ANTES de teorizar en atascos de perf.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
alpha-0.1-sec-dom
dev 3 months ago
parent 44a016538c
commit c9e7082213

@ -73,10 +73,18 @@
`onUploadImage`). Descartado: historia (comparte referencias), render (no re-decode). **Destapé y
arreglé** un anti-patrón: 6 effects del chrome (bubble/slash/activate/grip/inserter/resize) dependían de
`provider.html` (join O(documento)) como señal de cambio → recomputado por tecla → migrados a `blocks`
(O(1)). NO era el coste dominante del base64 (el residual ~4ms/MB/char parece intrínseco del navegador:
un `src` de MB en el DOM del contenteditable ralentiza el tecleo). **El fix de RAÍZ queda PENDIENTE de
decisión del usuario** (mañana): opciones en `imagenes-base64.md` §Opciones (aviso+cap / downscale /
render-como-blob-URL / solo-documentar). Verificado: check 0 palabras; Playwright grip+bubble OK.
(O(1)). (Este NO era el coste dominante — ver la resolución abajo.)
- **base64 atasco RESUELTO — `d96e4cde7` + `013ef5afc` (2026-07-20)**: un **CPU profile** destapó la
causa REAL (mi hipótesis DOM-src era ERRÓNEA): **`samePalabrasDocument` = 34% de las muestras** — una
comparación DEEP del doc en `syncExternalState` (el sync del `bind:value`) que recorría el string base64
por tecla, porque el editor escribe `value` con la MISMA referencia del doc interno. **Fix 1
(`d96e4cde7`)**: check por REFERENCIA antes del deep-compare (20→12ms/char). **Fix 2 (`013ef5afc`, win
secundario)**: `$adom.DataUrlBlobUrls` (utilidad NUEVA reutilizable) + swap de `<img data:>`→`blob:` en
palabras (el modelo conserva el base64; 12→8ms/char). Ubicación de la utilidad VALORADA: NO en `<Image>`
(ningún consumidor pasa base64 → prematuro). Verificado: profiler + Playwright + 58 tests (provider +
adom util). **LECCIÓN**: para un atasco de perf, **perfilar ANTES de teorizar** — dos hipótesis
(DOM-src, html) fallaron; el profiler dio la respuesta en un intento. Follow-up opcional NO hecho: aviso
dev + cap/downscale de la raíz (base64-en-modelo sin adapter) — solo acota el PESO del doc, no el tecleo.
### Deuda declarada de esta sesión
- **Langs es/en** de las acciones nuevas (`action.merge-right`/`merge-down`/`split-cell`/`resize-column`
@ -90,9 +98,10 @@
### Próximo (del mapa «qué queda del componente», presentado al usuario)
Finos de F3 (typography.* granular DENTRO del mega-control · familia efectiva del bubble) · **deuda del
motor**: `deleteRange` cross-callout (no-op diferido) · **base64: DECIDIR + implementar el fix de raíz**
(investigado — ver [`imagenes-base64.md`](./imagenes-base64.md); opciones + repro listos) · **F4.10
táctil** (gestos long-press/drag, necesita dispositivo) · **F5** (deltas ⇒ id de
motor**: `deleteRange` cross-callout (no-op diferido) · ~~base64~~ **RESUELTO** (`d96e4cde7` fast-path +
`013ef5afc` blob — la causa real era `samePalabrasDocument` en el `bind:value`, NO el DOM; ver
[`imagenes-base64.md`](./imagenes-base64.md)) · **F4.10 táctil** (gestos long-press/drag, necesita
dispositivo) · **F5** (deltas ⇒ id de
bloque obligatorio v3 · colaboración DG-11 · comentarios · export DOCX/PDF) · **puertas FD** (FD.3 mood del
chrome · FD.4 firma perceptual, requiere permiso). Y los langs diferidos + v1.1 del resize de arriba.

@ -57,29 +57,35 @@ Curva medida (Playwright, `keyboard.type` 20 chars; incluye overhead CDP):
| 300 KB base64 | 12.8 |
| 2 MB base64 | 17.9 |
→ **~+4 ms/MB/char**, monotónico. Tras descartar historia/render/html/value, el
residual parece **intrínseco del navegador**: un atributo `src` de varios MB en
el DOM del contenteditable ralentiza el tecleo (layout/entrada). La única forma
de eliminarlo es **que el DOM no cargue el data-URL gigante**.
## Opciones de fix (DECISIÓN PENDIENTE)
1. **Aviso dev + cap de tamaño** (bajo riesgo, mensaje correcto): sin adapter,
`console.warn` en dev («imágenes como base64 hinchan el documento; pasa
`onUploadImage`») + rechazar/avisar imágenes > N MB en el fallback.
2. **Downscale antes de base64** (trabajo medio): en el fallback, redimensionar
con canvas a un máximo (p.ej. 1600px) antes de convertir → el data-URL baja de
MB a ~decenas de KB → el atasco desaparece en la práctica.
3. **Render como blob URL** (fix completo, complejo): el modelo guarda base64
(persistencia) pero el RENDER usa un `blob:` URL corto → el DOM queda ligero →
cero jank aun con base64. Requiere gestionar el ciclo de vida del blob
(crear/revocar por bloque imagen).
4. **Solo documentar**: el fallback es dev-only; producción usa adapter. Cerrar
sin cambio de código.
**Doctrina existente**: el camino de PRODUCCIÓN es el adapter `onUploadImage`
(F4.1, `31d49c88`) → el JSON guarda una URL, nunca base64 (test lo verifica). El
base64 es el fallback de conveniencia dev.
→ **~+4 ms/MB/char**, monotónico. (Hipótesis inicial ERRÓNEA: «el `src` de MB en
el DOM»; ver la causa real abajo.)
## Causa REAL (hallada por CPU profile — 2026-07-20) ✅ RESUELTA
Un **CPU profile** del tecleo con la imagen de 2MB fue definitivo: **`samePalabrasDocument`
= 34% de las muestras** (391/1140). Es una **comparación DEEP de documentos** en
`syncExternalState` (el sync del `bind:value`, `palabras-provider.svelte.ts`):
compara el `value` bindeado contra el doc interno EN CADA cambio. Como el editor
escribe `value` con la MISMA referencia del doc interno (`publishState` →
`this.document`), esa comparación recorría el string base64 (MB) por tecla **para
nada**. NO era el DOM, ni la historia, ni `html`.
**Fix 1 — `d96e4cde7`** (el que arregla el atasco): check de igualdad por
REFERENCIA antes del deep-compare — el caso común (ediciones propias) en O(1); el
`samePalabrasDocument` solo corre si la referencia difiere de verdad. Medido:
**20 → 12 ms/char**.
**Fix 2 — `013ef5afc`** (win secundario): `$adom.DataUrlBlobUrls` + swap del `src`
de los `<img data:…>` por `blob:` URLs en palabras (el modelo conserva el base64).
El `src` de MB en el DOM SÍ añadía ~2-4ms. Medido: **12 → 8 ms/char**. Ubicación
valorada: la utilidad NO va en `<Image>` (ningún consumidor pasa base64 →
abstracción prematura); es reutilizable si algún día aparece uno.
**Sobre la raíz (base64-en-modelo sin adapter)**: sigue siendo dev-only por
diseño. El camino de PRODUCCIÓN es el adapter `onUploadImage` (F4.1, `31d49c88`) →
el JSON guarda una URL, nunca base64. **Follow-ups opcionales NO hechos**: aviso
dev cuando no hay adapter + cap/downscale de tamaño (el atasco de TECLEO ya está
resuelto por los dos fixes; esto solo acotaría el PESO del documento).
## Repro (para re-verificar cualquier fix)

Loading…
Cancel
Save

Powered by TurnKey Linux.