Files
Artstr-Studio/TODO.md
T
thePR0M3TH3ANandClaude Opus 4.7 d95acfaf17 TODO: queue one-click JPEG export via direct canvas rasterizer
Current JPEG flow makes the user save a PDF, then re-pick it from
disk for PDF.js to convert. We can't cache the PDF in between
because window.print() doesn't expose the generated bytes to JS.

Log the proper fix: write a direct canvas rasterizer that walks
the layer model (image / text / shape / color / qr / pixelart),
draws each layer onto an off-screen canvas at the artboard's design
dims, then toBlob('image/jpeg'). Same approach the pixel-art PNG
export already uses, generalised across all layer types. CORS-
tainted remote images fall back to the existing PDF round-trip,
with a toast explaining why. ~300–500 LoC, no new dep.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-27 09:14:08 -04:00

177 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Artstr Studio — TODO
Running list of deferred work. Detailed specs for the larger items live in
`docs/`. Each entry notes scope and where the spec is (if any).
## Adam's working list
- **Click-canvas-background-to-deselect.** In the vector canvas editor,
clicking on empty canvas (outside any layer) should clear the layer
selection. Today the selection persists, which makes it hard to "exit"
a selected layer without picking another one.
- **Toggle crop marks for export previews.** A new toggle in the
vector designer to hide crop marks from the PDF Print/Save and
JPEG Export flows. Today crop marks bleed into clean exports.
- **Custom-art canvas size from imported background URL.** When a
user pastes a background image URL on a Custom Art canvas, offer
a one-click "Match canvas to image size" button (or apply
automatically on first import) so the canvas dims = image dims.
Edge cases: huge images (clamp to CUSTOM_ART_MAX), aspect-locked
layouts (Slide is locked 16:9 — should be skipped or warn).
- **JPEG export: hide layer-selection outlines.** The Save-as-JPEG
flow currently renders the selected-layer dashed outline in the
output. Add `body.exporting` (or similar) state that strips
selection chrome before the raster snapshot.
- **One-click JPEG export (skip the PDF round-trip).** Today the
flow is: print → user saves PDF → user re-picks PDF → PDF.js
converts to JPEG. We can't cache the PDF in between — `window
.print()` doesn't expose the generated bytes to JS. Instead,
write a direct canvas rasterizer that walks the layer model
(image / text / shape / color / qr / pixelart) and draws each
layer onto an off-screen canvas at the artboard's design dims,
then `toBlob('image/jpeg')`. Same approach the pixel-art PNG
export already uses, generalised. CORS-tainted remote images
(cross-origin host without `Access-Control-Allow-Origin`) would
break the direct path — fall back to the existing PDF round-trip
for those with a one-line "this image needs the PDF route" toast.
Most modern image hosts (CDNs, Blossom blobs) send CORS so the
fallback should be rare. ~300–500 LoC of layer-by-layer drawing
code; reuses `renderShapeIntoElement` etc. as references for
geometry but writes to canvas instead of DOM.
- **Independent-axis corner-node resize.** Corner resize handles
currently lock the aspect ratio (or scale both axes together).
Behaviour should follow the actual drag vector: dragging mostly
horizontally with a small vertical move resizes width a lot,
height a little — both axes scale proportional to their own
movement, not to each other. Applies to every layer type's
resize handles, not just text.
## CORS for remote-image raster export
Quick reply, no action yet:
- The PDF Print/Save path renders via `window.print()` → the browser's
native print/PDF engine, which handles cross-origin images fine. No
CORS issue there.
- The JPEG export path already round-trips through PDF.js (PDF → JPEG),
so it inherits the PDF path's CORS tolerance. If it's failing for
some images, the root cause is likely the print path itself, not a
canvas taint.
- The canvas-tainted situation only bites if we ever do a direct
`<canvas>.toBlob('image/jpeg')` against a layout that contains a
cross-origin remote `<img>` without CORS headers. Options if it
becomes a real problem: (a) keep using the PDF.js round-trip;
(b) recommend CORS-friendly hosts (Blossom blobs, ImgBB, most
CDNs send `Access-Control-Allow-Origin: *`); (c) add an
optional same-origin proxy endpoint — needs a server, which the
app currently doesn't have.
## QR code layer — remaining phases
Phases A and B shipped (the `qr` layer type: data, error-correction level,
quiet zone, module / background colors, transparent background, renders
on every surface + canvas export, JSON round-trip; plus "Insert npub" and
"Insert share link" buttons under the QR data field).
- **Phase C — visual polish.** Optional center logo overlay and rounded
modules. Riskier: a logo eats into the error-correction budget, so it
should nudge the ECC level up. Skippable.
## Editor / tool UX
Small editor-usability improvements — no spec doc; scoped here.
Shipped: on-canvas drag-resize handles for every layer type, text-box
resize that never rescales the point size, the expanded Text tool panel
(font / size / color / alignment / bold / italic), a collapsible sidebar
toggle that fits the early-web aesthetic. Add new editor-UX items below.
## Profile pages — deferred polish
(Core profile pages at `/u/<npub>` already shipped.)
- Make author chips clickable in the **community thread view** (comments and
reposts) — currently only feed-card and preview-meta author chips route to
`openProfilePage`.
- **About-text clamp** — long bios overflow the profile header; clamp to ~4
lines with a "Show more" toggle.
- **Profile-grid pagination** — an author with 100+ designs renders them all
at once; cap the initial render and add a "Show more" pager.
## Edit-published-designs — deferred polish
(Core edit-in-place via NIP-33 replaceable semantics already shipped.)
- Replace the remaining `confirm()` calls in the edit / publish flow with the
styled in-app modal pattern, for consistency with the close-project and
publish-confirm modals.
- "Editing a previously-deleted design" warning — if the user opens Edit on a
design they had tombstoned with a kind-5, warn that publishing will
republish it.
## Pixel art — future polish
- **`pixelsB64` size-fallback encoder.** The spec calls for each frame
to store whichever is smaller: `pixels` (RLE pairs) or `pixelsB64`
(base64 of the raw `Uint8Array` of indices). Only RLE is implemented
today. For noisy / photo-pixelized content with many palette colours,
base64 is ~5× smaller — a 24-frame 64×64 sprite tested at 785 KB
RLE would drop to ~150 KB. Worth shipping alongside a live event-size
readout in the editor (soft warn at 50% of relay budget, hard block
at 90%) so users see the budget before publish.
## Drafted-but-unbuilt features
Each has a full spec in `docs/`.
- **Private (encrypted) publishing** — `docs/PRIVATE_PUBLISHING_FEATURE.md`.
Publish any design encrypted, readable only by the author (Phase 1
"private to me"). The agreed next build; Phase 0 is a validation spike.
- **Artstr Stacks** — `docs/STACKS_FEATURE.md`. Interactive fullscreen
page/card stacks (HyperCard for Nostr) — the Slide Deck extended with
layer actions and an interactive viewer. Queued after private publishing.
- **Badges (NIP-58)** — `docs/BADGES_FEATURE.md`. Award / display Nostr
badges on profile pages.
- **Collections (NIP-51 sets)** — `docs/COLLECTIONS_FEATURE.md`. Let users
organize favorite community templates into named sets.
- **Zap-gated premium templates** — `docs/ZAP_GATED_TEMPLATES_FEATURE.md`.
Creators mark a template premium with a minimum-zap threshold.
- **Zaps via Nostr Wallet Connect** — `docs/ZAPS_NWC_FEATURE.md`. In-app
zapping without the wallet handoff. (Tracked LNURL-pay tips already ship;
this would be the NWC path.)
## Shipped (recent)
- **Slide deck PDF export** — multi-page PDF, one page per slide. The
existing Print / Save PDF button is context-aware: in the Deck Builder
it builds a hidden print-only container with one page per slide (themed
via `composeSlideForDeck`, each page sized to the preview's native
dimensions so the slide fills the page exactly) and emits one PDF via
`window.print()`. Inside a single-slide editor, the button still prints
just that slide.
- **Copy / paste transform consistency** — paste now computes a single
group bounding box across all clipboard items, applies one shared scale
(only shrinks, never enlarges) and one shared translation, so cross-
target paste keeps every layer's relative position and size to the
others intact.
- **Pixel-editor colour picker** — ported the OS native `<input type="color">`
over from the vector tools (same swatch styling; opens the OS picker with
its built-in eyedropper on click). Removed the Screen-Capture-API screen-
eyedropper workaround (~170 lines of dead code). Palette swatches and
the in-canvas eyedropper tool now sync the picker too, so grabbing a
palette colour as the starting point for a tweaked variant just works.
- **Pixel Art** — `docs/PIXEL_ART_FEATURE.md`. Phases A–D all shipped:
embedded `pixelart` layer + standalone `casewrap-pixelart` design,
PNG / sprite-sheet / GIF export, drawing tools (line / rect / ellipse,
flip / rotate, grid resize), animation (multi-frame timeline, playback,
onion-skin), image → pixelize, and animated previews in every preview
surface. Undo / redo integrated with the top-level toolbar.
- **Pixel editor layout expansion** — addressed by the collapsible
sidebar (gives both the pixel and vector editors a lot more breathing
room when needed).
- **Print / Save event ID wrapping** — the event-id `<dd>` already uses
`overflow-wrap: anywhere; word-break: break-word`, so no horizontal
scroll.
- **TMDB auto-fill cleanup** — `docs/TMDB_AUTOFILL_FEATURE.md` removed.