thePR0M3TH3ANandClaude Opus 4.7 861e17e3f9 Vector shapes (Phases A-E) + project polish
Vector shapes feature (per docs/VECTOR_SHAPES_FEATURE.md):

Phase A - Shape primitive layers (rect, rounded-rect, circle, ellipse,
triangle, polygon, star, line) with a 0-100 viewBox coord system that
renders on all six surfaces (editor canvas + five preview renderers)
plus Canvas2D export. Solid fill, kind-specific controls (corner
radius, sides, star points, inner ratio, line stroke width). Layer
data is purely additive in JSON; old clients skip unknown shape kinds.

Phase B - Linear + radial gradient fills with two-stop pickers. SVG
emits via <defs><linearGradient>/<radialGradient>; canvas via
createLinearGradient/createRadialGradient. Angles follow CSS
linear-gradient convention. Switching fill types preserves the most
relevant color.

Phase C - Solid strokes on all non-line shapes. Color, width (in
shape-local viewBox units), dash style (solid / dashed / dotted) with
auto-scaled dash patterns. SVG emits stroke attributes; canvas uses
strokeStyle + setLineDash.

Phase D - Custom SVG / full-SVG layer via file upload:
- File picker (.svg, image/svg+xml) replaces a paste textarea.
- Detects single <path d=…> vs full <svg>…</svg>; single paths land as
  kind:'path' with editable fill/stroke, multi-element SVGs land as
  kind:'svg' with fills baked in.
- Sanitizer strips <script>, <foreignObject>, <iframe>, <embed>,
  <animate>, <image>, <a>, all on* event handlers, external
  hrefs/xlink:hrefs, and javascript:/external-url style entries.
- Canvas export serializes the sanitized inner into a blob URL, loads
  via Image, drawImage.
- Per-element editor: a list inside the shape panel exposes every
  paintable child (path / rect / circle / ellipse / polygon /
  polyline / line) with fill + stroke color pickers and a stroke-width
  input. Auto-applies a viewBox-relative default stroke width when
  stroke is first set so strokes are visible at any zoom. Layer-level
  fill / stroke panels are hidden for SVG imports (per-element is the
  canonical control).

