Resize: refit a design when the canvas size changes - #8
Conversation
Changing the canvas size resized the frame and left every object exactly where it was, so a square design retargeted to a story sat crammed against the top edge and a landscape one was simply cut off. The size picker was effectively a way to break a design. Resizing now refits the whole design: - Artwork scales by the smaller of the two ratios and is anchored to the centre, so nothing distorts and nothing that fitted before lands outside the new frame. - The backdrop a design is built on -- a template's full-bleed rectangle, or an image set through the background picker -- stretches to the new frame instead, so a taller ratio no longer leaves a band of bare canvas. - Text scales its font size and box width rather than its transform, so it re-wraps at the new width and the font-size control keeps reporting the real number. Two things had to be fixed for the frame to survive at all: - saveDesign never sent width/height, so a resized design reopened at its old dimensions with artwork scaled for the new ones. - The effect that adopts a design's stored frame was keyed on the design object, which save replaces. Saving after a resize snapped the canvas straight back. It is keyed on the design id now, and adopting a stored frame no longer reflows -- the stored artwork already fits it. A resize spans every page at once, so it cannot be expressed in the per-page undo stacks; and because the fit is lossy, resizing back is not an undo either (1080 square to 1200x627 scales by 0.58, coming back by 0.9). It gets one snapshot of its own instead, restored atomically by an "Undo resize" control that appears next to the size picker. Also adds Instagram Portrait, Pinterest, YouTube and 16:9 presets, groups the menu by platform, and adds a bounded custom width/height field.
…ored The snapshot behind "Undo resize" lived until the next resize, which made it outlive the state it was captured from in two ways. An edit made after a resize was silently destroyed: restoring the snapshot reverts every page to the moment before the resize, and the per-page histories are reset at that point too, so Ctrl+Z could not recover it either. The resize is no longer undoable once an edit lands on top of it. The snapshot also survived leaving the design. It is keyed by page id, so on another design no entry matched and nothing was restored -- but the frame was still applied, resizing an unrelated design to the previous one's dimensions with no reflow, and saving persisted that. A snapshot is only valid while the canvases it captured are mounted, so unregistering any page it holds retires it. That covers page deletion, switching designs, and leaving the editor and coming back. Both paths are regression tested in a real browser.
|
Pressure-tested the snapshot's lifecycle and found two defects the original tests were blind to — both were on paths my checks never walked (I resized and undid without editing in between, and never left the design). Fixed in 24f555f: An edit after a resize was silently destroyed. The snapshot leaked across designs. It was cleared only by 5 new regression checks in a real browser, plus the existing 14 geometry and 8 end-to-end checks still pass. One related pre-existing issue, not touched here: |
The problem
Changing the canvas size resized the frame and left every object exactly where it was. A square design retargeted to a story sat crammed against the top edge; a landscape one was simply cut off. On top of that the new size was never saved —
saveDesigndidn't sendwidth/height, and the effect that adopts a design's stored frame was keyed on the design object, which save replaces. So saving after a resize snapped the canvas straight back.The size picker was, in practice, a way to break a design.
What this does
Resizing now refits the whole design (
src/client/resize.ts):Plus the two persistence fixes above, Instagram Portrait / Pinterest / YouTube / 16:9 presets, a menu grouped by platform, and a bounded custom width/height field.
Undo
A resize spans every page at once, so it can't be expressed in the per-page undo stacks. And because the fit is lossy, resizing back is not an undo either — 1080² → 1200×627 scales by 0.58, and coming back scales by 0.9, not 1/0.58.
So a resize keeps one snapshot of its own, restored atomically by an Undo resize control that appears next to the size picker. Each page's history is reset to a baseline at that point, so you can never land in a state where a page's artwork is sized for a frame it no longer has.
Verification
Run against real Fabric 6.9.1 in real Chrome (headless), not just typechecked.
Reflow geometry — 14/14, using the actual
Quote Cardseed template: backdrop detection (and non-detection), backdrop fills the new frame at both taller and shorter ratios, uniform font scaling,scaleXleft at 1, everything inside the new frame, stacking order preserved, no-op resize is a no-op, lone-text canvas with no backdrop, resize-is-lossy, and snapshot-restore-is-exact.End-to-end in the running app — grouped menu and custom section render; canvas retargets to 1080×1920; undo-resize control appears; custom size applies; out-of-range input is clamped rather than crashing the tab; undo restores the previous frame; and the resized frame survives save + reload (the bug above).
Rendered pixels sampled from the canvas at 1%/25%/50%/75%/99% height confirm the backdrop fills top to bottom at 1080×1080, 1080×1920 and 1200×627.
tsc --noEmitadds no new errors (the 6 reported are pre-existing onmain:canvas-area.tsx, anduploads.tsmissing@types/node).Notes
designs.width/heightand thePUT /api/designs/{id}body already carried the frame — the client just never sent it.maindefect met while testing: the Worker can't boot becauseuploads.tscallsmkdirSyncat module scope. fix(uploads): store images in R2 so the Worker can boot #4 fixes it; I applied that branch locally to run the app and reverted it before committing.