From a0a7f215c2a069aa96608d1aaa725e0fa39b58c0 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sun, 31 May 2026 21:12:48 -0400 Subject: [PATCH] Linked Designs P1d: autosave restore + publish strip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit restoreAutosave now collects every linked layer (top-level + every deck slide) and kicks off linkedDesigns.resolveLinked for each. This pre-warms the cache so the first render's renderResolved fires from a synchronous peek instead of paying the relay round-trip serially, and duplicate refs across slides dedupe via the inflight map. stripLinkedDesignEphemera removes session-only fields (lastResolvedAt, lastResolvedSize, transient fallbackPreview) from linked layers in a published payload. Called once at the top of publishCurrentTemplateToNostr — before the private/premium/public branches — so every publish path is covered. fallbackPreview only survives if design.snapshotPreview === true (a future opt-in surface for designs that want a still preview to ship alongside the link). Unit tests at /tmp/test_stripLinkedEphemera.js (16 cases) cover the mutation, opt-in, deck walking, and null-tolerance paths. --- src/index.html | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/index.html b/src/index.html index f0f2ed8..31126df 100644 --- a/src/index.html +++ b/src/index.html @@ -6646,6 +6646,32 @@ return { resolveLinked, walkLinkedTree, walkContributors, clearCache, peek, keyFor, _parseAddressCoord, _cacheKey }; })(); + // Pre-publish hook. Linked layers carry render-only fields + // (lastResolvedAt, lastResolvedSize, transient fallbackPreview) + // that are session-local and should not ship in the published + // event. Walks the host payload's layers + every deck slide's + // layers and strips them in place. fallbackPreview survives only + // when the user explicitly opted in via design.snapshotPreview = + // true (a future polish-phase UI surface; not exposed yet). + function stripLinkedDesignEphemera(payload) { + if (!payload || typeof payload !== 'object') return payload; + const strip = (l) => { + if (l?.type !== 'design' || l.design?.kind !== 'linked') return; + delete l.design.lastResolvedAt; + delete l.design.lastResolvedSize; + if (!l.design.snapshotPreview) delete l.design.fallbackPreview; + }; + if (Array.isArray(payload.layers)) { + for (const l of payload.layers) strip(l); + } + if (Array.isArray(payload.deck?.slides)) { + for (const s of payload.deck.slides) { + if (Array.isArray(s?.layers)) for (const l of s.layers) strip(l); + } + } + return payload; + } + // ---- NWC sync (cross-device wallet credential sharing) ----------------- // Stores the NWC connection URI in a NIP-44-self-encrypted kind-30078 // event with d=artstr:nwc-connection:v1. Mirrors the purchase-vault @@ -15428,6 +15454,11 @@ throw new Error('Add cover artwork or at least one layer before publishing.'); } } + // Linked-design layers carry session-only fields (lastResolvedAt, + // lastResolvedSize, transient fallbackPreview) that must not ship + // in the published event. Strip them once here, before the path + // branches into private/premium/public publish. + stripLinkedDesignEphemera(payload); // Private: encrypt and publish a feed-invisible event instead of the // normal public, tagged one. if (state.visibility === 'private') { @@ -26868,6 +26899,29 @@ if ((parsed.appMode || parsed.ui?.appMode) === 'designer') { loadCurrentDiscDesignLayers(); } + // Kick off the linked-design resolver for every restored + // linked layer so the cache warms in parallel with the first + // render. The renderer's async branch handles the empty-cache + // case correctly on its own, but pre-warming dedupes + // duplicate refs across slides via the inflight map and lets + // the slot's renderResolved fire from a synchronous peek on + // subsequent re-renders. Non-blocking. + const linkedSpecs = []; + const collect = (layers) => { + if (!Array.isArray(layers)) return; + for (const l of layers) { + if (l?.type === 'design' && l.design?.kind === 'linked' && l.design.ref) { + linkedSpecs.push({ ref: l.design.ref, pinnedEventId: l.design.pinnedEventId || null }); + } + } + }; + collect(state.layers); + if (Array.isArray(state.deck?.slides)) { + for (const s of state.deck.slides) collect(s?.layers); + } + for (const spec of linkedSpecs) { + try { linkedDesigns.resolveLinked(spec); } catch {} + } } catch (error) { console.warn('Autosave restore failed', error); }