Phase E - Clip / mask on any layer type:
- New optional `clip` field with two flavors: inline-shape (clip to a
  primitive defined inline) and layer-ref (clip to another shape
  layer's geometry in the same target).
- Clip source dropdown in the selected-layer panel offers rect /
  rounded-rect / circle / ellipse / triangle / polygon / star / custom
  path / From another layer. Kind-specific param inputs show on demand.
- DOM rendering uses a shared hidden SVG defs container with
  <clipPath clipPathUnits="objectBoundingBox"> entries, referenced via
  clip-path: url(#clip-<layerId>) on each layer wrapper.
- Canvas export builds a Path2D with a DOMMatrix transform mapping
  0-100 coords to the layer's pixel bounds, then ctx.clip() before
  drawing the layer contents.
- Cycle detection: if A clips to B and B clips back to A, both fall
  back to unclipped instead of looping.
- Single-path-SVG clips reuse the upload modal in an edit-clip flow
  that rejects multi-element SVGs with an inline error.

Phase A bonus polish:
- Shapes spawn 1:1 (min(bounds.w, bounds.h) * 0.4).
- Session-wide aspect-ratio lock (default on) with a 🔒/🔓 toggle
  between the W/H inputs in the selected-layer panel. Typing in one
  dimension updates the other proportionally on every layer type.

Per-mode metadata isolation:
- state.savedMetaByMode parks Template-mode and Disc-Designer-mode
  metadata separately. swapMetaForAppMode runs before any appMode
  flip so loading a template in disc designer doesn't overwrite the
  designer's title / category / identifiers. omdbApiKey stays sticky
  across modes as a per-user preference.

Strict per-sub-mode layout isolation:
- isLayerForCurrentMode no longer treats target='page' as a universal
  wildcard. Each layer belongs to exactly one sub-mode by its target.
- Removed the "Whole page" entry from the layer-target dropdown.
- migrateLegacyLayerTargets retargets legacy page/empty layers to the
  current sub-mode's primary target on load. Wired into all four
  mode-specific load paths and the legacy full-project path.
- Fixes the bug where case-cover artwork was rendering misaligned in
  the disc-sheet preview.

Layer clipboard:
- Session-only clipboard supports cross-sub-mode and cross-tool paste
  with retargeting. Copy / Cut on selected layer + Copy all / Paste
  in the panel header. Paste retargets to the current sub-mode's
  primary target and clamps positions so a layer copied from a wide
  cover doesn't land off-canvas in a 4.6" disc.

UI polish:
- Hide unused artwork-mode sidebar controls: cover full-wrap mode
  hides the separate Back / Spine / Front controls and vice versa,
  via the existing body.mode-combined / mode-separate classes.
- Category-aware identifier visibility: only show IMDb / UPC /
  TheTVDB / MusicBrainz fields that fit the chosen category. Custom
  art hides all four.
- Added "Custom art" to the canonical mediaCategories list and
  populated both the template and designer category selects from it
  (the template select was previously hardcoded).
- Selected-layer panel hides entirely when nothing is selected.
- Jewel spine-text details block hides until at least one jewel
  insert has artwork.
- Close project / disc-designer reset / clear images all use a
  shared blankMeta() helper that covers UPC / TheTVDB / MusicBrainz
  (the previous reset path was missing them). Close project also
  resets state.customArt to its defaults so the background color
  doesn't carry over.
- Disc Designer mode now has a proper add-layer panel (Add image
  with URL + Copy / Add text / Add color block / Add shape menu) in
  addition to the layer list.
- Project file loads route through loadProjectFromText regardless of
  which "Load from file" button was used; an info toast announces a
  mode switch when one is needed.

Publish flow:
- New publish-confirm modal renders a live design preview, a
  metadata table (Type / Title / Category / Language / IMDb / UPC /
  TheTVDB / MusicBrainz / Author), and an edit-mode tradeoff banner.
  Replaces the bare confirm(). Errors render inline.
- Removed the legacy "Copy Nostr event" button — only "Copy event ID"
  remains, and it stays hidden until something has been published.

Toast system:
- All 41 native alert() calls swapped for in-app toasts (toastError
  with a red border for failures, toastSuccess with the accent color
  for publish/copy confirmations). Auto-dismiss after a few seconds;
  manual close button on each.

NIP-57 zap tab:
- _feedPagination grew a Zapped entry so the new tab can render
  without crashing the rerender pipeline.
- fetchZapsForEvents now runs chunks in parallel and streams a
  debounced rerender on every ingested receipt, so totals appear
  within a second or two instead of taking up to ~28 s.
- invalidateFeedCache clears the zap cache too so Refresh re-queries
  receipts.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-19 14:10:02 -04:00
2026-05-13 17:25:59 -04:00
2026-05-18 13:21:30 -04:00

CaseWrap Studio

CaseWrap Studio is a static print-layout editor for physical media packaging. It supports DVD/Blu-ray case wraps, Avery 8960/8944 disc-label sheets, Avery 8693/8943 CD jewel-case insert sheets, and reusable single-disc designs.

What it supports

  • Separate front, spine, and back case artwork (or one combined wrap image)
  • Disc-label sheet mode (two print positions on one Avery 8960/8944 sheet)
  • CD jewel insert mode (front + tray inserts on one Avery 8693/8943 sheet)
  • Disc Designer mode for creating a reusable single-disc design
  • Additional image and text layers
  • Drag positioning, z-order changes, rotation, sizing, and opacity
  • PDF export through the browser print dialog
  • Nostr community browsing/sharing for templates and disc designs
  • Publishing, replies, reactions, follows, and reposts from inside the app

Important workflow

All image sources are remote URLs.

That means:

  • Case art, disc art, jewel insert art, and extra image layers should use http or https URLs
  • Saved project JSON remains portable across machines
  • Exported/copied Nostr events can reconstruct layouts from event content
  • Community feed data comes from relays listed in the Community panel
  • Publishing/social actions require a NIP-07 browser extension (for example Alby or nos2x)

If a URL stops working later, the image will no longer render. For long-term reliability, use stable hosted URLs.

Main file

  • src/index.html is the complete app entrypoint. Open it directly in a browser or serve it from any static host.

Export and print

  • Use Print / Save PDF for print-ready output
  • Keep browser scale at 100%
  • Disable browser headers and footers

Save, restore, and sharing

  • Save project writes project JSON
  • Load project restores project JSON
  • Copy Nostr event copies a signed event payload
  • Import from Nostr restores a template/design from a copied event
  • The Community modal lets you browse New/Popular/Search/Mine, preview templates, and import them
  • Disc designs can be published and reused independently, then imported into Disc 1 or Disc 2 positions on a disc sheet

Nostr data model

  • Template/design payloads are published as kind 30078 app-data events with casewrap tags
  • Typical social events use standard kinds (1, 7, 6, 3, 0)
  • Template modes include cover, disc, jewel, and disc-design
  • Current schema version is 5; minimum supported schema version is 4

Notes

  • The app is fully static and does not require a backend
  • Local browser autosave is still available
  • Older saved projects with embedded data URLs may still load, but new work should use remote URLs
S
Description
A static site DVD and Blu-Ray Case and disk label designer
Readme
5.7 MiB
0 Stars 1 Watchers 0 Forks
Languages
JavaScript 99.7%
HTML 0.3%