commit b1bb43919e8bed91acc21b4bb94d0ad9d34e3105 from: ale date: Fri Jul 31 00:24:39 2026 UTC Preview: no parte los bloques de código ``` en varios widgets split_paragraphs_with_offsets (agregado para el sync por párrafo) cortaba por cualquier línea en blanco, sin distinguir si estaba dentro de un bloque ```código``` — muy común tener líneas en blanco entre funciones/ comentarios en código real. Cada pedazo se montaba como su propio widget Markdown con su propio margen, así que el bloque se veía hecho pedazos con huecos enormes en vez de un solo bloque contiguo. Ahora un bloque ```...``` se detecta primero y se trata como un párrafo atómico — nunca se parte por las líneas en blanco que tenga adentro. El texto antes/después del bloque sigue partiéndose en párrafos finos para el sync, como antes. commit - 51d10b6fa8a8b60a714a894943377221cc93e435 commit + b1bb43919e8bed91acc21b4bb94d0ad9d34e3105 blob - 63304b2e41e165570a3cebe1e71b59a1842dc2b6 blob + 78ffd2d86e14a6fd54c1f34f719bcfabcef1a14a --- nezzontli_ctl/preview.py +++ nezzontli_ctl/preview.py @@ -39,6 +39,25 @@ def _parse_kv_args(raw): return {m.group(1): m.group(2) for m in _KV_RE.finditer(raw)} +_CODE_FENCE_RE = re.compile(r"```[^\n]*\n.*?\n```", re.DOTALL) + + +def _split_plain_paragraphs(text, base_offset): + """[(párrafo, offset_absoluto), ...] separados por 1+ líneas en blanco, + salteando los que quedan vacíos.""" + parts = [] + cursor = 0 + for m in re.finditer(r"\n{2,}", text): + para = text[cursor:m.start()] + if para.strip(): + parts.append((para, base_offset + cursor)) + cursor = m.end() + tail = text[cursor:] + if tail.strip(): + parts.append((tail, base_offset + cursor)) + return parts + + def split_paragraphs_with_offsets(chunk): """[(párrafo, offset_en_chunk), ...] separados por 1+ líneas en blanco, salteando los que quedan vacíos. Un tramo largo de texto plano (sin @@ -47,17 +66,20 @@ def split_paragraphs_with_offsets(chunk): imagen (si la había) se volvía a mover el preview. Sin más fórmulas después, se quedaba pegado ahí el resto del documento. Con un anchor por párrafo, el sync tiene puntos de referencia repartidos parejo en - todo el texto, no solo donde hay imágenes/fórmulas.""" + todo el texto, no solo donde hay imágenes/fórmulas. + + Los bloques ```código``` son un párrafo atómico, nunca se parten por + las líneas en blanco que tengan adentro (código con espaciado entre + funciones/comentarios, típico) — si se parten, cada pedazo reabre su + propio widget Markdown con su propio margen y el bloque se ve hecho + pedazos con huecos enormes en vez de un solo bloque de código.""" parts = [] cursor = 0 - for m in re.finditer(r"\n{2,}", chunk): - para = chunk[cursor:m.start()] - if para.strip(): - parts.append((para, cursor)) + for m in _CODE_FENCE_RE.finditer(chunk): + parts.extend(_split_plain_paragraphs(chunk[cursor:m.start()], cursor)) + parts.append((m.group(0), m.start())) cursor = m.end() - tail = chunk[cursor:] - if tail.strip(): - parts.append((tail, cursor)) + parts.extend(_split_plain_paragraphs(chunk[cursor:], cursor)) return parts