Files
store/github-utilities/github.sune

1 line
40 KiB
JSON

[{"id":"37uz2i8","name":"GitHub","pinned":true,"avatar":"data:image/webp;base64,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","url":"gh://sune-org/store/github-utilities/github.sune","updatedAt":1762743858534,"settings":{"model":"","temperature":"","top_p":"","top_k":"","frequency_penalty":"","repetition_penalty":"","min_p":"","top_a":"","verbosity":"","reasoning_effort":"default","system_prompt":"","html":"<div id=\"ghRepoBrowserSune\" class=\"relative px-0 bg-white/80 backdrop-blur-xl rounded-lg border border-gray-200 shadow-sm font-sans overflow-hidden\">\n <!-- Background Blob -->\n <div class=\"absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-96 h-96 bg-gradient-to-tr from-cyan-200 via-blue-300 to-purple-300 rounded-full opacity-30 filter blur-3xl -z-10\" aria-hidden=\"true\"></div>\n\n <!-- Header -->\n <div class=\"flex justify-between items-center p-4 pb-0\">\n <h2 id=\"viewTitle\" class=\"font-bold text-lg text-gray-800 transition-all\">GitHub Repositories</h2>\n <span class=\"text-xs text-gray-400\">v-0.8.1</span>\n </div>\n\n <!-- View: Repository List -->\n <div id=\"repoListView\" class=\"p-4\">\n <div id=\"repoList\" class=\"space-y-2 mb-3\"></div>\n <button id=\"addRepoBtn\" class=\"w-full flex items-center justify-center gap-2 px-4 py-2 text-xs text-gray-600 bg-gray-100 rounded-lg hover:bg-gray-200 active:scale-[0.98] transition-all\">\n <i data-lucide=\"plus\" class=\"h-4 w-4\"></i>\n <span>Add Repository</span>\n </button>\n </div>\n\n <!-- View: Repository Browser -->\n <div id=\"repoBrowserView\" class=\"hidden\">\n <div class=\"flex items-center mb-3 pt-2 px-4\">\n <button id=\"backBtn\" class=\"flex items-center gap-1.5 px-3 py-1 text-sm text-gray-600 rounded-lg hover:bg-gray-100 active:scale-[0.98] transition-all\">\n <i data-lucide=\"arrow-left\" class=\"h-4 w-4\"></i>\n <span>Back</span>\n </button>\n </div>\n\n <div id=\"fileBrowserContainer\" class=\"px-4 pb-4\">\n <div class=\"flex items-center justify-between gap-2 mb-2\">\n <div id=\"breadcrumbs\" class=\"flex-1 flex items-center gap-1.5 text-sm text-gray-500 overflow-x-auto pb-2 -mr-4 pr-4\"></div>\n <button id=\"createNewFileBtn\" class=\"flex-shrink-0 flex items-center gap-1.5 px-3 py-1.5 text-xs font-semibold text-white bg-green-600 rounded-lg hover:bg-green-700 active:scale-[0.98] transition-all\">\n <i data-lucide=\"plus\" class=\"h-3 w-3\"></i>\n <span>New File</span>\n </button>\n </div>\n <div id=\"fileTree\" class=\"space-y-1 max-h-[60vh] overflow-y-auto border border-gray-200 rounded-lg p-2 bg-gray-50/50\"></div>\n </div>\n\n <div id=\"fileHistoryContainer\" class=\"hidden px-4 pb-4\">\n <div id=\"fileHistoryHeader\" class=\"flex justify-between items-center text-sm font-medium text-gray-700 mb-2 truncate\">\n <span id=\"fileHistoryTitle\" class=\"truncate\"></span>\n <div class=\"flex items-center gap-2\">\n <button id=\"deleteFileBtn\" class=\"inline-flex items-center justify-center gap-1.5 h-8 px-3 shrink-0 rounded-lg bg-red-600 text-white shadow-sm hover:bg-red-700 active:scale-[0.98] transition text-xs font-medium\" title=\"Delete this file\">\n <i data-lucide=\"trash-2\" class=\"h-3 w-3\"></i>\n <span>Delete</span>\n </button>\n <button id=\"commitLatestBtn\" 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-[0.98] transition text-xs font-medium\" title=\"Edit latest version and commit\">\n <i data-lucide=\"edit-3\" class=\"h-3 w-3\"></i>\n <span>Commit</span>\n </button>\n </div>\n </div>\n <div id=\"commitList\" class=\"space-y-2 max-h-[60vh] overflow-y-auto -mr-2 pr-2\"></div>\n </div>\n\n <!-- Container for the Editor Sune -->\n <div id=\"editorContainer\" class=\"hidden\">\n <!-- GitHub File Editor Sune v1.5.6 (Integrated) -->\n <div id=\"gh-editor-sune\" class=\"mx-0 border-t border-gray-200 bg-gray-50 flex flex-col shadow-inner overflow-hidden w-full max-h-[80vh]\">\n <!-- Editor View -->\n <div id=\"gh-editor-view\" class=\"flex flex-col\">\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 History\">\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@branch/path/to/file.ext\" class=\"flex-1 w-full bg-transparent border-none focus:ring-0 p-0 text-sm placeholder:text-gray-400\"/>\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-[0.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 <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\"/>\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\">\n <i data-lucide=\"chevron-up\" class=\"h-5 w-5\"></i>\n </button>\n <button id=\"ghSearchNextBtn\" class=\"p-1 rounded-md hover:bg-gray-200 text-gray-500 hover:text-gray-800 transition-colors\">\n <i data-lucide=\"chevron-down\" class=\"h-5 w-5\"></i>\n </button>\n <button id=\"ghSearchCloseBtn\" class=\"p-1 rounded-md hover:bg-gray-200 text-gray-500 hover:text-gray-800 transition-colors\">\n <i data-lucide=\"x\" class=\"h-5 w-5\"></i>\n </button>\n </div>\n </div>\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 <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\">\n <i data-lucide=\"search\" class=\"h-4 w-4\"></i>\n </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\">\n <i data-lucide=\"copy\" class=\"h-4 w-4\"></i>\n </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\">\n <i data-lucide=\"scissors\" class=\"h-4 w-4\"></i>\n </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\">\n <i data-lucide=\"clipboard-paste\" class=\"h-4 w-4\"></i>\n </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.6</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n\n<script>\n(async function(){\n const sune=document.getElementById('ghRepoBrowserSune');\n if(!sune||sune.dataset.initialized)return;\n sune.dataset.initialized='true';\n\n const SUNE_ID_B=window.SUNE?.id||'gh-repo-browser-sune';\n const CACHE_KEY_B=`${SUNE_ID_B}_gh_repos`;\n let currentRepoInfo={},pathHistory=[],browserSubView='tree',currentFilePath='';\n\n const viewTitle=sune.querySelector('#viewTitle'),\n repoListView=sune.querySelector('#repoListView'),\n repoBrowserView=sune.querySelector('#repoBrowserView'),\n repoList=sune.querySelector('#repoList'),\n addRepoBtn=sune.querySelector('#addRepoBtn'),\n backBtn=sune.querySelector('#backBtn'),\n fileBrowserContainer=sune.querySelector('#fileBrowserContainer'),\n fileHistoryContainer=sune.querySelector('#fileHistoryContainer'),\n editorContainer=sune.querySelector('#editorContainer'),\n breadcrumbs=sune.querySelector('#breadcrumbs'),\n fileTree=sune.querySelector('#fileTree'),\n fileHistoryHeader=sune.querySelector('#fileHistoryHeader'),\n fileHistoryTitle=sune.querySelector('#fileHistoryTitle'),\n commitLatestBtn=sune.querySelector('#commitLatestBtn'),\n deleteFileBtn=sune.querySelector('#deleteFileBtn'),\n commitList=sune.querySelector('#commitList');\n\n const parseRepoPath=p=>p?.match(/^([a-z\\d-]+)\\/([a-z\\d-.]+)(?:@([^/]+))?$/i)?.slice(1);\n const saveRepos=()=>localStorage.setItem(CACHE_KEY_B,JSON.stringify(\n [...sune.querySelectorAll('.repo-path-input')].map(i=>i.value.trim()).filter(Boolean)\n ));\n\n const showView=v=>{\n repoListView.classList.toggle('hidden',v!=='list');\n repoBrowserView.classList.toggle('hidden',v!=='browser');\n if(v==='list'){\n viewTitle.textContent='GitHub Repositories';\n pathHistory=[];\n breadcrumbs.innerHTML='';\n fileTree.innerHTML='';\n showBrowserSubView('tree');\n }else{\n viewTitle.textContent=`${currentRepoInfo.owner}/${currentRepoInfo.repo}`;\n }\n };\n\n const showBrowserSubView=v=>{\n browserSubView=v;\n fileBrowserContainer.classList.toggle('hidden',v!=='tree');\n fileHistoryContainer.classList.toggle('hidden',v!=='history');\n editorContainer.classList.toggle('hidden',v!=='editor');\n if(v==='tree'){\n commitList.innerHTML='';\n fileHistoryTitle.innerHTML='';\n }\n };\n\n const ghApi=async(m,u,b=null)=>{\n const h={'Accept':'application/vnd.github.v3+json'};\n if(window.USER?.PAT)h['Authorization']=`token ${window.USER.PAT}`;\n const o={method:m,headers:h};\n if(b){\n o.body=JSON.stringify(b);\n h['Content-Type']='application/json';\n }\n const r=await fetch(u,o);\n if(!r.ok){\n const e=await r.json().catch(()=>({message:`API Err: ${r.status}`}));\n throw new Error(e.message||`API Err: ${r.status}`);\n }\n if(r.status===204||r.headers.get('Content-Length')==='0')return null;\n return r.json();\n };\n\n const fetchHistory=async(fPath,btn)=>{\n showBrowserSubView('history');\n currentFilePath=fPath;\n fileHistoryTitle.innerHTML=`<div class=\"flex items-center gap-2\">\n <svg class=\"animate-spin h-4 w-4\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2\n 5.291A7.962 7.962 0 014 12H0c0\n 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n <span>Loading…</span>\n </div>`;\n commitList.innerHTML='';\n try{\n const{owner,repo,branch}=currentRepoInfo;\n const commits=await ghApi('GET',\n `https://api.github.com/repos/${owner}/${repo}/commits?path=${\n encodeURIComponent(fPath)\n }&sha=${encodeURIComponent(branch)}`\n );\n fileHistoryTitle.innerHTML=`History for <b>${fPath.split('/').pop()}</b>`;\n if(!commits?.length){\n commitList.innerHTML=`<div class=\"text-xs text-center text-gray-500 p-3 bg-gray-50 rounded-md\">\n No commits found.\n </div>`;\n }else{\n commitList.innerHTML=commits.map(i=>{\n const m=i.commit,a=i.author||m.author,\n d=new Date(m.author.date).toLocaleString([],\n {dateStyle:'medium',timeStyle:'short'}\n );\n return `<div class=\"p-3 border border-gray-200 rounded-lg bg-gray-50/50 hover:bg-white hover:shadow-md transition-all\">\n <p class=\"font-medium text-sm text-gray-800 truncate\">\n ${m.message.split('\\n')[0]}\n </p>\n <div class=\"flex flex-col sm:flex-row justify-between items-start sm:items-center mt-2 gap-2\">\n <div class=\"flex items-center gap-2 text-xs text-gray-500\">\n ${a?.avatar_url?`<img src=\"${a.avatar_url}\" class=\"h-5 w-5 rounded-full\">`:''}\n <span class=\"font-medium\">${m.author.name}</span>\n <span class=\"hidden md:inline\">•</span>\n <span class=\"flex-shrink-0\">${d}</span>\n </div>\n <div class=\"flex items-center gap-2 self-end sm:self-center flex-shrink-0\">\n <button class=\"view-content-btn px-3 py-1 text-xs bg-white border border-gray-300 rounded-md shadow-sm hover:bg-gray-100 active:scale-95\" data-sha=\"${i.sha}\">\n View File\n </button>\n </div>\n </div>\n </div>`;\n }).join('');\n }\n }catch(e){\n fileHistoryTitle.innerHTML=`Error loading history`;\n commitList.innerHTML=`<div class=\"text-xs text-center text-red-600 p-3 bg-red-50 rounded-md\">\n ${e.message}\n </div>`;\n }finally{\n btn?.classList.add('bg-blue-100','font-semibold');\n }\n };\n\n const renderBreadcrumbs=()=>{\n breadcrumbs.innerHTML=pathHistory.map((p,i)=>`\n <button class=\"breadcrumb-item inline-flex items-center gap-1.5 hover:text-blue-600 disabled:text-gray-800\"\n data-index=\"${i}\" ${i===pathHistory.length-1?'disabled':''}>\n ${p.name==='root'\n ?'<i data-lucide=\"git-branch\" class=\"h-4 w-4\"></i>'\n :''}\n ${p.name}\n ${i<pathHistory.length-1\n ?'<i data-lucide=\"chevron-right\" class=\"h-4 w-4 text-gray-400 -mr-1\"></i>'\n :''}\n </button>\n `).join('');\n window.lucide?.createIcons();\n };\n\n const fetchAndRenderTree=async(sha,name)=>{\n showBrowserSubView('tree');\n if(name)pathHistory.push({name,sha});\n else pathHistory=[{name:'root',sha}];\n renderBreadcrumbs();\n fileTree.innerHTML='<div class=\"text-sm text-center p-4\">Loading…</div>';\n try{\n const{tree}=await ghApi('GET',\n `https://api.github.com/repos/${currentRepoInfo.owner}/${currentRepoInfo.repo}/git/trees/${sha}`\n );\n const s=tree.sort((a,b)=>{\n if(a.type===b.type)return a.path.localeCompare(b.path);\n return a.type==='tree'?-1:1;\n });\n fileTree.innerHTML=s.map(i=>`\n <button class=\"file-item w-full flex items-center gap-2 p-2 text-left text-sm rounded-md hover:bg-gray-200\"\n data-path=\"${i.path}\" data-sha=\"${i.sha}\" data-type=\"${i.type}\">\n <i data-lucide=\"${i.type==='tree'?'folder':'file-text'}\"\n class=\"h-4 w-4 text-gray-500 shrink-0\"></i>\n <span class=\"truncate\">${i.path.split('/').pop()}</span>\n </button>\n `).join('')||'<div class=\"text-xs text-center text-gray-500 p-3\">Empty directory.</div>';\n window.lucide?.createIcons();\n }catch(e){\n alert(`Error fetching tree: ${e.message}`);\n }\n };\n\n const addRepoRow=p=>{\n const r=document.createElement('div');\n r.className='repo-row relative flex w-full items-center';\n r.innerHTML=`\n <i data-lucide=\"github\"\n class=\"pointer-events-none absolute left-3 top-1/2 z-10 h-4 w-4 -translate-y-1/2 text-gray-400\"></i>\n <input type=\"text\" value=\"${p}\" placeholder=\"org/repo@branch\"\n class=\"repo-path-input w-full rounded-lg border py-2 pl-9 pr-32 text-sm outline-none\n focus:border-blue-500 focus:ring-2 focus:ring-blue-500/50\">\n <div class=\"absolute right-1.5 flex items-center gap-1\">\n <button class=\"browse-repo-btn flex items-center justify-center gap-2 rounded-md bg-blue-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-blue-700 active:scale-[0.98] disabled:bg-blue-400\">\n Browse\n </button>\n <button class=\"remove-repo-btn flex items-center justify-center rounded-md p-1.5 text-red-500/80 hover:bg-red-500/10 hover:text-red-700\">\n <i data-lucide=\"x\" class=\"h-4 w-4\"></i>\n </button>\n </div>`;\n repoList.append(r);\n window.lucide?.createIcons();\n };\n\n const startRepoBrowse=async b=>{\n const i=b.closest('.repo-row').querySelector('.repo-path-input'),\n v=i.value.trim();\n if(!v)return;\n const p=parseRepoPath(v);\n if(!p){alert(\"Invalid format: org/repo@branch\");return}\n b.disabled=true;\n try{\n currentRepoInfo={owner:p[0],repo:p[1],branch:p[2]||null};\n if(!currentRepoInfo.branch){\n const r=await ghApi('GET',\n `https://api.github.com/repos/${currentRepoInfo.owner}/${currentRepoInfo.repo}`\n );\n currentRepoInfo.branch=r.default_branch;\n i.value=`${currentRepoInfo.owner}/${currentRepoInfo.repo}@${currentRepoInfo.branch}`;\n saveRepos();\n }\n const branchInfo=await ghApi('GET',\n `https://api.github.com/repos/${currentRepoInfo.owner}/${currentRepoInfo.repo}/branches/${currentRepoInfo.branch}`\n );\n showView('browser');\n await fetchAndRenderTree(branchInfo.commit.commit.tree.sha);\n }catch(e){\n alert(`Error: ${e.message}`);\n showView('list');\n }finally{\n b.disabled=false;\n }\n };\n\n // UPDATED: stay in browser, go back to file tree instead of full reset\n const deleteFileHandler=async()=>{\n const{owner,repo,branch}=currentRepoInfo,\n path=currentFilePath;\n if(!path)return;\n if(!confirm(`Are you sure you want to delete \"${path}\"? This action cannot be undone.`))return;\n\n fileHistoryTitle.innerHTML=`<div class=\"flex items-center gap-2\">\n <svg class=\"animate-spin h-4 w-4\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle>\n <path class=\"opacity-75\" fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2\n 5.291A7.962 7.962 0 014 12H0c0\n 3.042 1.135 5.824 3 7.938l3-2.647z\"></path>\n </svg>\n <span>Deleting…</span>\n </div>`;\n\n try{\n const d=await ghApi('GET',\n `https://api.github.com/repos/${owner}/${repo}/contents/${\n encodeURIComponent(path)\n }?ref=${encodeURIComponent(branch)}`\n );\n await ghApi('DELETE',\n `https://api.github.com/repos/${owner}/${repo}/contents/${\n encodeURIComponent(path)\n }`,\n {\n message:`Delete ${path.split('/').pop()}`,\n sha:d.sha,\n branch\n }\n );\n\n alert(`Successfully deleted ${path}`);\n\n // reload current tree (using last breadcrumb / pathHistory entry)\n const last=pathHistory[pathHistory.length-1];\n if(last?.sha){\n await fetchAndRenderTree(last.sha,last.name==='root'?null:last.name);\n }else{\n // fallback: refetch from branch head\n const branchInfo=await ghApi('GET',\n `https://api.github.com/repos/${owner}/${repo}/branches/${branch}`\n );\n await fetchAndRenderTree(branchInfo.commit.commit.tree.sha);\n }\n\n showBrowserSubView('tree');\n currentFilePath='';\n }catch(e){\n alert(`Error deleting file: ${e.message}`);\n fileHistoryTitle.innerHTML=`History for <b>${path.split('/').pop()}</b>`;\n }\n };\n\n // --- EDITOR SUNE ---\n window.SUNE_GH_EDITOR=(()=>{\n const getEl=id=>sune.querySelector('#'+id);\n const[\n editorView,pathInput,commitBtn,fileEditorEl,\n searchBtn,copyBtn,cutBtn,pasteBtn,\n statusEl,langBadge,\n searchContainer,searchInput,searchCount,\n searchPrevBtn,searchNextBtn,searchCloseBtn,\n ghBackBtn\n ]=[\n 'gh-editor-view','ghPathInput','ghCommitBtn','ghFileEditor',\n 'ghSearchBtn','ghCopyBtn','ghCutBtn','ghPasteBtn',\n 'ghStatus','ghLangBadge',\n 'ghSearchContainer','ghSearchInput','ghSearchCount',\n 'ghSearchPrevBtn','ghSearchNextBtn','ghSearchCloseBtn',\n 'gh-back-btn'\n ].map(getEl);\n\n const S={\n o:null,r:null,p:null,b:null,\n sha:null,t:null,l:null,\n jar:null,\n search:{q:'',m:[],c:-1,oh:null}\n };\n\n const setStatus=(msg,isErr=false)=>{\n statusEl.textContent=msg||'';\n statusEl.className=`h-4 truncate ${isErr?'text-red-600':'text-gray-500'}`;\n if(!isErr&&msg)setTimeout(()=>{\n if(statusEl.textContent===msg)statusEl.textContent='';\n },4000);\n };\n\n const fmtChars=c=>c<1e3?`${c} chars`:`${(c/1e3).toFixed(1).replace(/\\.0$/,'')}k chars`;\n const setLoading=(btn,loading)=>{\n btn.disabled=loading;\n const i=btn.querySelector('i');\n if(!i)return;\n if(loading){\n if(!i.dataset.og)i.dataset.og=i.dataset.lucide;\n i.dataset.lucide='loader-circle';\n i.classList.add('animate-spin');\n }else{\n if(i.dataset.og)i.dataset.lucide=i.dataset.og;\n i.classList.remove('animate-spin');\n }\n window.lucide?.createIcons();\n };\n const parsePath=i=>String(i).trim().match(/^([\\w.-]+)\\/([\\w.-]+)@([\\w.-]+)\\/(.+)$/);\n const detectLang=p=>{\n p=String(p||'').toLowerCase();\n if(/\\.html?$/.test(p))return{lang:'xml',label:'HTML'};\n if(/\\.m?js|cjs$/.test(p))return{lang:'javascript',label:'JavaScript'};\n if(/\\.json$/.test(p))return{lang:'json',label:'JSON'};\n if(/\\.css$/.test(p))return{lang:'css',label:'CSS'};\n if(/\\.md$/.test(p))return{lang:'markdown',label:'Markdown'};\n return{lang:'plaintext',label:'Plain Text'};\n };\n const enc=s=>btoa(unescape(encodeURIComponent(s)));\n const dec=b=>decodeURIComponent(escape(atob(b)));\n const esc=s=>s.replace(/[&<>\"']/g,c=>({\n '&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'\n }[c]));\n\n const clip=async op=>{\n try{\n if(op==='cut'||op==='copy'){\n const text=S.jar.toString();\n if(!text)return;\n await navigator.clipboard.writeText(text);\n if(op==='cut')S.jar.updateCode('');\n setStatus(op==='cut'?'Cut.':'Copied.');\n }else if(op==='paste'){\n const txt=await navigator.clipboard.readText();\n S.jar.updateCode(txt);\n setStatus(`Pasted (${fmtChars(txt.length)}).`);\n }\n }catch{\n setStatus(\n (op.charAt(0).toUpperCase()+op.slice(1))+' failed.',\n true\n );\n }\n };\n\n const updateSearchUI=()=>{\n const{m,c}=S.search,t=m.length;\n searchCount.textContent=`${t>0&&c>-1?c+1:0}/${t}`;\n searchPrevBtn.disabled=t<1;\n searchNextBtn.disabled=t<1;\n };\n\n const navigateSearch=dir=>{\n const{m}=S.search;\n if(!m.length)return;\n if(S.search.oh)S.search.oh.classList.remove('ring-2','ring-orange-500');\n let i=S.search.c+dir;\n if(i>=m.length)i=0;\n if(i<0)i=m.length-1;\n S.search.c=i;\n const el=m[i];\n el.classList.add('ring-2','ring-orange-500');\n el.scrollIntoView({behavior:'smooth',block:'center'});\n S.search.oh=el;\n updateSearchUI();\n };\n\n const handleSearchInput=()=>{\n const q=searchInput.value;\n if(q===S.search.q)return;\n if(S.search.oh)S.search.oh.classList.remove('ring-2','ring-orange-500');\n S.search.q=q;\n S.search.c=-1;\n S.jar.updateCode(S.jar.toString());\n };\n\n const openSearch=()=>{\n searchContainer.classList.replace('hidden','flex');\n searchInput.focus();\n searchInput.select();\n if(searchInput.value)handleSearchInput();\n };\n\n const closeSearch=()=>{\n searchContainer.classList.replace('flex','hidden');\n if(S.search.oh)S.search.oh.classList.remove('ring-2','ring-orange-500');\n S.search.q='';\n searchInput.value='';\n S.jar.updateCode(S.jar.toString());\n S.search.m=[];\n S.search.c=-1;\n S.search.oh=null;\n updateSearchUI();\n };\n\n const ghApiEditor=async(m,o,r,ep,b=null)=>{\n const u=ep.startsWith('https://')\n ?ep\n :`https://api.github.com/repos/${o}/${r}/${ep}`;\n const h={\n 'Authorization':`Bearer ${S.t}`,\n 'Accept':'application/vnd.github.v3+json'\n };\n const op={method:m,headers:h};\n if(b){\n op.body=JSON.stringify(b);\n h['Content-Type']='application/json';\n }\n const re=await fetch(u,op);\n if(!re.ok){\n const e=await re.json().catch(()=>({message:`API Err:${re.status}`}));\n throw new Error(e.message||`API Err:${re.status}`);\n }\n return re.status===204||re.headers.get('Content-Length')==='0'\n ?null\n :re.json();\n };\n\n const fetchHandler=async(pathValue,sha)=>{\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@branch/path',true);\n [,S.o,S.r,S.b,S.p]=m;\n const{lang,label}=detectLang(S.p);\n S.l=lang;\n langBadge.textContent=label;\n const ref=sha||S.b;\n const endpoint=`contents/${encodeURIComponent(S.p).replace(/%2F/g,'/')}?ref=${encodeURIComponent(ref)}`;\n setStatus('Fetching…');\n S.jar.updateCode('Loading…');\n try{\n const d=await ghApiEditor('GET',S.o,S.r,endpoint);\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 ${sha?`@${sha.substring(0,7)}`:'latest'} (${fmtChars(content.length)}).`);\n }catch(err){\n setStatus(`Error: ${err.message}`,true);\n S.sha=null;\n S.jar.updateCode(`// Error: ${err.message}`);\n }\n closeSearch();\n };\n\n const openNewFile=pVal=>{\n setStatus();\n S.t=window.USER?.PAT||'';\n if(!S.t)return setStatus('ERR: Token not found.',true);\n const m=parsePath(pVal);\n if(!m)return setStatus('ERR: Invalid path',true);\n [,S.o,S.r,S.b,S.p]=m;\n S.sha=null;\n const{lang,label}=detectLang(S.p);\n S.l=lang;\n langBadge.textContent=label;\n pathInput.value=pVal;\n S.jar.updateCode('');\n setStatus('New file. Add content and commit.');\n closeSearch();\n };\n\n const commitHandler=async()=>{\n setStatus();\n const{o,r,p,b,sha,t,jar}=S;\n if(!t)return setStatus('Error: GitHub token not found.',true);\n if(!o)return setStatus('Error: Select a repo first.',true);\n const newPathVal=pathInput.value.trim();\n const m=parsePath(newPathVal);\n if(!m)return setStatus('Error: Invalid path format.',true);\n const[,newO,newR,newB,newP]=m;\n if(newO!==o||newR!==r||newB!==b)\n return setStatus('Error: Changing repo or branch not allowed.',true);\n const msg=prompt(`Commit message for:\\n${newP}`,\n `Update ${newP.split('/').pop()||newP}`);\n if(msg===null)return setStatus('Commit cancelled.');\n if(!msg.trim())\n return setStatus('Error: Commit message required.',true);\n const newContent=jar.toString();\n setLoading(commitBtn,true);\n setStatus('Committing…');\n try{\n if(newP===p){\n const body={\n message:msg.trim(),\n content:enc(newContent),\n branch:b\n };\n if(sha)body.sha=sha;\n const data=await ghApiEditor('PUT',o,r,\n `contents/${encodeURIComponent(p).replace(/%2F/g,'/')}`,\n body\n );\n S.sha=data.content.sha;\n setStatus('Committed successfully!');\n }else{\n setStatus('Renaming/Moving file…');\n const{object:{sha:parentSha}}=await ghApiEditor('GET',o,r,`git/refs/heads/${b}`);\n const{tree:{sha:baseTreeSha}}=await ghApiEditor('GET',o,r,`git/commits/${parentSha}`);\n const{sha:newBlobSha}=await ghApiEditor('POST',o,r,'git/blobs',{\n content:newContent,encoding:\"utf-8\"\n });\n const{sha:newTreeSha}=await ghApiEditor('POST',o,r,'git/trees',{\n base_tree:baseTreeSha,\n tree:[\n {path:p,mode:'100644',type:'blob',sha:null},\n {path:newP,mode:'100644',type:'blob',sha:newBlobSha}\n ]\n });\n const{sha:newCommitSha}=await ghApiEditor('POST',o,r,'git/commits',{\n message:msg.trim(),\n tree:newTreeSha,\n parents:[parentSha]\n });\n await ghApiEditor('PATCH',o,r,`git/refs/heads/${b}`,{sha:newCommitSha});\n S.p=newP;\n S.sha=newBlobSha;\n setStatus('File moved and committed!');\n }\n }catch(err){\n setStatus(`Error: ${err.message}`,true);\n }finally{\n setLoading(commitBtn,false);\n }\n };\n\n const init=async()=>{\n const{CodeJar:CJ}=await import('https://medv.io/codejar/codejar.js');\n const doHighlight=ed=>{\n const c=ed.textContent;\n if(S.search.q){\n const q=S.search.q.replace(/[-/\\\\^$*+?.()|[\\]{}]/g,\"\\\\$&\");\n ed.innerHTML=c.split(new RegExp(`(${q})`,'gi'))\n .map((p,i)=>i%2\n ?`<mark class=\"gh-search-match bg-yellow-200 rounded-sm\">${esc(p)}</mark>`\n :esc(p)\n ).join('');\n S.search.m=[...ed.querySelectorAll('.gh-search-match')];\n updateSearchUI();\n }else if(window.hljs&&S.l&&hljs.getLanguage(S.l)){\n ed.innerHTML=hljs.highlight(c,{language:S.l,ignoreIllegals:true}).value;\n }else{\n ed.textContent=c;\n }\n };\n S.jar=CJ(fileEditorEl,doHighlight,{tab:' '});\n ghBackBtn.addEventListener('click',()=>backBtn.click());\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=>{\n if(e.key==='Enter'){\n e.preventDefault();\n navigateSearch(e.shiftKey?-1:1);\n }\n if(e.key==='Escape')closeSearch();\n });\n editorContainer.addEventListener('keydown',e=>{\n if(e.ctrlKey&&e.key==='f'){\n e.preventDefault();\n openSearch();\n }\n });\n };\n\n return{init,loadFile:fetchHandler,openNewFile};\n })();\n\n const init=async()=>{\n await window.SUNE_GH_EDITOR.init();\n\n const cachedRepos=JSON.parse(localStorage.getItem(CACHE_KEY_B)||'[]');\n if(cachedRepos.length)cachedRepos.forEach(p=>addRepoRow(p));\n else addRepoRow('');\n\n sune.addEventListener('click',e=>{\n const t=e.target;\n\n const browseBtn=t.closest('.browse-repo-btn');\n if(browseBtn)return startRepoBrowse(browseBtn);\n\n const removeBtn=t.closest('.remove-repo-btn');\n if(removeBtn){\n removeBtn.closest('.repo-row').remove();\n saveRepos();\n if(!repoList.childElementCount)addRepoRow('');\n return;\n }\n\n if(t.closest('#addRepoBtn'))return addRepoRow('');\n\n if(t.closest('#backBtn')){\n if(browserSubView==='editor')showBrowserSubView('history');\n else if(browserSubView==='history')showBrowserSubView('tree');\n else showView('list');\n return;\n }\n\n const fileItem=t.closest('.file-item');\n if(fileItem){\n const{path,type,sha}=fileItem.dataset;\n if(type==='tree'){\n fetchAndRenderTree(sha,path.split('/').pop());\n }else if(type==='blob'){\n const fullPath=[\n ...pathHistory.slice(1).map(p=>p.name),\n path\n ].join('/');\n fetchHistory(fullPath,fileItem);\n }\n return;\n }\n\n const breadcrumbItem=t.closest('.breadcrumb-item:not([disabled])');\n if(breadcrumbItem){\n const i=+breadcrumbItem.dataset.index;\n pathHistory=pathHistory.slice(0,i+1);\n fetchAndRenderTree(pathHistory[i].sha);\n return;\n }\n\n const vContent=t.closest('.view-content-btn');\n if(vContent){\n const path=`${currentRepoInfo.owner}/${currentRepoInfo.repo}@${currentRepoInfo.branch}/${currentFilePath}`;\n window.SUNE_GH_EDITOR.loadFile(path,vContent.dataset.sha);\n showBrowserSubView('editor');\n return;\n }\n\n if(t.closest('#commitLatestBtn')){\n const path=`${currentRepoInfo.owner}/${currentRepoInfo.repo}@${currentRepoInfo.branch}/${currentFilePath}`;\n window.SUNE_GH_EDITOR.loadFile(path,null);\n showBrowserSubView('editor');\n return;\n }\n\n if(t.closest('#deleteFileBtn'))return deleteFileHandler();\n\n const createBtn=t.closest('#createNewFileBtn');\n if(createBtn){\n const fName=prompt(\"Enter new file name:\");\n if(!fName?.trim())return;\n if(/[/\\\\:]/.test(fName))return alert(\"Invalid filename.\");\n const exists=[...fileTree.querySelectorAll('.file-item span')]\n .some(s=>s.textContent.trim().toLowerCase()===fName.toLowerCase());\n if(exists)return alert('A file with this name already exists.');\n const cPath=pathHistory.slice(1).map(p=>p.name).join('/');\n const fullPath=cPath?`${cPath}/${fName}`:fName;\n const ePath=`${currentRepoInfo.owner}/${currentRepoInfo.repo}@${currentRepoInfo.branch}/${fullPath}`;\n window.SUNE_GH_EDITOR.openNewFile(ePath);\n showBrowserSubView('editor');\n return;\n }\n });\n\n sune.addEventListener('input',e=>{\n if(e.target.classList.contains('repo-path-input'))saveRepos();\n });\n\n sune.addEventListener('keydown',e=>{\n if(e.key==='Enter'&&e.target.classList.contains('repo-path-input')){\n e.preventDefault();\n e.target.closest('.repo-row')\n .querySelector('.browse-repo-btn')?.click();\n }\n });\n\n window.lucide?.createIcons();\n };\n\n init();\n})();\n</script>\n","extension_html":"<sune src='https://raw.githubusercontent.com/sune-org/store/refs/heads/main/sync.sune' private></sune>","hide_composer":true,"include_thoughts":false,"json_output":false,"ignore_master_prompt":false,"json_schema":"","presence_penalty":"","max_tokens":""},"storage":{}}]