July 22, 2025
wip
I wanted:
centered, 3:4, soft depth. positions live in percentages, not pixels.
what to notice
type WordItem = {
id: string; text: string;
xPercent: number; // 0..100
yPercent: number; // 0..100
};
measure from the magnet’s center → convert to board % → clamp to 0..100.
what to notice
const clamp = (v:number, lo=0, hi=100) => Math.min(Math.max(v, lo), hi);
const posFromClient = (cx:number, cy:number, board:DOMRect, off={x:0,y:0}) => ({
xPercent: clamp(((cx - board.left - off.x) / board.width) * 100),
yPercent: clamp(((cy - board.top - off.y) / board.height) * 100),
});
the dock glows while you hover with a drag. nothing happens until you drop.
what to notice
const overlaps = (a:DOMRect, b:DOMRect) =>
!(a.right<b.left || a.left>b.right || a.bottom<b.top || a.top>b.bottom);
// while dragging:
setInDelete(overlaps(new DOMRect(cx, cy, 1, 1), dockRect));
// on drop:
if (inDelete) removeWord(dragId);
dom snapshot via html2canvas
; scale by devicePixelRatio for hidpi.
what to notice
import html2canvas from 'html2canvas';
async function exportPNG(boardEl: HTMLElement) {
const scale = window.devicePixelRatio || 1;
const canvas = await html2canvas(boardEl, { scale });
const a = document.createElement('a');
a.href = canvas.toDataURL('image/png'); a.download = 'poem.png'; a.click();
}
words come from public/words.txt
. spawn near the middle so i’m not chasing them.
what to notice
const spawn = () => ({
xPercent: 50 + (Math.random() - 0.5) * 30,
yPercent: 50 + (Math.random() - 0.5) * 30,
});
next tiny passes: arrow-key nudge (1% / 5% with Shift), json export/import, and a lightweight overlap guard on spawn.