Saltar al contenido principal

Formato del documento

Actualizado: 2026-04-23|9 min|enes

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. Los bloques siempre terminan al encontrar una línea en blanco o el inicio de otro bloque.

Construcciones de bloque de un vistazoLos siete tipos de bloque que reconoce Postext: encabezado, párrafo, cita, lista no ordenada, lista ordenada, lista de tareas y fórmula en display, cada uno con su sintaxis markdown.Encabezado# TítuloPárrafoLíneas de texto planoCita> texto citadoLista no ordenada- itemLista ordenada1. itemLista de tareas- [x] hechoFórmula display$$ \int f(x) $$
Cada tipo de bloque y su entrada en markdown.
ConstrucciónSintaxisNotas
Encabezado# Título###### H6De uno a seis caracteres # seguidos de un espacio y del texto. Los niveles 1–6 se corresponden directamente con headings.levels.
PárrafoTexto plano en una o más líneasLas 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 citadoCada 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, + itemSe acepta cualquiera de las tres viñetas. El anidamiento usa exactamente dos espacios por nivel, hasta una profundidad máxima de 5.
Lista ordenada1. item, 2) itemDí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] hechoUn 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, bien en una única línea ($$\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, ajustada a la rejilla de línea base como una cabecera, y se mantiene vectorial en el PDF.

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.

#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.

SintaxisEfecto
:::pagebreakFuerza 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.

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 y numbering — cualquier otra línea :::nombre se parsea como párrafo y genera un aviso Directiva desconocida en el sandbox.

#:::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ítulo

Atributo parity

El atributo parity acepta los mismos cinco valores que headings.levels[*].breakBefore.parity:

  • '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 1

Las 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.

Profundidad de anidamiento de listasLas listas se anidan usando exactamente dos espacios por nivel, hasta una profundidad máxima de cinco. Cada nivel sangra más y puede usar un estilo de viñeta diferente.Nivel 1Nivel 2 — dos espaciosNivel 3 — cuatro espaciosExactamente dos espacios por nivel. Profundidad máxima: 5.
Dos espacios por nivel. Profundidad máxima: cinco.

#Formato en línea

El marcado en línea se reconoce dentro de cualquier bloque de texto (encabezados, párrafos, citas, elementos de lista).

Formato en línea de un vistazoComparativa markdown-a-renderizado para negrita, cursiva, negrita cursiva, código en línea y enlace.MarkdownRendered**negrita**negrita*cursiva*cursiva***ambas***ambas`código`código[enlace](https://…)enlace
Markdown a la izquierda, resultado renderizado a la derecha.
MarcadoSintaxisNotas
Negritanegrita o negritaSe renderiza con bodyText.boldFontWeight. Un bodyText.boldColor opcional anula el color general del cuerpo para los tramos en negrita.
Cursivacursiva o cursivaSe 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 cursivaambas o ambasSe combinan ambos estilos.
Código en líneacódigoLas comillas invertidas se eliminan; el tramo se renderiza como texto plano. El estilo diferenciado para código está en el roadmap.
EnlacetextoEl texto visible permanece en el flujo; la URL se descarta en el renderizador actual. El soporte de enlaces está en el roadmap.
ImagenaltEl 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.
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. Las fórmulas cuya ascendente o descendente se salga de la caja de línea se escalan hacia abajo para no romper la rejilla vertical. Usa \$ para un signo de dólar literal.

#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 entrada unclosedMath en 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 math de la configuración expone enabled, 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) 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 a una banda en la parte superior o inferior de la página, cerca de esa referencia, 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 a la parte superior (o inferior) de la página, 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 —arriba o abajo de la página, en una sola columna o a todo el ancho— lo decide su colocación (ver Colocación más abajo), no el lugar donde lo mencionas.

#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".

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.

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 dos atributos opcionales:

SintaxisRenderizaNotas
:ref{id="…"}Fig. 1.7Estilo 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.7El número calculado a secas, sin etiqueta.
:ref{id="…" style="full"}Figura 1.7El nombre completo (name) del tipo seguido del número. Útil al inicio de una frase o donde la abreviatura se lea mal.
:ref{id="…" text="ver el plano"}ver el planoUna 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.

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 :ref de 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 top / column:

CampoValoresSignificado
position"top" · "bottom" · "here"Flota a la banda superior o inferior de la página, o —con "here"— renuncia al flotado e inserta el recurso en línea en la directiva ::resource.
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.

Un flotante se coloca en la siguiente página que se abre tras su primera referencia, cerca de ella en orden de lectura. Si no cabe en la banda de esa página, se difiere a la página siguiente (nunca se reduce ni se parte). Los flotantes mantienen el orden de primera referencia, de modo que las figuras no se reordenan respecto a donde se mencionan.

Nota: el visor HTML aún no renderiza recursos; la previsualización en canvas y el backend PDF sí.

#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 en PostextContent.notes y 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. El ![alt](src) en 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.