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); }