Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

keeps: permalinks for the mint modal + link Token #N to objkt

/kept/\$code and /unkept/\$code now open the respective gallery tab and
auto-open the mint modal for that piece. Uses the existing pending-deep-link
pattern (pendingMintModalCode) and resolves against allCodes once the index
renders. openMintModal pushes history entry, closeMintModal restores tab
root, popstate keeps modal state in sync with nav. Distinct from the bare
/\$code market-detail permalink so both behaviors coexist.

Also lands the small pre-existing UX tweak in the kept index: the Token #N
chip is now an anchor pointing at objkt.com/tokens/{contract}/{tokenId},
with an underline-on-hover style and event.stopPropagation so clicking it
doesn't also open the mint modal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+74 -11
+74 -11
system/public/kidlisp.com/keeps.html
··· 2310 2310 font-size: 12px; 2311 2311 color: #8658e2; 2312 2312 margin-left: auto; 2313 + text-decoration: none; 2313 2314 } 2315 + a.keeps-row-token:hover { text-decoration: underline; } 2314 2316 .keeps-row-hits { 2315 2317 font-family: var(--font-mono); 2316 2318 font-size: 12px; ··· 3962 3964 ? (knownOwner.alias || knownOwner.address.slice(0, 8) + '…' + knownOwner.address.slice(-4)) 3963 3965 : '…'; 3964 3966 const ownerHref = knownOwner?.address ? `https://objkt.com/profile/${knownOwner.address}` : null; 3967 + const objktTokenHref = (activeKeepInfo?.contractAddress && tokenId != null) 3968 + ? `${getObjktBase(activeKeepInfo.network)}/tokens/${activeKeepInfo.contractAddress}/${tokenId}` 3969 + : null; 3965 3970 3966 3971 row.innerHTML = ` 3967 3972 <img class="keeps-row-thumb" alt="$${entry.code}" loading="lazy" /> 3968 3973 <a class="keeps-row-code" href="${KL_BASE}/$${entry.code}" title="Edit in KidLisp.com" onclick="event.stopPropagation()">$${entry.code}</a> 3969 3974 <span class="keeps-row-handle">${escapeHtml(handle)}</span> 3970 3975 <span class="keeps-row-hits">${hits.toLocaleString()} hit${hits !== 1 ? 's' : ''}</span> 3971 - ${tokenId != null ? `<span class="keeps-row-token">Token #${tokenId}</span>` : ''} 3976 + ${tokenId != null ? (objktTokenHref 3977 + ? `<a class="keeps-row-token" href="${objktTokenHref}" target="_blank" rel="noopener" title="View token on objkt.com" onclick="event.stopPropagation()">Token #${tokenId}</a>` 3978 + : `<span class="keeps-row-token">Token #${tokenId}</span>`) : ''} 3972 3979 ${tokenId != null ? `<span class="keeps-row-owner" id="row-owner-${tokenId}">${ownerHref ? `<a class="keeps-row-owner-link" href="${ownerHref}" target="_blank" rel="noopener" onclick="event.stopPropagation()">${escapeHtml(ownerLabel)}</a>` : ownerLabel}</span>` : ''} 3973 3980 ${keptTimeStr ? `<span class="keeps-row-time" title="Kept ${keptAt?.toLocaleString() || ''}">kept ${keptTimeStr}</span>` : ''} 3974 3981 ${createdTimeStr ? `<span class="keeps-row-time" title="Created ${createdAt?.toLocaleString() || ''}">made ${createdTimeStr}</span>` : ''}`; ··· 4083 4090 } 4084 4091 4085 4092 grid.appendChild(frag); 4093 + 4094 + // Deep-link: if the URL points at /kept/$code or /unkept/$code, open 4095 + // the mint modal for that piece once we have the entry in allCodes. 4096 + if (pendingMintModalCode) { 4097 + const match = allCodes.find(e => e.code === pendingMintModalCode); 4098 + if (match) { 4099 + const code = pendingMintModalCode; 4100 + pendingMintModalCode = null; 4101 + // Defer to next tick so the current render pass completes first. 4102 + setTimeout(() => openMintModal(match), 0); 4103 + } 4104 + } 4086 4105 } 4087 4106 4088 4107 function renderPaginationControls() { ··· 4315 4334 // KEEP DETAIL MODAL (permalink) 4316 4335 // ========================================= 4317 4336 let detailSortedEntries = []; // populated by renderMarket for index-based lookup 4318 - let pendingDeepLinkCode = null; // set on load if URL is /$code 4337 + let pendingDeepLinkCode = null; // set on load if URL is /$code (detail modal) 4338 + let pendingMintModalCode = null; // set on load if URL is /kept/$code or /unkept/$code 4319 4339 const detailOverlay = document.getElementById('keep-detail-overlay'); 4320 4340 const detailContent = document.getElementById('keep-detail-content'); 4321 4341 const detailTitle = document.getElementById('keep-detail-title'); ··· 4867 4887 const pageParam = new URLSearchParams(location.search).get('page'); 4868 4888 const page = pageParam ? Math.max(0, parseInt(pageParam, 10) - 1) : 0; 4869 4889 if (isKeepsDomain) { 4870 - const seg = location.pathname.replace(/^\/+/, '').split('/')[0]; 4871 - // Redirect /buy, /market, and /$code permalinks to buy.kidlisp.com 4872 - if (seg === 'buy' || seg === 'market') { 4890 + const parts = location.pathname.replace(/^\/+/, '').split('/'); 4891 + const seg1 = parts[0] || ''; 4892 + const seg2 = parts[1] || ''; 4893 + // Redirect /buy and /market to buy.kidlisp.com 4894 + if (seg1 === 'buy' || seg1 === 'market') { 4873 4895 window.location.href = 'https://buy.kidlisp.com'; 4874 4896 return { tab: 'unkept', code: null, page: 0 }; 4875 4897 } 4876 - if (seg.startsWith('$') && seg.length > 1) { 4877 - return { tab: 'unkept', code: seg.slice(1), page }; 4898 + // /kept/$code and /unkept/$code → gallery tab with mint modal open 4899 + // for that piece. Distinct from the bare /$code market-detail permalink. 4900 + if ((seg1 === 'kept' || seg1 === 'unkept') && seg2.startsWith('$') && seg2.length > 1) { 4901 + return { tab: seg1, code: seg2.slice(1), page, mintOpen: true }; 4902 + } 4903 + if (seg1.startsWith('$') && seg1.length > 1) { 4904 + return { tab: 'unkept', code: seg1.slice(1), page }; 4878 4905 } 4879 - const tab = seg === 'kept' ? 'kept' : 'unkept'; 4906 + const tab = seg1 === 'kept' ? 'kept' : 'unkept'; 4880 4907 return { tab, code: null, page }; 4881 4908 } else { 4882 4909 const hash = location.hash.replace(/^#/, ''); ··· 4889 4916 } 4890 4917 } 4891 4918 const initialRoute = tabFromLocation(); 4892 - if (initialRoute.code) pendingDeepLinkCode = initialRoute.code; 4919 + if (initialRoute.mintOpen && initialRoute.code) { 4920 + pendingMintModalCode = initialRoute.code; 4921 + } else if (initialRoute.code) { 4922 + pendingDeepLinkCode = initialRoute.code; 4923 + } 4893 4924 switchTab(initialRoute.tab); 4894 4925 4895 4926 // Show wallet button immediately; check if already connected ··· 4899 4930 // Handle browser back/forward 4900 4931 window.addEventListener('popstate', () => { 4901 4932 const route = tabFromLocation(); 4902 - // Close detail modal if navigating away from a $code permalink 4933 + const mintOverlayEl = document.getElementById('mint-overlay'); 4934 + const mintOpenNow = mintOverlayEl?.classList.contains('open'); 4935 + // Close modals whose permalink context no longer matches the route. 4903 4936 if (!route.code && detailOverlay.classList.contains('open')) { 4904 4937 detailOverlay.classList.remove('open'); 4905 4938 document.body.style.overflow = ''; 4906 4939 } 4940 + if (!route.mintOpen && mintOpenNow) { 4941 + closeMintModal(); 4942 + } 4907 4943 switchTab(route.tab); 4908 - if (route.code) openKeepDetailByCode(route.code); 4944 + // Open the correct modal for the new URL. 4945 + if (route.mintOpen && route.code) { 4946 + const match = allCodes.find(e => e.code === route.code); 4947 + if (match) openMintModal(match); 4948 + else pendingMintModalCode = route.code; 4949 + } else if (route.code) { 4950 + openKeepDetailByCode(route.code); 4951 + } 4909 4952 if (route.page > 0 && route.page < totalPages) { 4910 4953 goToPage(route.page, { pushState: false }); 4911 4954 } ··· 5987 6030 clearTrackLog(); 5988 6031 document.getElementById('mint-overlay').classList.add('open'); 5989 6032 document.body.style.overflow = 'hidden'; 6033 + 6034 + // Permalink: reflect the open mint modal in the URL so sharing /kept/$code 6035 + // or /unkept/$code reopens this exact view. Only push when the URL isn't 6036 + // already at this target (avoids duplicate history entries when the modal 6037 + // was opened via the initial-route deep link). 6038 + if (isKeepsDomain && entry?.code) { 6039 + const tabPath = activeTab === 'kept' ? '/kept' : activeTab === 'unkept' ? '/unkept' : ''; 6040 + const targetPath = `${tabPath}/$${entry.code}`; 6041 + if (location.pathname !== targetPath) { 6042 + history.pushState({ tab: activeTab, code: entry.code, mintOpen: true }, '', targetPath); 6043 + } 6044 + } 5990 6045 } 5991 6046 5992 6047 function beginMintFlow() { ··· 6002 6057 mintAutoSyncAfterRebake = false; 6003 6058 document.getElementById('mint-overlay').classList.remove('open'); 6004 6059 document.body.style.overflow = ''; 6060 + 6061 + // Permalink: restore URL to tab root when the modal closes. Matches 6062 + // /kept/$code or /unkept/$code (and bare /$code inside unkept context) 6063 + // so direct-link exit → back button feels right. 6064 + if (isKeepsDomain && /^\/(kept|unkept)?\/?\$[^/]+$/.test(location.pathname)) { 6065 + const tabPath = activeTab === 'kept' ? '/kept' : '/'; 6066 + history.pushState({ tab: activeTab }, '', tabPath); 6067 + } 6005 6068 if (mintPollTimer) { clearInterval(mintPollTimer); mintPollTimer = null; } 6006 6069 if (mintAbortController) { 6007 6070 mintAbortController.abort();