From dfa25f4a0757692d43502cacead8003912e80405 Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Tue, 16 Jun 2026 19:04:46 -0700 Subject: [PATCH] keeps: embedded focused-keep mode for the editor's Keeps tab The editor's Keeps tab embedded keeps.html but it rendered the preamble/login gate (looked blank). Add an embedded focused mode: keeps.html: - When running in an iframe (window.self !== window.top), init() skips the preamble + the 14k-item grid, reveals UI, inits wallet, and posts keeps:ready to the parent. - Listens for { type:'keeps:piece', code, source } and opens the keep modal for exactly that piece. Auto-stores the current source via store-kidlisp first to resolve the canonical $code (so it keeps what's in the editor, not a stale cached code). Falls back to the same-origin localStorage seed. - .embedded CSS hides tabs/search/grid/castle; adds a focused empty-state. index.html (editor): - postCurrentPieceToKeeps() sends the current code+source to the iframe on tab open / iframe load, and replies to the keeps:ready handshake. Same-origin (kidlisp.com), so postMessage + Auth0 + wallet work inline. Co-Authored-By: Claude Opus 4.8 --- system/public/kidlisp.com/index.html | 27 +++++++ system/public/kidlisp.com/keeps.html | 102 +++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) diff --git a/system/public/kidlisp.com/index.html b/system/public/kidlisp.com/index.html index 064126dc02..25a0eae279 100644 --- a/system/public/kidlisp.com/index.html +++ b/system/public/kidlisp.com/index.html @@ -15560,6 +15560,28 @@ s("ape_breaks_3").loopAt(2) if (learnIframe) learnIframe.dataset.src = 'https://localhost:8888/kidlisp.com/learn.html'; } + // Hand the embedded Keeps tab the piece currently open in the editor so it + // can keep exactly that (same-origin iframe → postMessage with our origin). + function postCurrentPieceToKeeps() { + try { + const keepsIframe = document.getElementById('keeps-iframe'); + if (!keepsIframe?.contentWindow) return; + const code = (localStorage.getItem('kidlisp-current-code') || '').trim(); + const source = (window.editor?.getValue?.() || '').trim(); + if (!code && !source) return; + keepsIframe.contentWindow.postMessage( + { type: 'keeps:piece', code, source }, + window.location.origin, + ); + } catch (e) { /* iframe not ready / cross-origin — ignore */ } + } + + // The embedded keeps page announces readiness; (re)send the current piece. + window.addEventListener('message', (e) => { + if (e.origin !== window.location.origin) return; + if (e.data?.type === 'keeps:ready') postCurrentPieceToKeeps(); + }); + // Configure Monaco with jsDelivr CDN (modern, well-supported) require.config({ paths: { @@ -20116,7 +20138,12 @@ s("ape_breaks_3").loopAt(2) const keepsIframe = document.getElementById('keeps-iframe'); if (keepsIframe && !keepsIframe.hasAttribute('src')) { console.log('📦 [KEEPS] Lazy-loading keeps iframe'); + // Post the current piece once the embedded keeps page loads. + keepsIframe.addEventListener('load', postCurrentPieceToKeeps, { once: true }); keepsIframe.src = keepsIframe.dataset.src; + } else { + // Already loaded — re-target it at whatever piece is open now. + postCurrentPieceToKeeps(); } } diff --git a/system/public/kidlisp.com/keeps.html b/system/public/kidlisp.com/keeps.html index 133c32e993..adbcd6dd09 100644 --- a/system/public/kidlisp.com/keeps.html +++ b/system/public/kidlisp.com/keeps.html @@ -2779,6 +2779,29 @@ /* Embedded in parent editor iframe — hide branding */ .embedded .keeps-hero { display: none; } .embedded .keeps-footer { display: none; } + /* Embedded focused-keep mode: hide the browse chrome and the preamble so the + tab opens straight onto the current editor piece's keep panel. */ + .embedded .keeps-tabs { display: none; } + .embedded #index-search-bar { display: none; } + .embedded #keeps-grid { display: none; } + .embedded #keeps-about-inline { display: none !important; } + .embedded #keeps-castle { display: none; } + #keeps-embed-empty { + display: none; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + padding: 40px 20px; + text-align: center; + color: #8a8a8a; + font-size: 13px; + line-height: 1.5; + min-height: 180px; + } + #keeps-embed-empty .embed-empty-code { font-size: 22px; opacity: 0.5; } + .embedded #keeps-embed-empty { display: flex; } + .embedded.has-piece #keeps-embed-empty { display: none; } /* ======================================== FOOTER @@ -3282,6 +3305,12 @@ + +
+
$
+
Open or run a piece in the editor
to keep it on Tezos.
+
+
@@ -7127,10 +7156,83 @@ await startMintFlow(piece, { rebake, regenerate: !rebake }); } + // ========================================= + // EMBEDDED FOCUSED-KEEP MODE + // When this page runs inside the kidlisp.com editor's "Keeps" tab iframe it + // skips the browse UI / preamble and opens straight onto a single piece — + // the one currently open in the editor — for keeping on Tezos. The editor + // and this page are same-origin, so the current piece arrives either via + // postMessage or directly from the shared localStorage seed. + // ========================================= + const IS_EMBEDDED = window.self !== window.top; + let pendingEmbedPayload = null; + let embedHandledKey = null; + + async function openEmbeddedKeep(payload = {}) { + let code = String(payload.code || '').replace(/^\$/, '').trim(); + const source = typeof payload.source === 'string' ? payload.source : ''; + // Dedupe so repeated posts of the same piece don't reopen the modal. + const key = source && source.trim() + ? 'src:' + source.length + ':' + source.trim().slice(0, 48) + : (code ? 'code:' + code : ''); + if (!key || key === embedHandledKey) return; + + // Auto-store the current source first: store-kidlisp dedupes by hash and + // returns the canonical $code, so we always keep exactly what's in the + // editor (even if the cached code was for an older edit). + if (source && source.trim()) { + try { + const headers = { 'Content-Type': 'application/json' }; + if (acToken) headers['Authorization'] = `Bearer ${acToken}`; + const res = await fetch(`${AC_BASE}/api/store-kidlisp`, { + method: 'POST', headers, body: JSON.stringify({ source }), + }); + if (res.ok) { const d = await res.json(); if (d.code) code = d.code; } + } catch (e) { console.warn('[KEEPS embed] store-kidlisp failed:', e?.message || e); } + } + if (!code) return; + + embedHandledKey = key; + document.documentElement.classList.add('has-piece'); + openMintModal({ code }); + } + + if (IS_EMBEDDED) { + window.addEventListener('message', (e) => { + if (e.origin !== window.location.origin) return; + const d = e.data; + if (!d || d.type !== 'keeps:piece') return; + pendingEmbedPayload = d; + openEmbeddedKeep(d); + }); + } + // ========================================= // INIT // ========================================= async function init() { + // Embedded: reveal immediately, skip the preamble + the 14k-item grid, + // and focus on the editor's current piece. + if (IS_EMBEDDED) { + await ensureKeepsConfig(); + loading.style.display = 'none'; + revealUI(); + hideInlineAbout(); + const tabsEl = document.querySelector('.keeps-tabs'); + if (tabsEl) tabsEl.classList.remove('locked'); + initTezosWallet().catch(() => {}); + // Tell the parent editor we're ready for the current piece. + try { window.parent.postMessage({ type: 'keeps:ready' }, window.location.origin); } catch (e) {} + if (pendingEmbedPayload) { + openEmbeddedKeep(pendingEmbedPayload); + } else { + // Fallback: same-origin localStorage seed the editor keeps updated. + const seeded = (localStorage.getItem('kidlisp-current-code') || '').trim(); + if (seeded) openEmbeddedKeep({ code: seeded }); + } + return; + } + setIndexSearchVisible(false); loading.style.display = 'flex'; grid.innerHTML = ''; -- 2.51.2