Canva Editor Improvement Backlog¶
Prioritised ideas for the local Canva editor (http://localhost:8790/), from a feature inventory + dedicated UI/UX review on 2026-10-09. Ordered by impact ÷ effort. Companion page: [[OpenCode Canva Design Skill]].
Verdict of the review: competent developer tool with a Canva-ish control list, not a Canva-like product. Bones are right (sidebar / stage / optional chat, live CSS-var editing, drag handles) — thumbnails, selection state, zoom, and a real export result are missing.
Status note (2026-10-11): the editor has since moved to a descriptor model — freeform chat (
/api/chat) was removed and replaced by/api/ask(plain-language → change descriptors, applied bylib/patch.js), plus site-colour extraction (lib/palette.js). Item 10's "chat drawer" now refers only to the Ask panel, which is a normal sidebar panel, not an overlay. Items 1–9 and 11–16 are unchanged. See [[OpenCode Canva Design Skill]].
Bugs first (fix before building)¶
- Drag/resize runs at 2× cursor speed — pointer deltas are divided by
SCALEtwice; inner iframe coords are already design px (verified with a Playwright probe: outer 100px → inner 200px atscale(.5)). One-line fix ateditor.js:103-104,125. - Non-square templates are clipped — stage hardcoded 540×540, iframe 1080×1080 (
editor.html:26-27), but 5 of 9 templates aren't square (story 1080×1920, linkedin-banner 1584×396, banner 1200×628, youtube-thumbnail 1280×720, flyer 1080×1350). Export dims are correct, so preview ≠ output. - Sidebar color rows are no-ops on half the templates — hardcoded to
--bg/--primary/...butlogo/flyer/announcement/quote-card/storyuse--mark/--ink/--bg3. Build rows dynamically from the template's actual:rootvars (values are already read ateditor.js:189). - Export success signal is popup-blocked —
window.openafter twoawaits usually gets blocked; only feedback is an 11pxbreak-allstatus line. Button never disables, so double-clicks fire duplicate renders.
High impact¶
- Fit-to-template stage + zoom/pan — size stage/frame from
curW/curH(already fetched), replace theSCALEconst (editor.js:5) with a live scale, add Fit/50%/100% + percentage readout, pan when zoomed past fit. Unblocks everything else. - Persistent selection + contextual toolbar —
.selclass exists but has no CSS; selection is a 600ms flash (editor.js:144-149). Make it stick, anchor a floating toolbar with font size, text color, duplicate, delete, forward/back, lock. Delete and z-order currently have no UI at all. - Undo/redo — snapshot stack over the existing
getCleanHtml()serializer (editor.js:269), pushed on drag/resize pointerup, color/font commit, image insert, template load, and chat-apply. Today a bad Hal reply blanks the canvas with zero recovery. - Export panel — format select (PNG/WebP/JPG —
render.js:35-38already supports all three; server hardcodes WebP ateditor-server.js:202), scale 1×/2× (server hardcodes"2"at:208), filename with real output name shown, and a result card: thumbnail + Download (<a download>, notwindow.open) + Copy path. - Template gallery with rendered thumbnails — replace the
<select>(editor.html:58) with a card grid; one-time server render of 9 previews via existinglib/render.js, cached indesigns/previews/. Biggest single "Canva vs dev tool" visual gap. - Chat as overlay drawer — toggling chat mutates grid columns so the canvas jumps (
editor.js:264-266); below ~1400px render as right-edge overlay with scrim. Add greeting + 4 tappable suggestion chips as empty state, persistent typing indicator instead of"…", style the disabled send button, rehydrate transcript from server-sidechatHistory.
Cheap polish (low effort, disproportionate effect)¶
- Quiet the canvas — dashed
.edoutlines are permanent (strongest "dev tool" signal); show on hover/selection only. Handles are 16px in design space = 8 screen px at scale .5; scale inversely to zoom → 24–32 screen px. - Loading/empty states — template load has zero indication (first paint is a black box); guard the unguarded
fetch().json()ateditor.js:16; drag-over needs text ("Drop an image"), not just an outline. - Keyboard — only shortcut today is Ctrl+Enter for chat. Add Del-to-delete, arrow nudge 1px / Shift 10px (no fine positioning exists), Esc clear, Cmd/Ctrl+Z undo,
+/-/0zoom,:focus-visiblerings (none anywhere). - Sidebar reorder + identity — chat toggle is currently the first control; Template should be. Replace
system-uiand<h1>Canva-style Editorwith a real product name; delete leaked hint "Talks to the same AI as this session" (editor.html:104). - Font picker — replace free-text font fields (
editor.html:79-80) with a dropdown of installed Noto families (~1300) rendered in their own faces. - Guards — template switch silently discards edits (
editor.js:34); add dirty-dot + confirm,localStoragedebounced autosave as a save bridge.
Housekeeping¶
lib/brand.jsonis documented as the defaults source (editor/README.md:24) but nothing reads it; its keys (background,secondary) don't match sidebar var names either. Wire it in or drop the claim.- Stale
editor-server.js.bak.20260830(and.bak.20261009) are publicly fetchable via the/editor/*route; the Aug 30 copy still contains the old credential-reading approach. Exclude*.bak*from that route or move backups out ofeditor/. POST /api/exportoutput is always.webp@ 2× despiterender.jssupporting png/jpg and SKILL.md documenting PNG as the skill output.readTemplatesdimension regex (editor-server.js:138) requires an exact one-linehtml, body { width: Npx; height: Npxshape; silently falls back to 1080×1080.safeJoinpath guard uses barefp.startsWith(base)without a separator check (theoretical prefix confusion) — same pattern inlib/serve.js:36.
Suggested sequence¶
1 (one-liner) → 2+5 (stage/zoom) → 6 (selection) → 11+12 (polish) → 8 (export) → 9 (thumbnails) → 7+13 (undo+keys) → 10 (chat) → 14-16
Related¶
- [[OpenCode Canva Design Skill]] — the skill itself, architecture, CLI usage
- [[OpenCode Voice Input]] — separate project, unrelated to the editor