mirror of
https://github.com/sune-org/store.git
synced 2026-01-14 16:48:14 +00:00
1 line
20 KiB
JSON
1 line
20 KiB
JSON
[{"id":"bxvj8jx","name":"Commit","pinned":false,"avatar":"data:image/webp;base64,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","url":"gh://sune-org/store@main/commit.sune","updatedAt":1757190940802,"settings":{"model":"openai/gpt-5","temperature":"","top_p":"","top_k":"","frequency_penalty":"","presence_penalty":"","repetition_penalty":"","min_p":"","top_a":"","max_tokens":"","verbosity":"","reasoning_effort":"default","system_prompt":"","html":"<!-- GitHub File Editor Sune v1.5.1 -->\n<div id=\"gh-editor-sune\" class=\"mx-0 border-b border-gray-200 bg-gray-50 flex flex-col rounded-lg shadow-lg overflow-hidden max-w-2xl w-full max-h-[90vh]\">\n\n <!-- Path List View -->\n <div id=\"gh-path-list-view\" class=\"flex flex-col\">\n <div class=\"flex items-center justify-between gap-3 px-3 sm:px-4 py-2 border-b border-gray-200 bg-white/80 backdrop-blur-sm shrink-0\">\n <div class=\"flex items-center gap-3\">\n <i data-lucide=\"github\" class=\"h-5 w-5 text-gray-500 shrink-0\"></i>\n <h2 class=\"font-medium text-gray-800\">Repository Paths</h2>\n </div>\n <button id=\"gh-add-path-btn\" class=\"inline-flex items-center justify-center h-8 w-8 shrink-0 rounded-lg bg-gray-100 text-gray-800 shadow-sm hover:bg-gray-200 active:scale-[.98] transition\" title=\"Add New Path\">\n <i data-lucide=\"plus\" class=\"h-4 w-4\"></i>\n </button>\n </div>\n <div id=\"gh-path-container\" class=\"p-2 sm:p-3 space-y-2 overflow-y-auto max-h-[75vh]\">\n <!-- Dynamic path inputs will be injected here -->\n </div>\n </div>\n\n <!-- Editor View -->\n <div id=\"gh-editor-view\" class=\"hidden flex-col\">\n <!-- Toolbar -->\n <div class=\"flex items-center gap-3 px-3 sm:px-4 py-2 border-b border-gray-200 bg-white/80 backdrop-blur-sm shrink-0\">\n <button id=\"gh-back-btn\" class=\"p-1.5 -ml-1.5 rounded-md hover:bg-gray-200 text-gray-500 hover:text-gray-800 transition-colors\" title=\"Back to Paths\">\n <i data-lucide=\"arrow-left\" class=\"h-5 w-5 shrink-0\"></i>\n </button>\n <input type=\"text\" id=\"ghPathInput\" placeholder=\"owner/repo/path/to/file.ext@branch\" class=\"flex-1 w-full bg-transparent border-none focus:ring-0 p-0 text-sm placeholder:text-gray-400\" autocomplete=\"off\" spellcheck=\"false\" readonly/>\n <button id=\"ghCommitBtn\" class=\"inline-flex items-center justify-center gap-1.5 h-8 px-3 shrink-0 rounded-lg bg-green-600 text-white shadow-sm hover:bg-green-700 active:scale-[.98] transition text-sm font-medium\" title=\"Commit Changes\">\n <i data-lucide=\"check-circle\" class=\"h-4 w-4\"></i>\n <span class=\"hidden sm:inline\">Commit</span>\n </button>\n </div>\n\n <!-- Search Bar -->\n <div id=\"ghSearchContainer\" class=\"hidden flex items-center gap-2 px-3 sm:px-4 py-1.5 border-b border-gray-200 bg-white shrink-0\">\n <i data-lucide=\"search\" class=\"h-4 w-4 text-gray-500 shrink-0\"></i>\n <input type=\"text\" id=\"ghSearchInput\" placeholder=\"Find\" class=\"flex-1 w-full bg-transparent border-none focus:ring-0 p-0 text-sm placeholder:text-gray-400\" autocomplete=\"off\" spellcheck=\"false\" />\n <span id=\"ghSearchCount\" class=\"text-xs text-gray-500 shrink-0 tabular-nums\">0/0</span>\n <div class=\"flex items-center\">\n <button id=\"ghSearchPrevBtn\" class=\"p-1 rounded-md hover:bg-gray-200 text-gray-500 hover:text-gray-800 transition-colors\" title=\"Previous (Shift+Enter)\"><i data-lucide=\"chevron-up\" class=\"h-5 w-5\"></i></button>\n <button id=\"ghSearchNextBtn\" class=\"p-1 rounded-md hover:bg-gray-200 text-gray-500 hover:text-gray-800 transition-colors\" title=\"Next (Enter)\"><i data-lucide=\"chevron-down\" class=\"h-5 w-5\"></i></button>\n <button id=\"ghSearchCloseBtn\" class=\"p-1 rounded-md hover:bg-gray-200 text-gray-500 hover:text-gray-800 transition-colors\" title=\"Close (Esc)\"><i data-lucide=\"x\" class=\"h-5 w-5\"></i></button>\n </div>\n </div>\n\n <!-- Editor -->\n <div class=\"bg-white flex-1 relative\">\n <pre id=\"ghFileEditor\" class=\"w-full h-[65vh] p-3 overflow-auto font-mono text-[12px] leading-5 focus:outline-none [white-space:pre!important]\" contenteditable=\"plaintext-only\" spellcheck=\"false\"></pre>\n </div>\n\n <!-- Footer -->\n <div class=\"flex items-center justify-between gap-4 px-3 sm:px-4 py-1 border-t border-gray-200 bg-white/80 backdrop-blur-sm text-xs shrink-0\">\n <div class=\"flex items-center gap-1\">\n <button id=\"ghSearchBtn\" title=\"Search (Ctrl+F)\" class=\"p-1.5 rounded-md hover:bg-gray-200 text-gray-500 hover:text-gray-800 transition-colors\"><i data-lucide=\"search\" class=\"h-4 w-4\"></i></button>\n <button id=\"ghCopyBtn\" title=\"Copy\" class=\"p-1.5 rounded-md hover:bg-gray-200 text-gray-500 hover:text-gray-800 transition-colors\"><i data-lucide=\"copy\" class=\"h-4 w-4\"></i></button>\n <button id=\"ghCutBtn\" title=\"Cut\" class=\"p-1.5 rounded-md hover:bg-gray-200 text-gray-500 hover:text-gray-800 transition-colors\"><i data-lucide=\"scissors\" class=\"h-4 w-4\"></i></button>\n <button id=\"ghPasteBtn\" title=\"Paste\" class=\"p-1.5 rounded-md hover:bg-gray-200 text-gray-500 hover:text-gray-800 transition-colors\"><i data-lucide=\"clipboard-paste\" class=\"h-4 w-4\"></i></button>\n </div>\n <div class=\"flex items-center gap-3 text-gray-500 truncate\">\n <span id=\"ghLangBadge\" class=\"font-medium\">Plain Text</span>\n <div id=\"ghStatus\" class=\"h-4 truncate\"></div>\n </div>\n <span class=\"text-gray-400\">v1.5.1</span>\n </div>\n </div>\n</div>\n\n<script>\n(async () => {\n // Elements\n const getEl = id => document.getElementById(id);\n const [\n pathListView, editorView, pathContainer, addPathBtn, backBtn, pathInput, commitBtn,\n fileEditorEl, searchBtn, copyBtn, cutBtn, pasteBtn, statusEl, langBadge,\n searchContainer, searchInput, searchCount, searchPrevBtn, searchNextBtn, searchCloseBtn\n ] = [\n 'gh-path-list-view', 'gh-editor-view', 'gh-path-container', 'gh-add-path-btn', 'gh-back-btn',\n 'ghPathInput', 'ghCommitBtn', 'ghFileEditor', 'ghSearchBtn', 'ghCopyBtn', 'ghCutBtn',\n 'ghPasteBtn', 'ghStatus', 'ghLangBadge', 'ghSearchContainer', 'ghSearchInput',\n 'ghSearchCount', 'ghSearchPrevBtn', 'ghSearchNextBtn', 'ghSearchCloseBtn'\n ].map(getEl);\n\n // State\n const S = {paths: [], o: null, r: null, p: null, b: null, sha: null, jar: null, t: null, l: null, search: {q: '', m: [], c: -1, oh: null}};\n const getLsKey = () => `sune_gh_paths_${window.SUNE?.id||'default'}`;\n let debounceTimer;\n\n // Helpers\n const setStatus=(msg,isErr=false,el=statusEl)=>{el.textContent=msg||'';el.className=`h-4 truncate ${isErr?'text-red-600':'text-gray-500'}`;if(!isErr&&msg)setTimeout(()=>el.textContent===msg&&(el.textContent=''),4e3);};\n const fmtChars = c => c < 1e3 ? `${c} chars` : `${(c/1e3).toFixed(1).replace(/\\.0$/, '')}k chars`;\n const setLoading=(btn,loading)=>{btn.disabled=loading;const i=btn.querySelector('i');if(!i)return;if(loading){if(!i.dataset.og)i.dataset.og=i.dataset.lucide;i.dataset.lucide='loader-circle';i.classList.add('animate-spin');}else{if(i.dataset.og)i.dataset.lucide=i.dataset.og;i.classList.remove('animate-spin');}lucide?.createIcons();};\n const parsePath = i => String(i).trim().match(/^([\\w.-]+)\\/([\\w.-]+)\\/(.+?)@([\\w.-]+)$/);\n const detectLang=p=>(p=String(p||'').toLowerCase(),{lang:/\\.html?$/.test(p)?'xml':/\\.m?js|cjs$/.test(p)?'javascript':/\\.json$/.test(p)?'json':/\\.css$/.test(p)?'css':/\\.md$/.test(p)?'markdown':'plaintext',label:/\\.html?$/.test(p)?'HTML':/\\.m?js|cjs$/.test(p)?'JavaScript':/\\.json$/.test(p)?'JSON':/\\.css$/.test(p)?'CSS':/\\.md$/.test(p)?'Markdown':'Plain Text'});\n const enc=s=>btoa(unescape(encodeURIComponent(s)));\n const dec=b=>decodeURIComponent(escape(atob(b)));\n const esc=s=>s.replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''})[c]);\n const switchView=toEditor=>{pathListView.classList.toggle('hidden',toEditor);editorView.classList.toggle('hidden',!toEditor);editorView.classList.toggle('flex',toEditor);};\n\n // Path Management\n const savePaths = () => { try { localStorage.setItem(getLsKey(), JSON.stringify(S.paths)); } catch (e) { console.error(\"Sune: LS save failed\", e); } };\n const loadPaths = () => { try { S.paths = JSON.parse(localStorage.getItem(getLsKey())) || []; } catch (e) { S.paths = []; console.error(\"Sune: LS load failed\", e); } };\n const renderPaths = () => {\n pathContainer.innerHTML = '';\n if (S.paths.length === 0) {\n pathContainer.innerHTML = `<div class=\"text-center text-sm text-gray-500 py-8\">No paths saved. Click the '+' button to add one.</div>`;\n return;\n }\n S.paths.forEach((path, i) => {\n const row = document.createElement('div');\n row.className = 'flex items-center gap-2';\n row.innerHTML = `\n <input type=\"text\" value=\"${esc(path)}\" placeholder=\"owner/repo/path@branch\" class=\"flex-1 w-full bg-white border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm px-2.5 py-1.5 shadow-sm\" autocomplete=\"off\" spellcheck=\"false\" />\n <button class=\"fetch-btn inline-flex items-center justify-center h-8 w-8 shrink-0 rounded-lg bg-gray-100 text-gray-800 shadow-sm hover:bg-gray-200 active:scale-[.98] transition\" title=\"Fetch File\"><i data-lucide=\"download-cloud\" class=\"h-4 w-4\"></i></button>\n <button class=\"delete-btn inline-flex items-center justify-center h-8 w-8 shrink-0 rounded-lg bg-red-50 text-red-600 hover:bg-red-100 active:scale-[.98] transition\" title=\"Delete Path\"><i data-lucide=\"trash-2\" class=\"h-4 w-4\"></i></button>\n `;\n const input = row.querySelector('input');\n input.addEventListener('input', () => { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { S.paths[i] = input.value; savePaths(); }, 400); });\n input.addEventListener('keydown', e => { if(e.key === 'Enter') { e.preventDefault(); row.querySelector('.fetch-btn').click(); }});\n row.querySelector('.fetch-btn').addEventListener('click', e => fetchHandler(input.value, e.currentTarget));\n row.querySelector('.delete-btn').addEventListener('click', () => { S.paths.splice(i, 1); savePaths(); renderPaths(); });\n pathContainer.appendChild(row);\n });\n lucide?.createIcons();\n };\n\n // Search\n const updateSearchUI=()=>{const{m,c}=S.search;const t=m.length;searchCount.textContent=`${t>0&&c>-1?c+1:0}/${t}`;searchPrevBtn.disabled=t<1;searchNextBtn.disabled=t<1;};\n const navigateSearch=dir=>{const{m}=S.search;if(!m.length)return;if(S.search.oh)S.search.oh.classList.remove('ring-2','ring-orange-500');let i=S.search.c+dir;if(i>=m.length)i=0;if(i<0)i=m.length-1;S.search.c=i;const el=m[i];el.classList.add('ring-2','ring-orange-500');el.scrollIntoView({behavior:'smooth',block:'center'});S.search.oh=el;updateSearchUI();};\n const handleSearchInput=()=>{const q=searchInput.value;if(q===S.search.q)return;if(S.search.oh)S.search.oh.classList.remove('ring-2','ring-orange-500');S.search.q=q;S.search.c=-1;S.jar.updateCode(S.jar.toString());};\n const openSearch=()=>{searchContainer.classList.replace('hidden','flex');searchInput.focus();searchInput.select();if(searchInput.value)handleSearchInput();};\n const closeSearch=()=>{searchContainer.classList.replace('flex','hidden');if(S.search.oh)S.search.oh.classList.remove('ring-2','ring-orange-500');S.search.q='';searchInput.value='';S.jar.updateCode(S.jar.toString());S.search.m=[];S.search.c=-1;S.search.oh=null;updateSearchUI();};\n\n // Editor Setup\n const{CodeJar:CJ}=await import('https://medv.io/codejar/codejar.js');\n const doHighlight=ed=>{const c=ed.textContent;if(S.search.q){const q=S.search.q.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g,\"\\\\$&\");ed.innerHTML=esc(c).replace(new RegExp(`(${q})`,'gi'),`<mark class=\"gh-search-match bg-yellow-200 rounded-sm\">$1</mark>`);S.search.m=Array.from(ed.querySelectorAll('.gh-search-match'));updateSearchUI();}else if(window.hljs&&S.l&&hljs.getLanguage(S.l)){ed.innerHTML=hljs.highlight(c,{language:S.l,ignoreIllegals:true}).value;}else{ed.textContent=c;}};\n S.jar=CJ(fileEditorEl,doHighlight,{tab:' '});\n\n // API Handlers\n const fetchHandler = async (pathValue, btn) => {\n setStatus();\n S.t = window.USER?.PAT || '';\n if (!S.t) return setStatus('Error: GitHub token not found.', true);\n const m = parsePath(pathValue);\n if (!m) return setStatus('Error: Invalid path. Use: owner/repo/path@branch', true, btn.parentElement.querySelector('input'));\n [ , S.o, S.r, S.p, S.b] = m;\n const {lang, label} = detectLang(S.p);\n S.l = lang;\n langBadge.textContent = label;\n const url = `https://api.github.com/repos/${S.o}/${S.r}/contents/${encodeURIComponent(S.p).replace(/%2F/g, '/')}?ref=${encodeURIComponent(S.b)}`;\n setStatus('Fetching...');\n setLoading(btn, true);\n try {\n const r = await fetch(url, {headers: {'Authorization': `Bearer ${S.t}`, 'Accept': 'application/vnd.github.v3+json'}});\n const d = await r.json();\n if (!r.ok) throw new Error(d.message || 'Failed to fetch file.');\n if (d.type !== 'file') throw new Error('Path is not a file.');\n S.sha = d.sha;\n const content = dec(d.content || '');\n S.jar.updateCode(content);\n pathInput.value = pathValue;\n setStatus(`Loaded ${S.p} (${fmtChars(content.length)}).`);\n switchView(true);\n } catch (err) {\n setStatus(`Error: ${err.message}`, true);\n S.sha = null;\n } finally {\n setLoading(btn, false);\n }\n };\n const commitHandler=async()=>{setStatus();const{o,r,p,b,sha,t,jar}=S;if(!t)return setStatus('Error: GitHub token not found.',true);if(!o||!sha)return setStatus('Error: Fetch a file first.',true);const msg=prompt(`Enter commit message for:\\n${p}`,`Update ${p.split('/').pop()||p}`);if(msg===null)return setStatus('Commit cancelled.');if(!msg.trim())return setStatus('Error: Commit message cannot be empty.',true);const newContent=jar.toString();const url=`https://api.github.com/repos/${o}/${r}/contents/${encodeURIComponent(p).replace(/%2F/g,'/')}`;setStatus('Committing…');setLoading(commitBtn,true);try{const res=await fetch(url,{method:'PUT',headers:{'Authorization':`Bearer ${t}`,'Accept':'application/vnd.github.v3+json','Content-Type':'application/json'},body:JSON.stringify({message:msg.trim(),content:enc(newContent),sha,branch:b})});const data=await res.json();if(!res.ok)throw new Error(data.message||'Failed to commit.');S.sha=data.content.sha;setStatus('Committed successfully!');}catch(err){setStatus(`Error: ${err.message}`,true);}finally{setLoading(commitBtn,false);}};\n\n // Clipboard\n const clip=async op=>{try{if(op==='cut'||op==='copy'){const text=S.jar.toString();if(!text)return;await navigator.clipboard.writeText(text);if(op==='cut')S.jar.updateCode('');setStatus(op==='cut'?'Cut.':'Copied.');}else if(op==='paste'){const txt=await navigator.clipboard.readText();S.jar.updateCode(S.jar.toString()+txt);setStatus(`Pasted (${fmtChars(txt.length)}).`);}}catch{setStatus(`${op.charAt(0).toUpperCase()+op.slice(1)} failed.`,true);}};\n\n // Listeners\n addPathBtn.addEventListener('click', () => { S.paths.push(''); savePaths(); renderPaths(); pathContainer.lastChild?.querySelector('input')?.focus(); });\n backBtn.addEventListener('click', () => { S.jar.updateCode(''); setStatus(); langBadge.textContent='Plain Text'; switchView(false); });\n commitBtn.addEventListener('click', commitHandler);\n copyBtn.addEventListener('click', () => clip('copy'));\n cutBtn.addEventListener('click', () => clip('cut'));\n pasteBtn.addEventListener('click', () => clip('paste'));\n searchBtn.addEventListener('click', openSearch);\n searchCloseBtn.addEventListener('click', closeSearch);\n searchNextBtn.addEventListener('click', () => navigateSearch(1));\n searchPrevBtn.addEventListener('click', () => navigateSearch(-1));\n searchInput.addEventListener('input', handleSearchInput);\n searchInput.addEventListener('keydown', e => { if (e.key === 'Enter') { e.preventDefault(); navigateSearch(e.shiftKey ? -1 : 1); } if (e.key === 'Escape') closeSearch(); });\n document.getElementById('gh-editor-sune').addEventListener('keydown', e => { if (e.ctrlKey && e.key === 'f') { e.preventDefault(); openSearch(); } });\n\n // Init\n loadPaths();\n renderPaths();\n lucide?.createIcons();\n})();\n</script>\n","extension_html":"<sune src='https://raw.githubusercontent.com/sune-org/store/refs/heads/main/sync.sune' private />"},"storage":{}}] |