mirror of
https://github.com/sune-org/store.git
synced 2026-01-13 16:17:58 +00:00
1 line
18 KiB
JSON
1 line
18 KiB
JSON
[{"id":"e1yibwd","name":"Marketplace","pinned":false,"avatar":"data:image/webp;base64,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","url":"gh://sune-org/store@main/marketplace.sune","updatedAt":1757269974397,"settings":{"model":"openai/gpt-5","temperature":"1","top_p":"0.97","top_k":"0","frequency_penalty":"0","presence_penalty":"0","repetition_penalty":"1","min_p":"0","top_a":"0","max_tokens":"","verbosity":"","reasoning_effort":"default","system_prompt":"","html":"<!--\nSune: Marketplace\nVersion: 2.3.4\nDescription: Discover and install new sunes from a configurable catalog URL. URL is saved in SUNE.storage.\n-->\n<section id=\"suneMarketplace\" class=\"p-4 md:p-6 bg-slate-50\">\n <header class=\"mb-4 pb-3 border-b border-slate-200\">\n <div class=\"flex items-center gap-2\">\n <i data-lucide=\"store\" class=\"h-5 w-5 text-slate-700\"></i>\n <h1 class=\"text-xl font-bold text-slate-800\">Sune Store</h1>\n <span class=\"text-[10px] font-mono text-slate-400\">v2.3.3</span>\n </div>\n </header>\n\n <div id=\"mpContent\" class=\"min-h-[120px]\">\n <div class=\"flex justify-center items-center py-12\">\n <div class=\"animate-spin rounded-full h-10 w-10 border-b-2 border-slate-500\"></div>\n </div>\n </div>\n\n <div id=\"mpLogWrap\" class=\"mt-8 hidden\">\n <div class=\"flex justify-between items-center mb-2\">\n <h3 class=\"text-sm font-semibold text-slate-600\">Terminal</h3>\n <div class=\"text-xs text-slate-500\">Avatar diagnostics only</div>\n </div>\n <div id=\"mpLog\" class=\"bg-slate-800 text-white font-mono text-[11px] rounded-lg p-3 h-40 overflow-y-auto\"></div>\n </div>\n\n <div class=\"mt-8 pt-6 border-t border-slate-200\">\n <div class=\"text-center bg-white p-5 rounded-lg border border-slate-200 shadow-sm\">\n <div class=\"flex items-center justify-center gap-2\">\n <i data-lucide=\"git-pull-request-arrow\" class=\"h-5 w-5 text-slate-600\"></i>\n <h3 class=\"text-sm font-semibold text-slate-800\">Contribute your Sune</h3>\n </div>\n <p class=\"mt-2 text-xs text-slate-600 max-w-lg mx-auto\">\n Share your sune with everyone. Open a pull request in\n <a href=\"https://github.com/sune-org/store\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"font-medium text-sky-600 hover:underline\">sune-org/store</a>.\n </p>\n <div class=\"mt-3 flex items-center justify-center gap-2\">\n <a href=\"https://github.com/sune-org/store\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"inline-flex items-center gap-1 px-3 py-1.5 rounded-md bg-slate-900 text-white text-xs hover:bg-slate-800\">\n <i data-lucide=\"external-link\" class=\"h-3.5 w-3.5\"></i><span>Open Repository</span>\n </a>\n </div>\n </div>\n </div>\n\n <div class=\"mt-6 pt-4 border-t border-slate-200\">\n <label for=\"mpCat\" class=\"block text-xs font-medium text-slate-600 mb-1.5\">Catalog URL</label>\n <div class=\"flex flex-col sm:flex-row gap-2\">\n <div class=\"relative flex-1\">\n <i data-lucide=\"link-2\" class=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-slate-400\"></i>\n <input id=\"mpCat\" type=\"url\" inputmode=\"url\" spellcheck=\"false\"\n class=\"peer w-full h-11 pl-9 pr-10 rounded-lg border border-slate-300 bg-white text-sm placeholder:text-slate-400 shadow-sm focus:outline-none focus:ring-4 focus:ring-slate-200/70 focus:border-slate-400 transition\"\n placeholder=\"https://raw.githubusercontent.com/sune-org/store/main/catalog.json (or gh://user/repo@branch/path.json)\"/>\n <span class=\"pointer-events-none hidden sm:flex items-center gap-1 absolute right-2 top-1/2 -translate-y-1/2 text-[10px] text-slate-400\">\n <i data-lucide=\"keyboard\" class=\"h-3.5 w-3.5\"></i>Enter to save\n </span>\n </div>\n <button id=\"mpRefresh\" class=\"h-11 px-3 rounded-lg bg-slate-900 text-white text-xs hover:bg-slate-800 active:scale-[.99] transition inline-flex items-center justify-center gap-1 shadow-sm\">\n <i data-lucide=\"refresh-cw\" class=\"h-3.5 w-3.5\"></i><span>Refresh</span>\n </button>\n </div>\n <p class=\"mt-1 text-[11px] text-slate-500\">Accepts raw https://… or gh://user/repo@branch/path.json. Value is saved per active sune (SUNE.storage).</p>\n </div>\n</section>\n\n<script>\n(()=>{const $=s=>document.querySelector(s),ico=()=>window.lucide?.createIcons(),esc=s=>String(s??'').replace(/[&<>\"]/g,m=>({'&':'&','<':'<','>':'>','\"':'"'}[m])),\nSID=()=>window.SUNE?.id||'default',LSK=k=>`sune-mp-v233:${SID()}:${k}`,\nroot=$(\"#suneMarketplace\"),content=$(\"#mpContent\"),logWrap=$(\"#mpLogWrap\"),logBox=$(\"#mpLog\"),catIn=$(\"#mpCat\"); if(!root)return;\n\n/* Terminal (optional) */\nconst LOG_VISIBLE=false; logWrap.classList.toggle('hidden',!LOG_VISIBLE);\nconst log=(m,t='i')=>{if(!LOG_VISIBLE||!logBox)return;const d=document.createElement('div'),map={i:['»','text-slate-300'],s:['✔','text-green-400'],e:['✖','text-red-400'],w:['⚠','text-yellow-300']},[p,c]=map[t]||map.i;d.className=`flex items-start gap-2 ${c}`;d.innerHTML=`<span class=\"pt-0.5\">${p}</span><span>${esc(m)}</span>`;logBox.appendChild(d);logBox.scrollTop=logBox.scrollHeight};\n\n/* Helpers */\nconst b64url2b64=s=>{let x=(s||'').replace(/-/g,'+').replace(/_/g,'/');while(x&&x.length%4)x+='=';return x};\nconst sniff=b=>b.startsWith('iVBOR')?'image/png':(b.startsWith('/9j/')?'image/jpeg':(b.startsWith('UklGR')?'image/webp':(b.startsWith('R0lGOD')?'image/gif':'application/octet-stream')));\nconst avatarSrc=v=>{if(!v){log('[avatar] none','w');return ''} if(/^data:/.test(v)){const med=v.slice(5,v.indexOf(';'))||'unknown';log(`[avatar] data-uri (type=${med}) len=${v.length}`,'s');return v} const b64=b64url2b64(v),mime=sniff(b64),head=b64.slice(0,12);log(`[avatar] b64url -> mime=${mime} head=${head} len=${b64.length}`,(mime==='application/octet-stream')?'w':'s');return `data:${mime};base64,${b64}`};\nconst ghRaw=u=>{try{if(!u)return u;if(u.startsWith('gh://')){const s=u.slice(5),[hd,...rest]=s.split('/'),[ur,br]=hd.split('@'),[user,repo]=ur.split('/');return `https://raw.githubusercontent.com/${user}/${repo}/${br}/${rest.join('/')}`}return u}catch{return u}};\nconst ghUrlFromRaw=u=>{try{const r=new URL(u);if(r.hostname!=='raw.githubusercontent.com')return'';const[,u1,r1,b,...p]=r.pathname.split('/');return `gh://${u1}/${r1}@${b}/${p.join('/')}`}catch{return''}};\nconst T=s=>(s||'Untitled').replace(/\\.sune$/,'').replace(/[-_]/g,' ').replace(/\\b\\w/g,m=>m.toUpperCase());\nconst installed=()=>new Set((window.SUNE?.list||[]).map(x=>x?.id).filter(Boolean));\n\n/* UI builders */\nconst btn=(it,ins)=>ins?`<button class=\"mt-3 w-full px-3 py-1.5 text-xs font-medium rounded-md bg-slate-100 text-slate-500 cursor-default flex items-center justify-center gap-1\" disabled><i data-lucide=\"check-circle\" class=\"h-4 w-4\"></i><span>Installed</span></button>`:`<button class=\"mpInstall mt-3 w-full px-3 py-1.5 text-xs font-medium rounded-md bg-slate-700 text-white hover:bg-slate-800 active:scale-[.99] transition flex items-center justify-center gap-1\" data-id=\"${esc(it.id||'')}\" data-name=\"${esc(T(it.name))}\" data-raw=\"${esc(it.raw||'')}\"><i data-lucide=\"download-cloud\" class=\"h-4 w-4\"></i><span>Install</span></button>`;\nconst fallbackNode=()=>{const d=document.createElement('div');d.className='h-9 w-9 rounded-md bg-slate-100 border border-slate-200 text-slate-500 flex items-center justify-center';d.innerHTML='<i data-lucide=\"image-off\" class=\"h-4 w-4\"></i>';return d};\nconst card=(it,ins)=>{const src=avatarSrc(it.avatar);return `<article class=\"bg-white border border-slate-200 rounded-lg shadow-sm p-4 flex flex-col\">\n <div class=\"flex items-center gap-3\">\n ${src?`<img alt=\"\" src=\"${src}\" loading=\"lazy\" data-name=\"${esc(T(it.name))}\" class=\"mpAvatar h-9 w-9 rounded-md object-cover bg-slate-100 border border-slate-200\">`:`<div class=\"h-9 w-9 rounded-md bg-slate-100 border border-slate-200 text-slate-500 flex items-center justify-center\"><i data-lucide=\"image-off\" class=\"h-4 w-4\"></i></div>`}\n <div class=\"min-w-0\">\n <h3 class=\"text-sm font-semibold text-slate-800 truncate\">${esc(T(it.name))}</h3>\n <p class=\"text-[11px] text-slate-500 truncate\">${esc(it.description||'Official utility sune.')}</p>\n </div>\n </div>\n <div class=\"mt-2 text-[10px] text-slate-400\">id: <span class=\"font-mono\">${esc(it.id||'(none)')}</span></div>\n ${btn(it,ins)}\n</article>`};\nconst bindAvatars=()=>{ico(); if(!LOG_VISIBLE)return;\n document.querySelectorAll('.mpAvatar').forEach(img=>{\n const nm=img.dataset.name||'(unknown)';\n img.addEventListener('load',()=>log(`[avatar] ${nm}: loaded ${img.naturalWidth}x${img.naturalHeight}`,'s'),{once:true});\n img.addEventListener('error',()=>{const s=String(img.getAttribute('src')||'');log(`[avatar] ${nm}: load error (src head=\"${s.slice(0,32)}\")`,'e');const fb=fallbackNode();img.replaceWith(fb);ico()},{once:true});\n })\n};\nconst render=items=>{const ids=installed(),html=items.map(i=>card(i,i.id?ids.has(i.id):false)).join('');content.innerHTML=`<div class=\"grid gap-3 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">${html}</div>`;bindAvatars()};\n\n/* Per-sune storage for Catalog URL (SUNE.storage) */\nconst DEF='https://raw.githubusercontent.com/sune-org/store/main/catalog.json';\nconst getStorage=()=>Object.assign({},window.SUNE?.storage||{});\nconst getUrl=()=>{try{return (window.SUNE?.storage?.marketplace?.url)||DEF}catch{return DEF}};\nconst setUrl=v=>{const url=(v||'').trim()||DEF,st=getStorage();st.marketplace=Object.assign({},st.marketplace,{url});window.SUNE&&(window.SUNE.storage=st);catIn.value=url};\n\n/* One-time migration from old localStorage cfg -> SUNE.storage */\nconst migrate=()=>{try{const k=LSK('cfg'),raw=localStorage.getItem(k);if(!raw)return;const cfg=JSON.parse(raw||'null');if(cfg?.url&&!window.SUNE?.storage?.marketplace?.url)setUrl(cfg.url);localStorage.removeItem(k)}catch{}};\n\n/* Per-sune cache of fetched catalog list in localStorage (ok to keep) */\nconst loadCat=()=>{try{return JSON.parse(localStorage.getItem(LSK('catalog'))||'null')}catch{return null}};\nconst saveCat=d=>{try{localStorage.setItem(LSK('catalog'),JSON.stringify({t:Date.now(),items:d}))}catch{}};\n\n/* Fetch */\nconst fetchCatalog=async(url)=>{const U=ghRaw(url||DEF),r=await fetch(U,{cache:'no-store'});if(!r.ok)throw new Error('Catalog '+r.status);const raw=await r.json(),items=(Array.isArray(raw)?raw:[]).filter(x=>x&&x.name&&x.raw);saveCat(items);return items};\n\n/* Boot + events */\nconst boot=async(force=false)=>{try{\n const src=getUrl();catIn.value=src;\n const cache=!force&&loadCat(); if(cache?.items)render(cache.items);\n const items=await fetchCatalog(src);render(items)\n}catch(e){content.innerHTML=`<div class=\"text-center py-12 bg-red-50 text-red-700 rounded-lg\"><p>Could not load Marketplace.</p><p class=\"text-xs mt-1\">${esc(e?.message||e)}</p></div>`}finally{ico()}};\n\n$(\"#mpRefresh\")?.addEventListener('click',e=>{e.preventDefault();boot(true)});\ncatIn.addEventListener('keydown',e=>{if(e.key==='Enter'){e.preventDefault();setUrl(catIn.value);boot(true)}});\ncatIn.addEventListener('blur',()=>{setUrl(catIn.value)});\n\ncontent.addEventListener('click',async e=>{const b=e.target.closest('.mpInstall');if(!b)return;\n try{const id=b.dataset.id||'',name=b.dataset.name||'Unknown Sune',raw=b.dataset.raw||'';if(!raw)throw new Error('Missing raw URL');\n b.disabled=true;b.innerHTML=`<i data-lucide=\"loader-circle\" class=\"h-4 w-4 animate-spin\"></i><span>Installing...</span>`;ico();\n const url=ghRaw(raw),res=await fetch(url);if(!res.ok)throw new Error('Fetch '+res.status);\n const arr=await res.json();if(!Array.isArray(arr)||!arr.length)throw new Error('Invalid .sune payload');\n let upd=0;for(const s of arr){if(!s?.id)continue;s.url=s.url||ghUrlFromRaw(url);const ex=(window.SUNE?.list||[]).find(x=>x.id===s.id);if(!ex)window.SUNE.create(s);else if(+s.updatedAt>+ex.updatedAt){Object.assign(ex,s);upd++}}\n if(upd>0)window.SUNE.save?.();window.renderSidebar?.();\n b.outerHTML=`<button class=\"mt-3 w-full px-3 py-1.5 text-xs font-medium rounded-md bg-slate-100 text-slate-500 cursor-default flex items-center justify-center gap-1\" disabled><i data-lucide=\"check-circle\" class=\"h-4 w-4\"></i><span>Installed</span></button>`;ico();\n }catch(err){b.disabled=false;b.innerHTML=`<i data-lucide=\"alert-triangle\" class=\"h-4 w-4\"></i><span>Retry</span>`;ico()}\n});\n\n/* Init */\nmigrate(); catIn.value=getUrl(); boot(); ico();\n})();\n</script>\n","extension_html":"<sune src='https://raw.githubusercontent.com/sune-org/store/refs/heads/main/sync.sune' private />","hide_composer":true},"storage":{"marketplace":{"url":"https://raw.githubusercontent.com/sune-org/store/main/catalog.json"}}}] |