$ cd ..

Así codeamos con Claude · 06: polish e ideas extra

12° · 50% · soleado
claudeclaude-codeprocesstinyspritedev-log

Capítulo 5 cerró con el MVP completo: pintás, deshacés, exportás, recarga la página y sigue. Suficiente para llamarlo terminado. Pero hay esos detalles que separan "MVP" de "se siente como un producto". Ese es este capítulo. La estrella es el time travel.

Time travel

El history ya existía: cada trazo iba a un stack de hasta 100 snapshots con ⌘Z para deshacer. El problema: si querés volver veinte trazos atrás, son veinte teclazos. Y si te pasaste y querés re-avanzar diez sin saber exactamente dónde, andás a ciegas.

La idea del time travel resuelve eso: una bar flotante con thumbnails de cada snapshot, donde podés saltar directo a cualquier estado o previsualizar uno antes de comprometerte.

Pero meter una bar permanente abajo del canvas le robaba espacio al dibujo y le agregaba ruido visual al editor. Solución: la bar está oculta por defecto, fuera del viewport, y un botón ghost mínimo la dispara.

Editor TinySprite en estado cerrado mostrando un dibujo de una caja de cartón isométrica. Abajo del canvas se ve un botón sutil con borde dashed que dice 'time travel'
estado default: solo el botón ghost time travel abajo del canvas. la bar de history está oculta fuera del viewport.

El botón es deliberadamente discreto. Mono font lowercase, dashed border, color subtle. Ocupa lo mínimo y aparece solo cuando le pasás el mouse por encima.

Close-up del botón time travel en estado de reposo: borde dashed, color sutil, mono font lowercase
el botón en reposo. el hover lo pasa de dashed a solid y le sube el contraste.
Editor con el timeline abierto: una bar con thumbnails de cada estado del dibujo aparece centrada abajo del canvas, con un thumb destacado en rosa indicando el estado actual
al pasar el mouse por el botón, la bar emerge desde abajo. el thumb activo va destacado en rosa.
Detalle de la fila de thumbnails: cada miniatura muestra un estado anterior del sprite. El thumb activo tiene un halo en color accent rosa
cada thumb es un pequeño canvas con el snapshot del sprite en ese punto. el activo lleva ring + halo en accent.

El cierre con gracia

Una vez abierto el timeline, lo lógico sería cerrarlo apenas el cursor se aleja. Pero eso castiga al usuario: si te alejás un milímetro de un thumb para apuntar a otro, la bar desaparece y arruina la interacción.

Solución: cuando salís de la bar, arranca un timer de 2 segundos completos. Si volvés a entrar antes, el timer se cancela y la bar sigue ahí. Si pasan los 2s sin actividad, slide down y el botón ghost vuelve a aparecer.

Dos segundos quedó bien. Menos te corre, más te hace dudar si todavía está.

El preview que vende el feature

Lo que diferencia el time travel del undo/redo plano es que hacer hover sobre un thumb muestra ese estado en el canvas grande, sin commitear. Podés mirar versiones antiguas, comparar, y si te gusta una hacés click para saltar ahí.

Demostración del time travel: el botón ghost se hovera y el timeline emerge desde abajo. Al pasar el mouse por thumbs anteriores, el canvas grande va mostrando esos estados antiguos del dibujo en vivo. Al salir, el timeline vuelve a esconderse tras unos segundos
el patrón completo en movimiento: descubrir, preview, jump o salir.

Por detrás es simple: el store del editor tiene un previewIndex: number | null. Cuando es null, el canvas renderiza el sprite actual. Cuando tiene un valor, el SpriteCanvas usa los pixels de history[previewIndex] en vez del estado compuesto. Hover seteas, leave nulleás. Si arrancás un trazo nuevo, el preview se limpia automático.

Lo demás del polish

Tres ajustes chicos que no merecen capítulo propio pero le suman al feel general.

Theme toggle light / dark. Botón sun/moon en la toolbar. Respeta prefers-color-scheme del OS por default, después del primer click se queda con tu elección. Persiste en localStorage. Un script inline en <head> aplica el theme guardado antes del primer paint para evitar el flash de tema equivocado.

user-select: none donde corresponde. Antes, intentar dibujar rápido a veces seleccionaba el texto de los labels del toolbar o del sidebar. Apliqué user-select: none al root del editor y un override text solo para inputs y contenteditable. Pintás sin que el browser intervenga.

Mobile y a11y. En viewports angostos (≤ 640px) la sidebar pasa a una bar horizontal abajo del canvas y los swatches crecen para dedo. Saqué los textos del toolbar para que las acciones quepan. Y un audit con Lighthouse: empezó en 95, lo cerré en 100/100 después de bumpear contraste en dark y alinear los aria-labels al patrón label-in-name de WCAG.

Próximo capítulo