3 Commits
Author SHA1 Message Date
thePR0M3TH3AN f6007876b7 External embed Phase 1: chromeless read-only viewer
Per docs/EXTERNAL_EMBED_FEATURE.md §18 Phase 1.

- parseEmbedRoute() detects three URL forms so the embed works on
  Vercel-rewrite-friendly hosts AND on plain static servers:
    /embed/<id>       (canonical; needs SPA rewrite)
    #/embed/<id>      (hash; works on any host)
    ?embed=<id>       (query; works on any host)
- Early-return at the top of init() routes embed mode to
  bootEmbedViewer; skips welcome modal, slot/disc/jewel controls,
  attachEvents, walletUI, autosave restore, share-route popstate.
  Editor JS stays loaded (single bundle), but none of its boot
  steps run.
- bootEmbedViewer() decodes the identifier via the existing
  decodeNostrId helper (note / nevent / naddr / hex), races the
  hint relays then user relays + DEFAULT_NOSTR_RELAYS for the
  event, warms linked-design sub-trees in one prefetchBatch
  round-trip, then paints via the existing
  renderDesignPayloadPreview + fitMiniPreview path.
- Premium events refuse with "🔒 Premium design — open in Artstr
  to unlock" placeholder; Phase 2 will render the watermarked
  preview JPEG with an inline CTA.
- Private (encrypted) envelopes refuse with a 🔒 placeholder; no
  decryption attempted.
- body.embed-mode CSS hides every existing top-level element
  (`body.embed-mode > :not(#embedRoot) { display: none !important }`)
  and shows a single #embedRoot with a stage + thin footer (author
  chip + "Open in Artstr ↗"). attr / open / controls query params
  toggle each piece.
- vercel.json gains /embed/:id SPA rewrites and a
  Content-Security-Policy: frame-ancestors * header so the route
  is iframe-able from any origin. Production behavior; dev (python
  http.server) needs no headers — cross-origin framing works by
  default.

Manual smoke-test paths:
  http://localhost:8000/?embed=<naddr>            (query form)
  http://localhost:8000/#/embed/<naddr>           (hash form)
  http://localhost:8001/                          (cross-origin
                                                   iframe host)
2026-06-01 09:01:35 -04:00
thePR0M3TH3ANandClaude Opus 4.7 e9b38710d6 Add /u/:npub profile pages
Phase A — URL routing and modal skeleton:
- Vercel rewrites for /u/:npub (with hash fallback for static hosts).
- profileModal markup, CSS for banner/avatar/header/chips/designs grid.
- decodeProfileIdentifier handles npub1, nprofile1, and raw hex.
- popstate routing dispatches between /share/:id and /u/:npub.

Phase B — Profile metadata fetch and render:
- ensureProfilePageData with 5-minute TTL cache layered over the
  existing state.community.profiles and _feedCache.profiles caches.
- Renders banner, avatar, display name, NIP-05, npub, about, lud16 /
  decoded lud06 copy chip, website chip.
- Action row: Follow/Unfollow (kind-3 toggle), Block (with confirm),
  Copy npub, Open in nostr client. Self-view shows "This is you".
- toggleProfileFollow added for arbitrary-pubkey follow/unfollow.

Phase C — Designs grid and wiring:
- Streams kind-30078 events by author into the designs grid using the
  existing buildFeedCard + diffWallCards plumbing. Filter dropdown
  reruns the render against the cache.
- Author surfaces across the app (feed-card chip, design preview meta)
  now route to openProfilePage.
- Top-right profile button opens the signed-in user's own profile page
  instead of the Mine tab.

Phase D — Polish:
- ESC closes profile / report / close-project / pdf-jpeg modals.
- Initials avatar fallback with a pubkey-derived hue when picture is
  missing or 404s; same gentle fallback for banner.
- Block-state banner with one-click Unblock on profiles you've blocked.
- Async NIP-05 verification adds a ✓ when /.well-known/nostr.json
  resolves the claim back to the same pubkey.
- Modal z-stack helper bringModalToFront so the design preview opened
  from inside a profile lands on top instead of being hidden behind it.
- Closing a preview that was launched from a profile dismisses the
  community modal too, returning the user to where they came from.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-18 13:21:30 -04:00
thePR0M3TH3ANandClaude Sonnet 4.6 d94eff9671 Add shareable design links via NIP-19 naddr/nevent
Share button (template browser preview):
- New "Copy share link" button next to Save JSON / Print
- Generates a NIP-19 identifier:
  * naddr (stable, address-based) for new-style events whose d-tag
    starts with the mode prefix (cover:, disc:, jewel:, disc-design:)
  * nevent (event-id-based) for legacy events whose d-tag is a bare
    IMDB id, so a cover share-link doesn't collide with the
    disc-design share-link for the same movie
- Encoded URLs use #/share/<id> form so they work on any host without
  a server rewrite; the URL bar also updates while a preview is open

Receiving:
- On init, parseShareIdFromLocation() reads either /share/<id> or
  #/share/<id> and dispatches to loadSharedDesign()
- loadSharedDesign uses a two-stage fast path: query the relay hints
  embedded in the naddr/nevent first (2s timeout), fall back to the
  user's default relay list (4s timeout) only if those don't have it
- Skips the full fetchFeedAndReactions(); instead fetches only the
  one event + that author's kind-0 profile + kind-7 reactions on that
  event + kind-5 deletions for that author/kind, all in parallel with
  short timeouts. Typical lookup is now ~1-3s instead of ~20s.

Bech32/NIP-19 codec:
- Self-contained encoder + decoder for note/nevent/naddr (no
  dependencies). Decoder also accepts a raw 64-char hex event id.

URL lifecycle:
- openDesignPreview replaces the URL with #/share/<id> so reload/
  bookmark/copy-from-address-bar all work
- closeNostrPreview restores the URL to / so reload doesn't re-open

Hosting:
- vercel.json with rewrites maps /share/:id -> / for the "pretty"
  path form. Hash form works without any host config.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-17 14:54:02 -04:00