Postext lee un dialecto de markdown deliberadamente reducido.
El parser es un tokenizador escrito a mano — no una implementación completa de CommonMark — por lo que el formato de entrada es estrecho y predecible. La intención es doble: mantener el motor pequeño y rápido, y hacer que los documentos sean trivialmente portables entre Postext y cualquier otro lector de CommonMark (Obsidian, Pandoc, VS Code…). Todo lo que no aparezca en esta página se trata como texto plano o se elimina del flujo en línea.
Si construyes el documento por código, la función parseMarkdown (ver Configuración › Parseo) te devuelve exactamente la misma estructura de bloques que consume el motor de composición.
#Frontmatter
Un documento puede empezar con un bloque opcional de frontmatter YAML encerrado entre marcadores ---:
---
title: Capítulo uno
author: Jane Doe
publishDate: 2026-04-15
---
# Capítulo uno
La historia empieza aquí…Llama a extractFrontmatter(source) para separar el frontmatter del cuerpo. El objeto de metadatos parseados se devuelve junto con el markdown restante y el desplazamiento (en caracteres) donde empieza el cuerpo — útil si necesitas mapear errores o posiciones de cursor al documento original.
El frontmatter se parsea con gray-matter, por lo que se acepta cualquier forma válida de YAML. Postext solo consulta title, subtitle, author y publishDate; las demás claves se conservan en PostextContent.metadata y quedan disponibles para tu uso.
#Construcciones de bloque
Postext reconoce siete tipos de bloque de prosa, además de las directivas y las inserciones de recursos que se describen más adelante en esta página. Los bloques siempre terminan al encontrar una línea en blanco o el inicio de otro bloque.
| Construcción | Sintaxis | Notas |
|---|---|---|
| Encabezado | # Título … ###### H6 | De uno a seis caracteres # seguidos de un espacio y del texto. Los niveles 1–6 se corresponden directamente con headings.levels. |
| Párrafo | Texto plano en una o más líneas | Las líneas no vacías y no especiales consecutivas se unen con un único espacio y se emiten como un solo párrafo. Los saltos de línea manuales dentro de un párrafo no se conservan — usa una línea en blanco para empezar un nuevo párrafo. |
| Cita (blockquote) | > texto citado | Cada línea de la cita debe comenzar con > (seguido de un espacio opcional). Las líneas consecutivas de cita se combinan en un único bloque. |
| Lista no ordenada | - item, * item, + item | Se acepta cualquiera de las tres viñetas. El anidamiento usa exactamente dos espacios por nivel, hasta una profundidad máxima de 5. |
| Lista ordenada | 1. item, 2) item | Dígitos seguidos de . o ). El número inicial se conserva (una lista puede empezar en 5 o en 0). El separador renderizado en la salida procede de orderedLists.separator, no de la fuente. |
| Lista de tareas (GFM) | - [ ] pendiente, - [x] hecho | Un elemento de lista no ordenada con una casilla entre corchetes. Acepta x en minúscula o X en mayúscula. Se renderiza con los glifos taskCheckboxChar / taskCheckedChar. |
| Fórmula display | $$ … $$ | Una fórmula LaTeX compuesta como bloque propio. Se renderiza centrada en la columna, ajustada a la rejilla de línea base como una cabecera, y se mantiene vectorial en el PDF. Las formas de una sola línea y de valla multilínea se describen en Fórmulas matemáticas. |
Se tolera una única línea en blanco entre dos elementos de lista — la lista se mantiene unida. Dos o más líneas en blanco consecutivas terminan la lista.
Se aceptan listas de tipos mixtos a la misma profundidad (puedes pasar de no ordenada a ordenada a mitad de recorrido), pero el motor trata cada tramo por separado a efectos de numeración. En la práctica, conviene mantener un único tipo por profundidad salvo que haya un motivo claro para mezclarlos.
#Atributos de encabezado
Una línea de encabezado puede terminar con un bloque de atributos entre llaves — la misma sintaxis clave="valor" de las directivas:
# El largo camino {author="I. Zango Martín" year=1998}Las llaves y su contenido se eliminan del texto del encabezado (el título anterior se renderiza como El largo camino) y se guardan en el encabezado como attrs. Se exponen a las ranuras de diseño como marcadores {attr.<clave>}: en la ranura de diseño avanzado del propio encabezado, y en los encabezados y pies de página, donde se resuelven a partir del H1 del capítulo actual. Solo se reconoce un bloque equilibrado y sin llaves anidadas al final de la línea; un {} suelto o una llave sin cerrar permanece en el texto. Ver Configuración → Encabezados y pies.
Dos atributos tienen significado propio. style="<id>" aplica al encabezado un estilo de encabezado con nombre — un prólogo o una lista de autores con su propio diseño de apertura, sus cabeceras, su geometría de página y su tipografía de cuerpo, y sin número de capítulo cuando el estilo declara numbered: false. toc="false" (o "true") decide si el encabezado aparece en :::toc:
# Prólogo {style="preliminar"}
# Índice {style="preliminar" toc="false"}#Directivas
Las directivas son etiquetas de control de una sola línea escritas como :::nombre o :::nombre{atributos} en su propia línea. No producen salida visible — controlan el pipeline de colocación y numeración.
| Sintaxis | Efecto |
|---|---|
:::pagebreak | Fuerza que el siguiente bloque comience en una nueva página. |
:::pagebreak{parity="odd"} | Igual, además asegura que la nueva página sea impar (lado derecho). Inserta una página en blanco de relleno cuando sea necesario. |
:::pagebreak{parity="even"} | Igual, pero apuntando a una página par (lado izquierdo). |
:::pagebreak{parity="always-odd"} | Garantiza al menos una página separadora en blanco obligatoria antes de aterrizar en una página impar. La página separadora pertenece al contenido anterior; cualquier relleno de paridad adicional pertenece a lo que sigue. Útil cuando cada capítulo debe empezar un pliego nuevo. |
:::pagebreak{parity="always-even"} | Igual, pero apuntando a una página par. |
:::numbering{format="decimal" startAt=1} | En el siguiente límite de página, cambia la secuencia de numeración. Ambos atributos son opcionales — omite format para mantener el formato, omite startAt para continuar el contador. |
:::columnbreak | Termina aquí la columna actual: el siguiente bloque comienza en la siguiente columna de la misma página (o en una página nueva cuando la directiva cae en la última columna). No hace nada en una columna vacía, así que nunca genera una columna o página en blanco. La columna que termina conserva su hueco inferior — el equilibrado de columnas no la estira. |
:::toc | Imprime aquí el índice de contenidos: una entrada por encabezado de los niveles indicados (título, número, número de página y, opcionalmente, los autores del capítulo) y una fila por separador de parte, compuestas según la configuración toc. Las entradas siguen al documento — renombra, mueve o renumera un capítulo y el índice le sigue. |
Los valores de los atributos pueden ir entre comillas dobles ("…"), comillas simples ('…') o sin comillas (startAt=17). Una clave sin = se trata como una bandera presente con valor vacío.
Hoy solo se reconocen pagebreak, numbering, columnbreak y toc como directivas de una sola línea — cualquier otra línea :::nombre que no sea un contenedor (más abajo) se parsea como párrafo y genera un aviso Directiva desconocida en el sandbox.
#Contenedores
Un contenedor envuelve una serie de bloques entre dos vallas: una línea de apertura :::nombre o :::nombre{atributos}, luego cualquier contenido normal — párrafos, encabezados, listas, citas, fórmulas, incluso otras directivas — y una línea de cierre con un ::: a secas. Los contenedores pueden anidarse; cada ::: de cierre cierra el más interno de los abiertos.
:::callout{type="note"}
Mantén la linterna encendida **todas** las noches.
- Revisa la mecha.
- Recórtala al anochecer.
:::
Se reconocen tres nombres de contenedor. Lo que renderiza cada uno se define en su propia sección de la configuración:
| Sintaxis | Efecto |
|---|---|
:::callout{…} … ::: | Contenido en caja — una nota, un consejo o un aviso separado del cuerpo en una caja con borde o fondo tintado. |
:::paragraphs{…} … ::: | Una serie de párrafos compuesta con un estilo de párrafo con nombre (una entradilla, un epígrafe, unas notas en letra pequeña) en lugar del estilo del cuerpo. |
:::part{…} … ::: | Un abridor de parte o sección: el encabezado y el texto que encierra forman la página de apertura de una división mayor. |
Los atributos que admite cada contenedor, y cómo se estiliza, se documentan en Configuración. Los valores de los atributos siguen la misma gramática que las directivas. Una valla :::callout admite type (el id de un estilo de aviso configurado — los tipos desconocidos o ausentes usan el primer estilo), title (sobrescribe el título por defecto del estilo) y span / placement (column, page o side; here, top, bottom o fixed) para sobrescribir la extensión y la posición del estilo en esa caja:
:::callout{type="objectives" title="Qué aprenderás" span="page" placement="top"}
- Nombrar las partes de la linterna.
- Recortar la mecha sin tocar el cristal.
:::
Un aviso se compone como una caja — un título opcional y después su contenido, con la tipografía de cuerpo y de listas propia del estilo. Por defecto se mantiene unido y pasa entero a la columna o página siguiente cuando no cabe (una caja más alta que una columna entera se parte igualmente, en lugar de desbordar); un estilo con keepTogether: false le permite partirse entre sus bloques, o entre líneas, dejando al menos splitMinLines líneas de texto — o una figura, tabla, fórmula en bloque o caja anidada — a cada lado del corte. Las cajas a ancho completo (span="page") cortan la página en bandas de columnas; las cajas span="side" salen del flujo a la columna lateral solo para flotantes de una disposición de columna y media (layout.sideColumnRole: 'floats'), apiladas junto al texto que interrumpen, y se componen como cajas de columna donde no existe tal columna; las cajas placement="fixed" salen del flujo y se fijan a coordenadas de página (por ejemplo, un distintivo de autoevaluación en la esquina inferior izquierda de la última página del capítulo), y las columnas que cubren ceden esa zona; las cajas flotantes (placement="top" / "bottom") salen del flujo donde aparecen y ocupan la primera banda libre tras esa posición — el pie de la página, o la cabecera o el pie de la siguiente — mientras el texto que las sigue rellena la página que dejaron. Un estilo con floatBarrier: true (normalmente la caja de «puntos clave» que cierra el capítulo) convierte la caja en una barrera de flotantes: toda figura o tabla referenciada antes se coloca antes que ella — en los huecos libres de la página, o en páginas abiertas por delante de la caja — de modo que ningún flotante escapa más allá del final de su capítulo. Una valla :::nombre desconocida no es un contenedor — la línea se trata como texto, exactamente igual que una directiva desconocida.
Una valla :::part admite number (tal como quieras imprimirlo — "I", "IV", "3"; además se interpreta para que el diseño pueda reformatearlo) y title; los dos son opcionales. Un tercer atributo, palette="band=#hex" (varios pares id=#hex separados por comas), recolorea todos los colores de diseño vinculados a esos ids de paleta — cabeceras, banda de apertura, diseños de parte — y los colores del flujo de texto que comparten su valor base (títulos, negrita, referencias, viñetas, pies) para la parte y los capítulos que la siguen, hasta la siguiente parte; ver Configuración › Partes. El contenedor abre siempre una página propia: un salto de página con la paridad configurada antes, una columna única de cuerpo en los parts.margins, el diseño de apertura sobre la página entera y otro salto de página tras la valla de cierre. El cuerpo — normalmente la lista de los capítulos que agrupa la parte, o nada en absoluto — se compone con parts.bodyStyle:
:::part{number="I" title="Fundamentos"}
1. El farol y sus partes
2. Recortar la mecha
:::
# El farol y sus partes
Con los valores por defecto de los encabezados esto produce la secuencia clásica: página de parte en página derecha, página izquierda en blanco, capítulo en la siguiente página derecha. La página se notifica como role: 'part' para que los encabezados y pies puedan saltársela, y {partTitle} / {partNumber} resuelven a la parte actual en todas las páginas siguientes. Consulta Partes en la referencia de configuración.
Una valla no necesita una línea en blanco delante: una valla de apertura o de cierre pegada justo debajo de un párrafo, una lista o una cita termina ese bloque. Un contenedor que queda abierto al final del documento se cierra automáticamente ahí, y el sandbox emite un aviso Contenedor sin cerrar que señala la línea de apertura. Un ::: suelto sin ningún contenedor abierto se deja en el texto como un párrafo visible en lugar de descartarse en silencio.
#:::columns
Dentro de un :::callout, un grupo :::columns{count=2} … ::: compone los bloques entre sus vallas en count columnas de igual ancho (separadas por el columnGap del estilo): la secuencia se corta donde mejor se nivelan las columnas —entre bloques, o entre las líneas de un párrafo o elemento de lista, cuya cola sigue en la cabeza de la columna siguiente sin su viñeta— y la caja crece hasta la columna más alta. Los bloques posteriores al grupo recuperan el ancho completo. Fuera de un aviso las vallas se ignoran y los bloques fluyen como siempre. El atributo breaks fija dónde empieza cada columna en lugar de nivelarlas: :::columns{count=2 breaks="4"} abre la segunda columna en el cuarto bloque del grupo (una lista separada por comas para más columnas), sin cortar dentro de un párrafo —una columna de texto junto a una columna con una figura—.
:::callout{type="summary"}
:::columns{count=2}
- Cada elemento es un tipo de átomo.
- Los electrones viven en orbitales.
- Un enlace comparte o transfiere electrones.
:::
:::Una valla :::callout admite también label="…": el texto que un estilo con pestaña label imprime en la esquina superior de la caja (:::callout{type="recuadro" label="RECUADRO 1-1" title="La regla del octeto"}).
Un :::callout dentro de otro es una caja propia —una ficha de ejercicio con recuadros de respuesta, por ejemplo—. Toma su propio estilo (fondo, borde, radio, relleno, título, icono) al ancho interior completo de la caja exterior y se apila entre sus demás bloques; su span y su placement se ignoran, porque una caja anidada siempre fluye dentro de su madre. Cada valla de cierre cierra la caja abierta más interna:
:::callout{type="ficha"}
El enunciado del ejercicio.
:::callout{type="respuesta"}
Un recuadro blanco de respuesta con su propio borde y relleno.
:::
:::callout{type="respuesta"}
Un segundo recuadro de respuesta.
:::
:::Cuando la caja exterior se parte entre columnas o páginas (keepTogether: false, o más alta que una columna), una caja anidada pasa entera al fragmento siguiente salvo que su propio estilo permita partirla también; cada fragmento vuelve a dibujar los marcos que contiene, y una caja anidada que continúa pierde su título y su icono.
#:::pagebreak
La directiva por sí sola no fuerza paridad — solo afecta al siguiente bloque. Úsala para terminar un prólogo, forzar una dedicatoria en su propia página, o marcar el final de una sección. Cuando se quieren ambos efectos (salto de página y reinicio de numeración), compón :::pagebreak seguido de :::numbering — el cambio de numeración se aplica en la nueva página que acaba de crear :::pagebreak.
El capítulo anterior termina aquí.
:::pagebreak{parity="odd"}
# Un nuevo capítuloAtributo parity
El atributo parity acepta los mismos cinco valores que headings.levels[*].breakBefore.parity:
'any'— el valor por defecto: sin restricción de paridad; el salto simplemente abre una página nueva.'odd'/'even'— la nueva página abre en el lado solicitado del pliego; solo se inserta una página en blanco cuando la siguiente página natural cae en el lado equivocado.'always-odd'/'always-even'— garantizan al menos una página separadora en blanco obligatoria entre el contenido anterior y la nueva página, y después fuerzan la paridad. La separadora pertenece al capítulo anterior; cualquier relleno de paridad adicional pertenece a lo que viene después.
Pertenencia de las páginas en blanco
Los dos tipos de páginas en blanco que :::pagebreak (y breakBefore) pueden introducir se distinguen en el modelo VDTPage:
blankForParity: true— insertada para satisfacer una restricción de paridad. En el marcador{chapterTitle}esta página lleva el título del capítulo entrante, porque la página en blanco solo existe para empujar ese capítulo a la paridad correcta.blankForForce: true— la página separadora obligatoria que añade un modo'always-*'. Pertenece al capítulo anterior — es una pausa deliberada de cierre de capítulo, no un relleno de paridad para el capítulo siguiente.
Excepción al inicio del documento
Cuando :::pagebreak es la primera construcción del documento (o un encabezado con breakBefore lo dispara), la imposición de paridad se omite mientras la primera página siga vacía. El siguiente bloque aterriza en la página 1 tal cual, independientemente de la paridad solicitada — no se inserta una página en blanco inicial superflua.
#:::numbering
:::numbering es la forma de reiniciar el contador de páginas a mitad del documento. Ejemplo canónico de libro:
---
title: "Un libro con páginas preliminares"
---
# Prefacio
…
:::pagebreak{parity="odd"}
:::numbering{format="decimal" startAt=1}
# Capítulo 1Las páginas del prefacio se etiquetan i, ii, iii, …; el primer capítulo se abre en una página derecha etiquetada 1.
Los cambios solo de formato (sin startAt) mantienen el contador vivo — útil para, por ejemplo, pasar de lower-alpha a upper-alpha sin reiniciar.
#:::toc
:::toc imprime el índice de contenidos en el punto donde está. Se expande en bloques ordinarios — uno por encabezado de los niveles que enumera toc.levels (el nivel 1 por defecto) y uno por :::part — de modo que el índice fluye por columnas y páginas como cualquier otro texto, y un clic sobre una entrada lleva a la línea de la directiva. Cada entrada muestra el número del encabezado (el resultado de su numberingTemplate o, en su defecto, el ordinal de capítulo), su título, una línea de puntos y la etiqueta de la página en que empieza y, si toc.subtitle está activado, una segunda línea con un atributo del encabezado, como los autores del capítulo ({author="…"}). Cada parte recibe una fila propia, diseñada por toc.parts.design y coloreada con la palette de la parte. Los encabezados cuyo estilo declara numbered: false aparecen sin número; {toc="false"} en un encabezado lo deja fuera (el propio encabezado del índice, normalmente).
# Índice {style="preliminar" toc="false"}
:::tocLos números de página son los que el documento imprime realmente. Un documento compuesto por sí solo se vuelve a componer con las etiquetas de la pasada anterior hasta que dejan de moverse — con la numeración reiniciada tras los preliminares (la receta de :::numbering de más arriba) basta una pasada adicional. Un capítulo compuesto por separado (las vistas previas del sandbox) recibe en su lugar el esquema del libro entero desde su anfitrión. Ver Índice de contenidos en la referencia de configuración.
#Saltos de línea en los títulos
Escribe \\ dentro de un encabezado (o dentro del atributo title de una parte) para forzar un salto de línea allí donde el título se muestra como tal: el encabezado en columna sigue fluyendo y muestra un espacio, mientras que el {titleText} de un diseño de apertura salta de línea en ese punto. Las cabeceras de página, {chapterTitle}, {partTitle} y el índice del PDF siempre componen el título en una sola línea.
# Concepto de salud y enfermedad. \\ Salud comunitaria {author="I. Zango Martín"}#Formato en línea
El marcado en línea se reconoce dentro de cualquier bloque de texto (encabezados, párrafos, citas, elementos de lista).
| Marcado | Sintaxis | Notas |
|---|---|---|
| Negrita | negrita o negrita | Se renderiza con bodyText.boldFontWeight. Un bodyText.boldColor opcional anula el color general del cuerpo para los tramos en negrita. |
| Cursiva | cursiva o cursiva | Se renderiza con la variante cursiva de la fuente actual. Un bodyText.italicColor opcional anula el color general del cuerpo para los tramos en cursiva. |
| Negrita cursiva | ambas o ambas | Se combinan ambos estilos. |
| Superíndice | ^texto^ | Se compone al 58 % del cuerpo y elevado un tercio de él — un exponente (10^-8^), la carga de un ion (Na^+^). El texto marcado empieza y termina en un carácter que no es un espacio; un acento circunflejo suelto en la prosa se mantiene literal. |
| Subíndice | ~texto~ | Mismo tamaño, bajado un tercio del cuerpo — un índice químico (H~2~O, pK~a~). Se combina con negrita y cursiva (H~2~O). |
| Código en línea | | Las comillas invertidas se eliminan; el tramo se renderiza como texto plano. El estilo diferenciado para código está en el roadmap. |
| Escape | *, _, ^, ~, ` | Una barra invertida compone el propio carácter marcador — el asterisco de una nota de tabla (* pOH = −log [OH^−^]), un acento circunflejo literal — en vez de abrir un tramo. Vale en el cuerpo y en pies, celdas y notas. |
| Enlace | texto | El texto visible permanece en el flujo; la URL se descarta en el renderizador actual. El soporte de enlaces está en el roadmap. |
| Imagen | | El markdown de imagen en línea se elimina del texto. Las imágenes deben declararse en PostextContent.resources para que el motor las coloque según las reglas de resourcePlacement. |
| Chip | :chip[texto] | Un fragmento de texto en una caja que pasa de línea como una unidad: un banco de palabras, una tecla, una etiqueta. Lo estilan los chipStyles; ver Chips en línea. |
| Matemáticas en línea | $…$ | Una fórmula LaTeX que fluye con el texto que la rodea, p. ej. $e^+1=0$. Se compone con MathJax y se renderiza como trazos vectoriales en todos los backends. Usa \$ para un signo de dólar literal. El escalado, la forma display ($$ … $$) y el manejo de errores se describen en Fórmulas matemáticas. |
#Chips en línea
:chip[texto] compone texto en una caja —un «chip» redondeado y tintado— que fluye con la línea: las palabras de un banco de palabras o de un ejercicio de clasificar, teclas, etiquetas. :chip[texto]{style="tecla"} elige un estilo con nombre de chipStyles (ver la referencia de configuración); sin style, o con un id que ningún estilo declara, el chip toma el primer estilo (un estilo chip integrado cuando la configuración no tiene ninguno; el sandbox avisa de un id desconocido).
Clasifica: :chip[pila] :chip[cable] :chip[interruptor] :chip[bombilla]
Pulsa :chip[Ctrl]{style="tecla"} + :chip[C]{style="tecla"} para copiar.- Una unidad. Un chip nunca se parte ni se divide con guion por dentro; la línea se corta entre chips, en los espacios que los rodean. Un chip más ancho que toda la línea la desborda en lugar de partirse.
- Ancho. Su avance es el texto más el relleno horizontal y el contorno a ambos lados. La justificación solo estira los espacios entre palabras, nunca el interior de un chip. El
gapdel estilo es el espacio mínimo entre la caja y la palabra o el chip vecino a través de un espacio: un espacio más estrecho se completa (no en el borde de la línea, ni junto a la puntuación pegada, como en:chip[a],). - Alto. La caja es una banda alrededor de la línea base, 0,8 em por encima y 0,25 em por debajo al cuerpo del chip, que crece con el relleno vertical y el contorno. El relleno vertical se pinta fuera de la caja de línea y nunca cambia el interlineado, así que la retícula se mantiene; una caja más alta que el interlineado toca los chips de la línea siguiente, y el sandbox lo señala («Los chips tocan la línea siguiente») para reducir el relleno, el contorno o el cuerpo.
- Texto. El texto del chip admite sus propias marcas (
:chip[**negrita** palabra],:chip[x^2^]) y el énfasis que lo rodea (**:chip[a]**); el estilo puede fijar su familia, cuerpo, color, negrita y cursiva. Escribe\]para un corchete literal dentro. Las referencias, las muestras de color y las fórmulas dentro de un chip quedan como texto literal. - Dónde. Párrafos, elementos de lista, citas, recuadros, celdas de tabla, leyendas y notas. En los encabezados
:chip[…]queda como texto literal. - Salida. Canvas, HTML y PDF pintan la caja y componen las palabras como texto real: seleccionable en el HTML, extraíble y en orden de lectura en el PDF (en un PDF etiquetado la caja es un artefacto de maquetación y las palabras pertenecen al párrafo).
#Fórmulas matemáticas
El soporte matemático forma parte del formato de documento de primer nivel. Postext parsea $…$ para fórmulas en línea y $$…$$ para fórmulas en display (bloque), y las renderiza con MathJax en modo SVG. Los mismos trazos vectoriales alimentan los tres backends, de modo que la vista previa en canvas, la exportación HTML y la salida PDF coinciden píxel a píxel — y el PDF se mantiene completamente vectorial sea cual sea el nivel de zoom.
- En línea:
$…$. Se reconoce dentro de cualquier bloque de texto (párrafo, encabezado, cita, elemento de lista). Aporta una única caja atómica no partible a la línea; Knuth-Plass la trata como una palabra que no puede separarse. Si la altura natural de la fórmula rompería la caja de línea, se escala uniformemente para conservar la rejilla de línea base — las expresiones muy altas deben vivir en modo display. - Display:
$$…$$. Bien en una línea propia ($$\int_0^1 x^2\,dx$$) o vallada en varias líneas con marcadores$$en sus propias líneas. Se renderiza centrada en la columna y ajustada a la rejilla de línea base con márgenes superior e inferior configurables (math.marginTop,math.marginBottom) — exactamente la misma corrección que usan los encabezados, de modo que el párrafo que sigue vuelve a caer sobre la rejilla. - Escapado:
\$es un signo de dólar literal. Un delimitador$o$$sin cerrar produce una entradaunclosedMathen el panel de avisos con un ancla de fuente al que puedes saltar con un clic. - Errores: el código TeX que MathJax rechaza (macros no definidos, errores de sintaxis) se refleja como un aviso
invalidMath. La fórmula se sustituye por una pequeña caja roja para que la geometría del layout siga siendo válida. - Configuración: la sección
mathde la configuración exponeenabled,fontSizeScale(relativo al tamaño del cuerpo),color(hereda del cuerpo si se omite) y los márgenes del display.
La identidad de Euler $e^{i\pi}+1=0$ enlaza las cinco constantes fundamentales.
$$
\int_0^{\infty} e^{-x^2}\,dx = \frac{\sqrt{\pi}}{2}
$$#Recursos
Las imágenes, los SVG y las tablas no se escriben en línea. Se declaran una sola vez como recursos (gestionados en el panel de Recursos del sandbox, que ofrece subida de imágenes y SVG, un editor interactivo de tablas y edición de pies y de colocación) y luego se conectan al texto por id. Basta con referenciar un recurso para incorporarlo: lo mencionas una vez con :ref{id="…"} y el motor hace flotar la figura o tabla al primer hueco libre tras esa referencia — el final de la columna donde la mencionas, el principio de la siguiente, una banda de la página siguiente —, igual que haría un tipógrafo de imprenta. No lo colocas una segunda vez.
Ambas formas de abajo son sintaxis completamente nueva que no choca con CommonMark, por lo que un documento que las use sigue leyéndose como texto plano en cualquier otro visor de markdown.
#Referencia en línea (la forma principal)
Refiérete a un recurso desde el texto con :ref{id="…"}. La primera referencia incorpora el recurso (para que se coloque en la página) y renderiza su número calculado, precedido por defecto por la etiqueta corta del tipo:
Como se ve en :ref{id="lighthouse-diagram"}, la sala de la linterna queda sobre la galería.se renderiza como: Como se ve en Fig. 1.7, la sala de la linterna queda sobre la galería. — y el propio diagrama flota al primer hueco libre tras la frase (el final de esta columna, el principio de la siguiente, o una banda de la página siguiente), mientras esta frase y el texto posterior siguen fluyendo sin interrupción.
El texto nunca se corta en el punto de la referencia. Dónde aterriza el recurso —el primer hueco libre, o solo un hueco superior o inferior; en una sola columna o a todo el ancho— lo decide su colocación (ver Colocación más abajo), junto con el lugar donde lo mencionas: la búsqueda empieza justo después de la referencia.
#Inserción en bloque (opcional, colocación en línea explícita)
A veces quieres que un recurso quede en un punto exacto del flujo en lugar de flotar. Renuncia al flotado dando al recurso placement.position: "here" e insertándolo con ::resource{id="…"} en su propia línea:
Aquí está el plano que comentamos.
::resource{id="lighthouse-diagram"}
Las dependencias del farero ocupan el ala este.Para un recurso que flota, la directiva ::resource es innecesaria —el :ref ya lo colocó, y un ::resource redundante para el mismo id se trata como otra referencia, no como una segunda copia. Un ::resource solo renderiza el recurso en línea cuando su colocación resuelta es "here". Un recurso en línea conserva una línea de espacio sobre él (el hueco de los flotantes), como lo haría un flotante, salvo que el bloque anterior pida más.
El id debe coincidir con un recurso definido en el panel de Recursos. El motor renderiza el recurso (imagen, SVG o tabla) con su pie dibujado debajo a modo de pie de figura/tabla. El texto del pie se compone a partir del captionPrefix del tipo de recurso, el número calculado y el pie propio del recurso — p. ej. Figura 1.7. El plano original del faro. Su tipografía se controla desde Configuración › Estilo de pies: la etiqueta y la descripción comparten tipografía y tamaño, mientras que la etiqueta conserva sus propios ajustes de negrita/cursiva/color; la separación sobre el pie es por defecto 0.75em y la alineación, a la izquierda. El pie puede situarse en cambio encima del recurso (captionStyle.position: 'above', de forma global o por tipo de recurso), opcionalmente sobre una barra de color. Un recurso puede llevar además una note — una línea breve de fuente o créditos, con el mismo formato en línea y las mismas marcas :ref que el pie — compuesta en un tamaño menor bajo el recurso (bajo el pie cuando este va debajo, bajo el cuerpo cuando va encima) y estilizada mediante captionStyle.note.
Los recursos de tabla dibujan su propia rejilla, con estilo definido en Configuración › Estilo de tablas: las celdas de cuerpo y de cabecera tienen tipografía totalmente independiente, el fondo de la cabecera es por defecto #f0f0f0, los bordes 0.75pt y el cellPadding 0.375em — cualquier campo sin definir hereda del texto del cuerpo. Los anchos de columna forman parte de la propia tabla: TableModel.columnWidths guarda un peso relativo por columna ([2, 1, 1] da a la primera columna la mitad del ancho); si no se indica, las columnas se reparten el ancho a partes iguales. Una tabla también puede componerse con una variante con nombre: table.styleId elige uno de los tableStyles del documento (véase Configuración › Estilos de tabla con nombre), cuyos campos sin definir heredan de tableStyle; un id desconocido o ausente conserva tableStyle.
Una celda coloca su contenido con TableCell.align (left, center, right; los elementos de lista siguen alineados a la izquierda) y TableCell.verticalAlign (top, por defecto, middle o bottom). La alineación vertical mueve todo el contenido de la celda —la imagen y el texto que va debajo, como una sola unidad— dentro de una celda más alta que él: una fila estirada por una vecina más larga, o las filas que abarca un rowSpan. Se aplica en todas las salidas (canvas, HTML, PDF), en las tablas giradas y en cada tramo de una tabla partida entre páginas. Ambas se fijan por celda desde los botones de alineación de la barra del editor de tablas del sandbox.
Una celda de tabla también puede contener una imagen. TableCell.image nombra un recurso de imagen o SVG por su id ({ "resourceId": "fig-brazo", "width": 0.7 }): la imagen se dibuja dentro de la celda — nunca se numera, flota ni lleva pie — ajustada al ancho interior de la celda (o a la fracción que indique width, por defecto 1) conservando su proporción, alineada como el texto de la celda, y el texto de la celda, si lo hay, va debajo. La fila crece para alojarla. En el editor de tablas del sandbox, el botón de imagen de la barra elige el recurso para la celda activa y un campo de ancho fija la fracción. Un id que no corresponda a ningún recurso de imagen deja la celda solo con texto.
Una celda puede llevar su propio relleno. TableCell.background es un valor de color ({ "hex": "#c1dfd6", "model": "hex" }, opcionalmente vinculado a una entrada de la paleta del documento con paletteId) que se pinta en lugar del fondo de cabecera o de cuerpo del estilo — así sombrea sus celdas en verde, rojo y amarillo una matriz de compatibilidades. En el editor de tablas del sandbox se fija con el control de relleno de la barra. Para dar la clave de esos rellenos, la leyenda, la nota y cualquier bloque de texto admiten una muestra de color en línea: :swatch{color="#c1dfd6"} (un hexadecimal, o el id de una entrada de la paleta — :swatch{color="table-compatible"}) compone un pequeño cuadrado sobre la línea base, de tres cuartos del cuerpo, relleno con el color y perfilado con el color del texto, de modo que una nota puede decir :swatch{color="ok"}: compatibles; :swatch{color="no"}: incompatibles. Un color que no resuelve a nada dibuja solo el perfil.
Los recursos SVG pueden además recolorearse para impresión a una sola tinta plana mediante diagramStyle.singleInk (por defecto false). Al activarlo, cada color del diagrama SVG se reasigna a un matiz de diagramStyle.inkColor según su luminancia (por defecto el color principal de la paleta, #295AA3) — el blanco se asigna al papel y el negro a la tinta plena — de modo que las figuras se reproducen con fidelidad cuando el documento se imprime con una única tinta plana. Ver Configuración › Estilo de diagramas.
Una inserción mal formada (sin id, con id vacío o con atributos de más) no se promueve a bloque de recurso; recae en el parseo de párrafo normal y sigue siendo visible en la salida, y el sandbox muestra un aviso.
Las referencias en línea se reconocen dentro de cualquier bloque de texto — párrafos, encabezados, citas y elementos de lista — y pueden convivir con negrita, cursiva, código en línea y matemáticas en línea.
Opciones de la referencia
La directiva :ref admite tres atributos opcionales, en cualquier orden. style selecciona cómo se renderiza la etiqueta calculada: style="number" imprime el número a secas (1.7), style="full" imprime el nombre completo del tipo seguido del número (Figura 1.7), y cuando style se omite se usa la etiqueta corta (shortLabel) del tipo seguida del número (Fig. 1.7). case cambia las mayúsculas y minúsculas solo de la parte de la etiqueta — lower, upper o capitalize — sin tocar el número. text es una anulación literal que sustituye cualquier etiqueta calculada y tiene prioridad tanto sobre style como sobre case. Las opciones de renderizado, lado a lado:
| Sintaxis | Renderiza | Notas |
|---|---|---|
:ref{id="…"} | Fig. 1.7 | Estilo por defecto: la etiqueta corta (shortLabel) del tipo seguida del número, unidas con un espacio irrompible para que nunca se separen al final de la línea. |
:ref{id="…" style="number"} | 1.7 | El número calculado a secas, sin etiqueta. |
:ref{id="…" style="full"} | Figura 1.7 | El nombre completo (name) del tipo seguido del número. Útil al inicio de una frase o donde la abreviatura se lea mal. |
:ref{id="…" case="lower"} | fig. 1.7 | Cambia la caja solo de la etiqueta: lower (fig. 1.7), upper (FIG. 1.7) o capitalize (primera letra en mayúscula). Se combina con style="full" (figura 1.7); el número nunca se toca, y un valor no reconocido se ignora. |
:ref{id="…" text="ver el plano"} | ver el plano | Una anulación explícita. El texto dado se usa literalmente en lugar de cualquier etiqueta calculada — útil para enlaces de prosa como "como vimos antes". Cuando está presente, text tiene prioridad sobre style y case. |
Si un :ref (o un ::resource) nombra un id sin recurso correspondiente, la etiqueta recae en ? y el sandbox emite un aviso de recurso desconocido.
#Numeración por primera referencia
El número de un recurso se asigna la primera vez que se menciona en orden de lectura — sea esa primera mención una inserción en bloque ::resource o una referencia en línea :ref. A partir de ahí, toda referencia al mismo id imprime ese mismo número.
Esto significa que los números siguen el orden en que el lector los encuentra, no el orden en que se crearon los recursos en el panel:
- Si haces
:refde una figura en la introducción y solo la insertas (::resource) dos páginas después, igualmente toma el número de la introducción — la referencia llegó primero. - Insertar una nueva referencia antes en el documento renumera automáticamente todo lo que viene después. No hay numeración manual que mantener sincronizada.
La numeración es por tipo de recurso y respeta el ámbito de reinicio y el formato de contador de cada tipo — ver Configuración › Tipos de recurso para los tokens de plantilla ({h1}, {n}), resetOn y counterFormat.
#Colocación
Cada recurso tiene una colocación que decide dónde aterriza su flotante, resuelta por recurso (su propio placement), luego el defaultPlacement de su tipo, y por último el valor por defecto integrado auto / column:
| Campo | Valores | Significado |
|---|---|---|
position | "auto" · "top" · "bottom" · "here" | "auto" (el valor por defecto) toma el primer hueco libre tras la referencia, arriba o abajo; "top" / "bottom" solo aceptan huecos de ese tipo; "here" renuncia al flotado e inserta el recurso en línea en la directiva ::resource. |
width, align | 0 < width < 1; "left" · "center" · "right" | Un recurso más estrecho que su hueco: width es la fracción del ancho de la columna (o de la página) que ocupa y align dónde se sitúa en el hueco (una tabla pequeña centrada en su columna; una banda a ancho de página cuya imagen abarca una sola columna). Vale tanto para flotantes como para inserciones ::resource en línea. |
captionSide | true · false | En una maquetación de columna y media cuya columna lateral está reservada a flotantes (layout.sideColumnRole: 'floats'), un flotante "column" con captionSide mantiene su cuerpo en la columna principal y compone su pie (y su nota) en la columna lateral, a la altura del borde superior de la figura —o del inferior en un flotante de pie—; la columna lateral cede esa banda. Una página sin esa columna deja el pie bajo la figura. |
span | "column" · "page" | Ocupa una sola columna, o rompe el flujo de columnas y abarca todo el ancho del contenido cruzando todas las columnas. En una maquetación de una sola columna ambas opciones son idénticas. |
rotate | "ccw" · "cw" | Compone el recurso girado un cuarto de vuelta — una tabla apaisada en un libro vertical. "ccw" lo gira en sentido antihorario, con la cabecera hacia el borde izquierdo de la página (el lector gira el libro en sentido horario), la convención habitual; "cw" en el otro sentido. Un recurso girado es siempre un flotante a todo el ancho en una página propia: se compone a lo largo de la altura del área de contenido, queda pegado al lomo cuando los márgenes son simétricos (a la izquierda si no) y una tabla demasiado ancha para una página se corta entre filas y continúa, girada, en las páginas siguientes con la cabecera repetida, exactamente como una tabla vertical más alta que la página. Una figura girada se escala para caber en la página. Se ignora en un embebido en línea ("here"). |
Un flotante ocupa el primer hueco libre tras su primera referencia, en orden de lectura: el final de la columna donde está la referencia, después el principio y el final de la siguiente columna vacía de la misma página, y después las bandas de la siguiente página que abra el flujo (un flotante a ancho de página ocupa el pie de la página cuando todas las columnas aún tienen sitio para él; si no, una banda de la página siguiente). Nunca se reduce, y nunca aterriza antes de su referencia. Los flotantes de una misma secuencia de numeración aparecen en orden de referencia: una figura que no cabe en ninguna parte de una página retiene a las figuras que vienen detrás (una tabla en espera no retiene a una figura, ni al revés), así que la figura 12 nunca aparece antes que la figura 11. Una tabla que tendría que esperar se corta en su lugar: si recibe la cabecera de una columna vacía, toma las filas que caben y continúa en el siguiente hueco — la columna de al lado o la página siguiente — con sus filas de cabecera repetidas (véase tableStyle.overflow).
Los flotantes nunca escapan de su capítulo: en una apertura de capítulo (un nivel de encabezado con breakBefore o span: 'page'), en un :::part, en un estilo de aviso con floatBarrier: true (la caja de «puntos clave» que cierra el capítulo) y al final del documento, todo flotante pendiente se coloca primero — en los huecos libres de la página, o en páginas abiertas por delante del límite. Una figura o tabla que pidió la cabeza de una página puede ocupar entonces el pie de la página de cierre del capítulo, bajo sus columnas equilibradas, en lugar de una página propia. Un :::pagebreak simplemente envía los flotantes pendientes a la página que le sigue.
Las bandas de flotantes se corrigen contra la rejilla base para que el texto circundante mantenga el ritmo vertical de toda la página. Una banda superior amplía su margen inferior hasta la siguiente línea de la rejilla, de modo que el texto bajo el flotante queda alineado con las columnas vecinas y con la página enfrentada. Un flotante inferior se ancla de forma que la última línea de su leyenda comparta línea base con la última línea de texto de las demás columnas (el contenido sin leyenda alinea su borde inferior con la última celda de la rejilla) — así, las páginas llenas terminan a la misma altura entre columnas y entre páginas enfrentadas.
Los tres backends — la previsualización en canvas, el visor HTML y la salida PDF — renderizan recursos. En el backend HTML, las imágenes viven fuera del documento, así que el anfitrión las suministra mediante la opción de resolución resourceImageUrl(fileId); cuando falta el resolutor (o no devuelve nada para un fichero), el recurso se renderiza como una caja neutra de relleno para que la maquetación se mantenga estable.
#Qué NO se soporta
Postext no reconoce las siguientes características de CommonMark. O se tratan como texto plano (y por tanto aparecerán literalmente en la salida) o se descartan silenciosamente:
- Encabezados tipo setext — la forma con subrayado
===/---. Usa encabezados ATX (#). - Bloques de código con cercas o con indentación — triple acento grave e indentación de 4 espacios. El código en línea funciona; el código multilínea se renderizará línea a línea como párrafos.
- HTML en crudo — las etiquetas
<tag>no se interpretan. Tampoco se admiten etiquetas estilo MDX; la fuente Postext es markdown puro. - Líneas horizontales —
---,***,___. - Tablas — las tablas con pipes no se parsean. Las tablas se modelan como recursos estructurados en
PostextContent.resources. - Enlaces por referencia —
[texto][id]con su bloque de definición. - Autolinks —
<https://example.com>. - Tachado —
~~texto~~. El renderizador de tachado está reservado actualmente para tareas completadas. - Marcas de nota al pie en markdown —
[^1]. Las notas al pie viajan enPostextContent.notesy se referencian por id, no con sintaxis en línea.
Esta lista se irá reduciendo con el tiempo. Mientras tanto, cualquier cosa que no aparezca explícitamente arriba debe considerarse texto literal.
#Normas de redacción
Unas pocas normas marcan la diferencia entre un documento que se parsea limpiamente y uno que te sorprende:
- Deja una línea en blanco entre bloques. Dos párrafos separados por una línea en blanco son dos párrafos. Dos párrafos en líneas consecutivas se convierten en uno — cada línea se añade al párrafo anterior.
- Anida las listas con exactamente dos espacios por nivel. Un único espacio se parsea como un elemento de nivel 1. Tres o cuatro espacios se redondean hacia abajo al nivel 2 (el motor aplica
floor(leading / 2) + 1, limitado a profundidad 5). Las tabulaciones no se reconocen — conviértelas a espacios. - No indentes el primer elemento de la lista. Los elementos de nivel 1 empiezan en la columna 0. Cualquier espacio en blanco inicial aumenta la profundidad de forma implícita.
- Las marcas de tarea van entre corchetes con un único espacio.
[ ],[x],[X]— sin variaciones.[*]o[-]no son marcas de tarea; se renderizan como texto literal. - No se admiten citas dentro de listas. Empieza la cita en la columna 0, fuera de la lista.
- Las imágenes y tablas viven en
resources. Elen línea se elimina precisamente porque rompe la colocación consciente de columnas. Declara cada imagen como recurso y refiérela por id — el motor decide si flota, rompe la columna o se desplaza al inicio de la página siguiente. - Escapa los signos de dólar con
\$cuando no sean matemáticas. Postext interpreta$…$como LaTeX en línea, así que un$suelto en prosa iniciaría una fórmula. Precios, prompts de shell y cualquier otro dólar literal deben escribirse como\$.
#Ejemplo completo
Un documento breve que ejercita todas las construcciones admitidas:
---
title: El oficio del tipógrafo
author: Anónimo
---
# Apertura
Un buen libro se lee solo. El **lector** no debería percibir nunca el
trabajo del tipógrafo — solo la voz del autor.
## Qué hace legible al texto
Tres propiedades son determinantes:
1. El ancho de carro — entre 40 y 75 caracteres por línea.
2. La interlínea — entre 1,3 y 1,5 veces el tamaño de fuente.
a. Más ajustada en anchos cortos.
b. Más holgada en anchos largos.
3. El contraste entre cuerpo y encabezados.
Entre los fallos habituales están:
- Líneas que atraviesan toda la página.
- Encabezados que flotan sin un párrafo detrás.
- Huérfanas y viudas en los límites de columna.
> La tipografía es el oficio de dotar al lenguaje humano de una forma
> visual duradera.
> — Robert Bringhurst
### Lista de comprobación
- [x] Ancho de columna por debajo de 75 caracteres
- [x] Interlínea ajustada a 1,5
- [ ] Paso de huérfanas y viudas
- [ ] Corrección final
### Una nota sobre fórmulas
Las matemáticas en línea como $a^2 + b^2 = c^2$ fluyen con el texto que las
rodea, y las de display quedan centradas sobre la rejilla de línea base:
$$
\int_0^1 x^2\,dx = \tfrac{1}{3}
$$Ese mismo documento, procesado por el motor, produce un VDTDocument estructurado cuyas páginas contienen cada uno de estos bloques como entradas tipadas — consulta la página de Arquitectura para ver cómo los bloques se convierten en geometría.