Una hoja de papel por día. Se imprime, se dobla, se guarda en el bolsillo, y funciona sin batería ni señal.
Ireneo Funes recordaba cada hoja de cada árbol de cada monte, y por eso era incapaz de pensar. Tu pliego recuerda un solo día, en ocho paneles.
pnpm install
pnpm --filter @funes/web dev # la app, en localhost:5173
pnpm taller # emite los PDFs de ejemplo en salida/
pnpm test # 61 tests
pnpm check # tsc --noEmit- Imprimí
salida/prueba-a4-octavo.pdfen A4 al 100%, con "ajustar a página" desactivado. - Doblalo juntando las formas iguales del margen: círculos con círculos, cuadrados con cuadrados.
- En cada panel hay una flecha. Doblada la hoja, todas tienen que apuntar para arriba.
- Si alguna salió al revés, el modelo está mal y se corrige en un solo lugar:
deriveRotation()enpackages/core/src/sheet.ts.
Doblar haciendo coincidir marcas es invariante a escala. Si el driver achica todo un 3 %, las marcas se achican junto con el contenido: juntás marca con marca y el pliegue cae exactamente donde tiene que caer. El objeto sale 3 % más chico y no se nota.
Lo que sí importa, y es donde estaba el problema de verdad, es la zona muerta: una láser doméstica no imprime los primeros 4 a 6 mm de la hoja, y algunas de inyección se comen hasta 15 mm en el borde de salida. Las marcas de plegado estaban a 1,6 mm del borde, o sea que la mayoría de las impresoras no las imprimía. Ahora van a 7 mm, corridas hacia adentro de su propia mitad — y como la sangría es simétrica, se siguen encontrando al doblar. Hay un test que lo verifica.
El PDF además pide /PrintScaling /None, que Acrobat respeta y el visor de
Chrome no siempre.
calibracion.pdf sigue existiendo, pero pasó a ser opcional. Sirve para dos
cosas puntuales: si querés que el objeto salga del tamaño exacto, o si tu
impresora escala distinto en cada eje — ahí sí el plegado se desalinea,
porque la invariancia solo vale para escala uniforme.
Ese paso 5 es el punto de toda la fase. En Paper Phone la rotación era un
val isRotated hardcodeado dentro de cada módulo, y quedó inconsistente:
paneles 1 y 2 girados, 3 y 4 no, los cuatro en la misma fila. Acá se deriva
del plegado, así que o está bien para todos o está mal para todos.
packages/core SheetSpec · layoutText() · DrawOp puro, sin I/O
packages/panels los paneles: (datos, caja) => DrawOp[]
packages/sources clima, mapa, cambio, .ics, .vcf sin API keys
packages/render DrawOp[] → PDF · Canvas2D pdf-lib + fontkit
apps/web la app Vite, sin framework
apps/taller las hojas de prueba y los ejemplos CLI
El núcleo no sabe medir texto: se lo inyecta un Measurer. Por eso se testea
con un medidor falso, sin abrir un PDF, y por eso en la Fase 2 se puede
cambiar a Noto subseteado —para japonés y árabe— sin tocar nada más.
El pliego es un dato. SheetSpec declara papel, márgenes, calles,
pliegues y grilla. A4_OCTAVO, LETTER_OCTAVO, TARJETA y A5_GRANDE son
cuatro constantes, no cuatro implementaciones. En el original la posición de
cada panel estaba escrita a mano en un when(index) de ocho ramas.
La rotación se deriva, no se declara. deriveRotation() la calcula desde
los pliegues. La rotación pertenece a la ranura, no al contenido —por eso
ahora los paneles se pueden reordenar sin romper el objeto físico.
Se mide antes de dibujar. layoutText() devuelve entra: boolean y
sobrante: string. El original cortaba títulos con substring sobre un
breakText de una sola línea, porque no tenía forma de preguntar si entraba.
validate(spec) corre antes de emitir cualquier PDF y ataja la clase de error
que el original tenía suelta: pliegues que caen adentro de un panel en vez de
en la calle, ranuras más chicas que un sello, márgenes que no dejan área útil.
Hay un test de regresión que documenta el bug original con sus constantes textuales: la aritmética vertical de Paper Phone sumaba 598 pt contra los 595,28 de un A4 —casi un milímetro fuera de la hoja— y no se notaba solo porque el canvas recortaba.
| Fase | Qué | |
|---|---|---|
| 0 | La geometría, probada en papel | hecho |
| 1 | Los paneles, previa en Canvas2D, imprimir | hecho |
| 2 | Tipografía embebida con eje de ancho | hecho |
| 3 | Cargar tus datos: lugar, agenda, teléfonos, tareas | hecho |
| 4 | Clima, mapa y cotización en vivo, sin API keys | hecho |
| — | Dos escrituras: Noto subseteado, BiDi, kinsoku | falta |
| — | El ritual: diseños guardados, PWA offline | falta |
| — | Modo acompañante, crucigrama, formato de panel abierto | falta |
Paper Phone lo hizo Google Special Projects con Google Creative Lab en 2019: una app Android que imprimía en una hoja A4 lo que ibas a necesitar ese día, para que pudieras dejar el teléfono en casa. La idea entera es de ellos.
Funes no es un port. Está escrito de cero, en otro lenguaje y con otra arquitectura.
El nombre viene de otro lado: de «Funes el memorioso», de Borges.
- Archivo, de Omnibus-Type
(Buenos Aires). Cinco instancias estáticas, embebidas subseteadas en cada
PDF. SIL Open Font License 1.1 — el texto va en
packages/render/src/fuentes/OFL.txt. - OpenStreetMap para el mapa, vía la Overpass API. Licencia ODbL: la atribución se imprime en el propio panel del mapa, como corresponde.
- Open-Meteo para el clima y para geocodificar, y las tasas de referencia del BCE vía Frankfurter para la cotización. Ninguno de los tres pide API key: por eso el proyecto se puede clonar y correr sin registrarse en nada.