The undo/redo pair and the trim/sheet canvas-size pills lived inside
.topbar's flex row, which wraps at narrow widths. Adam wanted them in a
permanent, predictable place so muscle memory works regardless of
editor mode or sidebar state.
Changes:
- .status is now `position: fixed; top: var(--top-strip-height);
right: 0; flex-wrap: nowrap` with the same panel/border styling as
the Nostr widget — a second strip directly below it.
- Reordered the markup so the canvas-size pills come first and the
undo/redo group last, so undo/redo end up hugging the viewport edge
and the pills sit to their left (matching the requested layout).
- Dropped the `body.app-designer .status` override (full-width
left-aligned), since the new positioning is universal across modes.
- updateNostrWidgetSpace() now measures the wider of the Nostr widget
or the new strip and reserves topbar right padding accordingly, so
the project title can't slide under the strip.
Verified via CDP at 1440px (cover, customart, pixelart modes) and at
900px narrow — strip stays pinned top-right with undo/redo on the
outside edge in every case.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
9.1 KiB
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
- Pixel canvas size dropdown — fix sidebar clipping. When the sidebar is open and the canvas size row wraps to a second line on the left, the pixel-art canvas-size dropdown is clipped by the sidebar. Easiest fix is probably "always right-align when wrapped" so the dropdown opens away from the sidebar; or move the dropdown to a placement that can't collide with the sidebar.
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
pixelsB64size-fallback encoder. The spec calls for each frame to store whichever is smaller:pixels(RLE pairs) orpixelsB64(base64 of the rawUint8Arrayof 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)
-
Top-right action strip — permanent, never wraps. Pulled the canvas-size readouts and the undo / redo pair out of the topbar's flex flow and pinned them in a fixed strip directly under the Nostr widget, right-anchored, with
flex-wrap: nowrap. Order: canvas-size pills on the left, undo / redo on the right (so undo / redo hug the viewport edge). The topbar's right padding now tracks the wider of the Nostr widget or the strip via the existing--nostr-widget-spacevar, so the project title never slides under it. -
Click canvas background → deselect. The existing #sheet click handler missed clicks in the gutter between sheet edge and previewShell edge; added a #previewShell handler.
-
Crop marks for Custom Art / Slide canvases. New
.canvasCropscorner marks inside .sheet, scoped to those template modes. The existing showCrops / printCrops toggles drive screen + print visibility (relaxed from cover-only to a new.crops-toggleclass). -
Strip selection chrome from PDF Print / JPEG-fallback output. Dead
body.exportingCSS rules were renamed to actually match the class JS sets. Wired both the regular printBtn flow and the JPEG-PDF flow to set the class. Two-class split (body.exportingfor chrome-stripping in either flow,body.export-printfor the heavier container-isolation only the JPEG-PDF flow does) avoids the blank-print regression that a single class caused. -
Custom Art: match canvas to image's natural dimensions. New "Match canvas to image size" button on the image-layer panel (Custom Art mode only; Slide is 16:9 locked). Loads the image via loadImageWithCors, clamps to CUSTOM_ART_MAX, pushes one undo entry.
-
JPEG export — rich text rendering (P2). Canvas-based engine that walks the layer's HTML into styled runs (bold/italic/underline, bullets, numbered lists, line breaks), lays them out with ctx.measureText + word wrap, and draws each item with its own font
- underline. Dispatches to plain
fillTextwhen no inline marks are present. SVG-foreignObject was tried first; it taints the canvas in Chromium so we built the run-layout engine instead.
- underline. Dispatches to plain
-
Independent-axis corner-node resize. Corner handles default to independent axes (each axis follows its own drag delta); Shift+drag locks aspect (Figma / Photoshop / Keynote convention). The sidebar 🔒 toggle still governs typed W/H input cascades.
-
JPEG export — DPI / quality selector (P4 polish). 96/150/300 dpi presets in a dropdown next to the Export JPEG button.
-
JPEG export — one-click direct rasterizer (P1). Replaces the print-to-PDF-then-re-pick flow for Cover, Disc sheet, Jewel, Disc design, Custom Art, and Slide canvases. The infrastructure was already 90% built (drawLayerToCanvas + four canvas-type rasterizers existed but were never wired to any entry point); P1 added the missing Custom-Art rasterizer + the orchestrator that picks the right rasterizer per templateMode, pre-flights CORS via the existing _exportFailedImages set, and falls back to the print modal when any image can't be embedded directly.
-
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 viawindow.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: embeddedpixelartlayer + standalonecasewrap-pixelartdesign, 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 usesoverflow-wrap: anywhere; word-break: break-word, so no horizontal scroll. -
TMDB auto-fill cleanup —
docs/TMDB_AUTOFILL_FEATURE.mdremoved.