mirror of
https://github.com/sune-org/store.git
synced 2026-01-13 16:17:58 +00:00
1 line
32 KiB
JSON
1 line
32 KiB
JSON
[{"id":"37uz2i8","name":"GitHub","pinned":true,"avatar":"data:image/webp;base64,UklGRmIOAABXRUJQVlA4WAoAAAAwAAAAfwAAfwAASUNDUMgBAAAAAAHIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAEAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAACRyWFlaAAABFAAAABRnWFlaAAABKAAAABRiWFlaAAABPAAAABR3dHB0AAABUAAAABRyVFJDAAABZAAAAChnVFJDAAABZAAAAChiVFJDAAABZAAAAChjcHJ0AAABjAAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAAAAAAACSgAAAPhAAAts9YWVogAAAAAAAA9tYAAQAAAADTLXBhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACAAAAAcAEcAbwBvAGcAbABlACAASQBuAGMALgAgADIAMAAxADZBTFBIwwcAAAEhMWkagr0a0f+ISQKSFGURETFda9vWatH9Jrh2Dr1D7VBRunvp7nAGDp1r56W709l4P6XrTvJ9X95nNPuXA5iICZgAS9i2HZJkPV8kSmPbtm3btn20tc/O9nLO2ra1s20XMvOPH9+7qKiIc12RvY+ICaBj27YisTn3/SoxK9IQlDJGUkYZyzAG9hCY2bEjL6Vm5iHQAJjZbrvrv2vsqrV+KXZETADx/037OQYfPPOTPU576ReeMdvmXLUO0LIJAPzjB2+87N2SHf3Gbccgk7yGddC2fvpS+Mo24bnZfvUaOvwiTp973xhjNnTu92F5nh211xjogtOdbX7tha75X317Pp21Hrru2XNpkfXQtR+ZN0ZuQxf/8BfmjKDrX3b+fOmWXz96W8dPz5wbRroJ333dnBjtW+i2r3h+LnxmEvx3JHcs0AVeFeNW3DOYB5Pgv8fKublqctpWnfKCUl5ZVc9tRZkDRhp8IhCYTZJrVuuwV5W3AhVvacHjvXtoDjQ0MGLEYIVaTw7AGPeN5DJIMcavHWSbyislkFvV5gPb9W0ONBMDAyMIACMw6EFMNRxU5/4qx/jZLJkJM9pv9GyPYxoBEBgIACMCAAIgTGhQXYoZIKR2vKRX5hg0F4N/GoHgnwYGQpiBmYGEyUzt3tqrq9ZqZgAxAoN/GhFxApgMgQHCzDCwdnpxn9ZCm2IAA4ERQJCBGAgghAPGZhlqx1t7tMMpbdRyYgDACADjHIyIETKDiq0NrAPWn0vQZgZGACMCAWoARJwALnAJhABRurykP79qRyCAwDjjIIkDnOtFcnEEmCFkIMm6vLk3U7zTRpCBERipmVqCinMV10NO3GQjJ7kQMjPAzKrUgXFfZjqs2WAZiAHE1aKgkmQKUssBgv/V/13/38TdZGKzBFXV4U19OQzNvwNAIKaWFFxJWmomcElvkvv3P1cndUzIZA0GKHZ4YV/azYkYgaklQRBFQVRKnHMmY9ZnszCZTJNkCJBMGNZOPTHSLBIAMEmUFFqCSgWFoBaZJNPD9D//W1sLsS4SBjLDhNXt+rrUdc3+KXCuJfhNUqgErkUqlYwgE1Ka5jyrowQIkADrcG0/DpmsmTFiBCapRUkhKSVRUKpVHCS3Ot0IIUQqAJmBmSGzdm/sxzNobIwYAcQVCqUgSQrJH0pJIibKhbX1EEKY5GTCAAEYHT/Wjy812wcGACRIHBAFUVSpJBUnklsL9TTEUuIIw8wwAdgN7f7dj/eaDSEGBg6uEkVJEpSCSqnkoqSnzGwjhFjXpc5qRCaA49p9uR8/aNZTRgSASEaSUlAICtEfXKWUyck0TsIs5VhkBgiTbRq0+18/rN0/HQPTaGjyRMx1CrXHaXHtsseEKsKRhGXrp9V2fdbEae0+iDCkYpXJXCmXFEIqs96WLIWEwYENkY+sduK1Jk5q99cKMGNBhpWSS0yp5BhSSmUZpbDskIVBVsaw2q7Pmri6HY4QFqtUKiuevKSQY4y4+6MPwl0awMgCs/r635p4VweZycgliRiyFFLKIXkiasl+MJ0lLBmRMeYKTX64wwyQqwxNkgf3VEoJdca1RA2vMTIykWTJKyNeaqPrDgA2HDiGcgl1jrF48oFSa6EborcFBmGuHdGoOpAxwy27Z1DxnN1LDGGL7LOrnWQAYZlZ3L7DPpIMRzLlYiV5jtkzymCQokuEJZMl14y48YFG3tXh3w6qfCyrTJ7cc8kpRdWypER2FIwwhJORv9HoAx0YA9VoOKpMQxlV8VJySVKFZb9chywjcK4b82Qr3c8RLIyGg4XF0VhWyLnkhJLM4lhskpAtvJa5fHuX7z2PL4yWl8ajhfG4GsjlxavBxY5SS6QA1jD2+ZPNvKgLD543TIs77LrrTosrw7FMSp6Eq4auJ1UMwj2jT9KsrAvfHY9H4+132nn3HXdaWKDKJWdTo64ZorOETK5h9M5v2uGxpirn1IAWTl9cWt5jt722XxqPXJ5iVLkOa1kTTuGeCX+i4Q80HWtUOW2Cnyxt/69dDjzggF12c48plYEYUl2/2Ozh1VNMqd9b4nUNP3STWW4AjlnZ49jbP/q3jbW6jnV4UP1l8eKL73+2ZNpNPzf1igZGv4M/rOQtAOXTV6cxppSUHmL5y9n3mHr9l7R9SAOHDsuBE3IL3lzPYoqppJRqavExky9o/DdnNMBB4J/MLXZXmOWYYooqe9n9dvHpid492hrf3+IvblxN/nnTn3YoyZO8qEaS1PyIiY/RvI5q4moNKr5waE6vgo/svlw8lVw8F1u2B+b7Zx9v+oILRmCvzPl6ZVIqpRRZRlTlNNc/NQdcf2EDY+BCTAvYZwghqbiQBXYkk74r5vGriw0M3YAqmG5ifZJSkQSWUYanuCjmMlgD42cdZED5x0adXO7OP2VrgkM3MJ/asYH3jvd0ueNf++VqlArGvxo8bt+jzOnqCxvg36PRaDQc/+Wf05izpCZsjXroKPP69i83bfmntY1UJNFs2WPeEHN78Y7tfr9Wp+Jia4/auof5XeU1bX77n5AzrTNXOv76D8zyq9rkSSp09Cq7H2O+97WmYai7mP9+8IYPmfE/i9dv+tFGoXPkP848cwNz/3IGx+w0qdWpbt707W23+xmuhuUna1Ons3/+jbu5ajr/V/P/TAEAVlA4IKgEAACQGwCdASqAAIAAPm0ylkgkIqIrpBQLAXANiWcNuD8HEMw51fMIHemjbgc7ppmfoAdKn+3f7Ae0lqtnjvsX/vP4uXCe4vOPt+eoGkC/Pv656gHl6ZovpL2A/5D/YeqX6GX6ZlOWxKqFH2W7PzGmbXvhmv9sGoJHQb2/1nWFHgc8XnQCn/FeiUiPKR+Cya3OEjGbuTIFFhib2CPqN5IME51yJiF2cnaxUUWtsaACzXoZ7bGqLTeBQwqxgFoVpOAh1HPj+iJRMaTzca9aDWaMV8H/QdmEXd/0ROZZV7s2HNgebtJWJbCAAP78rRADN3z3rP+m1M8qfXxPSaEsZwmO6otPZZx4Juk1MU1Lzjx9KVqLxt9fS2twv5W1ioBzQX0/zj83K4uGiT75jpi4rADEtzlbbUBNn16cgMFLkTxWfpWkJT4mySPWF/1J+n5//yGP/4GI//+ASTKuU/9uY5yYd4WP60bb7wFfOxdyrnb35PiB409fgwfRfwjzpzfhbevSO76VygixbhY5qanDzbHTNpIyEPtDTxl+N3M3xe4nGc4s7H/+P+5LKL1UXhNuODkMyXCXM4W3vWyhVoUXZpxnphi9ek4FBtXyIz6EkavKM6s9ikJ8FC/meVEbJjXufhSINFwdTJKaIyjgP2+nsv5cXMinYHB7/Yr/cUo2f6L2OHl3xyhTbhlQWnFZXwzJwghZFCxgAbTTOwSFP/rZob7//3D+5f9/PXbDXH0O+zHhhPlbq0bt6/2xtru0nwIyYJNSAj0Lq9y98KG5eewIyf9Or2taZGimOEdbHFmWAtEOgA0dT6439LYU0KLb5QPrqKCpm3dAb+zFbnjlXaquNUaUfndq+d5HDLy9V0XwC7r6H3BzNysYWJE5GU3ZQilPA3Wzz4nIKsR09B5AVhX3Wx7oj3XiBGfb1WUaW94SI4rSfhodhcC1opfldHtvmDBwdor766Idic7zc746HAU3ET18yncX1pb+Km8W8l6BkIoknY0OpRdEQlr1ZKb7FeN7nIMM90aEDWNbfVl2U5CTdnBP1r/WhP1201+jWIm++PjF5/qyfCsF7Y451oj4w8l7duGH6RViO08wJqU4VA2sI3lGLKuJXZFmO628VhAaX/wEqdrFi8ZWGAGu6e/ASuVyXZzRkYHuzx/qBVIznftcuvFHI0g/VGY4UD9h6qFLtXOn71lKEFB9gMJ6ZFgbD39o4K3VAU3yGO/Lk+EaA1oWqzmiZbmtAQEOftLUof2Gz2iIDfXXbPoZUjDkLCj2p6YC/ShrlRmTnv18hGOc9zC17KkaYy71Twbtpi63C+uuETeJaLQzrvY+SfLK1CuCmGNniGw9Ii7SKflGXX7hOsSK0PxHd8vt1XEX/ZFqd6C4g1G28Win5+i0km+OcOOok6AJQTPHQepwU/79pf/TRf/9RsmZYmdjVILqUAcMyTJPJvaPY0RWXSLLsk0uWeiUg1OXThW8ycrgpLwgHTtngE51YrXrrj4LwL/IuqNdLj7PYhW+wCN6qGcrXC40LQgM7YlJjQa0X3qr0+jfN8PPFeACeYEPaXqZdziAabGxPX66LOuiNEm/RIltyAAAAAAA","url":"gh://sune-org/store/github-utilities/github.sune","updatedAt":1757587963094,"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.7.8</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><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><span>Back</span>\n</button>\n</div>\n\n<div id=\"fileBrowserContainer\" class=\"px-4 pb-4\">\n<div id=\"breadcrumbs\" class=\"flex items-center gap-1.5 text-sm text-gray-500 mb-2 overflow-x-auto pb-2 -mr-4 pr-4\"></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><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><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.5 (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\"><i data-lucide=\"arrow-left\" class=\"h-5 w-5 shrink-0\"></i></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\"><i data-lucide=\"check-circle\" class=\"h-4 w-4\"></i><span class=\"hidden sm:inline\">Commit</span></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\"><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\"><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\"><i data-lucide=\"x\" class=\"h-5 w-5\"></i></button>\n</div>\n</div>\n<div class=\"bg-white flex-1 relative\"><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></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\"><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.5</span>\n</div>\n</div>\n</div>\n</div>\n</div>\n\n<script>\n(async function() {\nconst sune = document.getElementById('ghRepoBrowserSune');\nif (!sune || sune.dataset.initialized) return;\nsune.dataset.initialized = 'true';\n\n// --- BROWSER SUNE ---\nconst SUNE_ID_B = window.SUNE?.id || 'gh-repo-browser-sune';\nconst CACHE_KEY_B = `${SUNE_ID_B}_gh_repos`;\nlet currentRepoInfo = {}, pathHistory = [], browserSubView = 'tree', currentFilePath = '';\n\nconst viewTitle=sune.querySelector('#viewTitle'),repoListView=sune.querySelector('#repoListView'),repoBrowserView=sune.querySelector('#repoBrowserView'),repoList=sune.querySelector('#repoList'),addRepoBtn=sune.querySelector('#addRepoBtn'),backBtn=sune.querySelector('#backBtn'),fileBrowserContainer=sune.querySelector('#fileBrowserContainer'),fileHistoryContainer=sune.querySelector('#fileHistoryContainer'),editorContainer=sune.querySelector('#editorContainer'),breadcrumbs=sune.querySelector('#breadcrumbs'),fileTree=sune.querySelector('#fileTree'),fileHistoryHeader=sune.querySelector('#fileHistoryHeader'),fileHistoryTitle=sune.querySelector('#fileHistoryTitle'),commitLatestBtn=sune.querySelector('#commitLatestBtn'),deleteFileBtn=sune.querySelector('#deleteFileBtn'),commitList=sune.querySelector('#commitList');\n\nconst parseRepoPath=p=>p?.match(/^([a-z\\d-]+)\\/([a-z\\d-.]+)(?:@([^/]+))?$/i)?.slice(1);\nconst saveRepos=()=>localStorage.setItem(CACHE_KEY_B,JSON.stringify([...sune.querySelectorAll('.repo-path-input')].map(i=>i.value.trim()).filter(Boolean)));\nconst showView=v=>{repoListView.classList.toggle('hidden',v!=='list');repoBrowserView.classList.toggle('hidden',v!=='browser');if(v==='list'){viewTitle.textContent='GitHub Repositories';pathHistory=[];breadcrumbs.innerHTML='';fileTree.innerHTML='';showBrowserSubView('tree')}else{viewTitle.textContent=`${currentRepoInfo.owner}/${currentRepoInfo.repo}`}};\nconst showBrowserSubView=v=>{browserSubView=v;fileBrowserContainer.classList.toggle('hidden',v!=='tree');fileHistoryContainer.classList.toggle('hidden',v!=='history');editorContainer.classList.toggle('hidden',v!=='editor');if(v==='tree'){commitList.innerHTML='';fileHistoryTitle.innerHTML=''}};\nconst ghApi=async(m,u,b=null)=>{const h={'Accept':'application/vnd.github.v3+json'};if(window.USER?.PAT)h['Authorization']=`token ${window.USER.PAT}`;const op={method:m,headers:h};if(b){op.body=JSON.stringify(b);h['Content-Type']='application/json'};const r=await fetch(u,op);if(!r.ok){const e=await r.json().catch(()=>({message:`API Err: ${r.status}`}));throw new Error(e.message||`API Err: ${r.status}`)};if(r.status===204||r.headers.get('Content-Length')==='0')return null;return r.json()};\nconst fetchHistory=async(fPath,btn)=>{showBrowserSubView('history');currentFilePath=fPath;fileHistoryTitle.innerHTML=`<div class=\"flex items-center gap-2\"><svg class=\"animate-spin h-4 w-4\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\"><circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle><path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path></svg> <span>Loading…</span></div>`;commitList.innerHTML='';try{const{owner,repo,branch}=currentRepoInfo;const commits=await ghApi('GET',`https://api.github.com/repos/${owner}/${repo}/commits?path=${encodeURIComponent(fPath)}&sha=${encodeURIComponent(branch)}`);fileHistoryTitle.innerHTML=`History for <b>${fPath.split('/').pop()}</b>`;if(!commits?.length)commitList.innerHTML=`<div class=\"text-xs text-center text-gray-500 p-3 bg-gray-50 rounded-md\">No commits found.</div>`;else commitList.innerHTML=commits.map(i=>{const m=i.commit,a=i.author||m.author,d=new Date(m.author.date).toLocaleString([],{dateStyle:'medium',timeStyle:'short'});return`<div class=\"p-3 border border-gray-200 rounded-lg bg-gray-50/50 hover:bg-white hover:shadow-md transition-all\"><p class=\"font-medium text-sm text-gray-800 truncate\">${m.message.split('\\n')[0]}</p><div class=\"flex flex-col sm:flex-row justify-between items-start sm:items-center mt-2 gap-2\"><div class=\"flex items-center gap-2 text-xs text-gray-500\">${a?.avatar_url?`<img src=\"${a.avatar_url}\" class=\"h-5 w-5 rounded-full\">`:''}<span class=\"font-medium\">${m.author.name}</span><span class=\"hidden md:inline\">•</span><span class=\"flex-shrink-0\">${d}</span></div><div class=\"flex items-center gap-2 self-end sm:self-center flex-shrink-0\"><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}\">View File</button></div></div></div>`}).join('')}catch(e){fileHistoryTitle.innerHTML=`Error loading history`;commitList.innerHTML=`<div class=\"text-xs text-center text-red-600 p-3 bg-red-50 rounded-md\">${e.message}</div>`}finally{btn?.classList.add('bg-blue-100','font-semibold')}};\nconst renderBreadcrumbs=()=>{breadcrumbs.innerHTML=pathHistory.map((p,i)=>`<button class=\"breadcrumb-item inline-flex items-center gap-1.5 hover:text-blue-600 disabled:text-gray-800\" data-index=\"${i}\" ${i===pathHistory.length-1?'disabled':''}>${p.name==='root'?'<i data-lucide=\"git-branch\" class=\"h-4 w-4\"></i>':''}${p.name}${i<pathHistory.length-1?'<i data-lucide=\"chevron-right\" class=\"h-4 w-4 text-gray-400 -mr-1\"></i>':''}</button>`).join('');window.lucide?.createIcons()};\nconst fetchAndRenderTree=async(sha,name)=>{showBrowserSubView('tree');if(name)pathHistory.push({name,sha});else pathHistory=[{name:'root',sha}];renderBreadcrumbs();fileTree.innerHTML='<div class=\"text-sm text-center p-4\">Loading…</div>';try{const{tree}=await ghApi('GET',`https://api.github.com/repos/${currentRepoInfo.owner}/${currentRepoInfo.repo}/git/trees/${sha}`);const s=tree.sort((a,b)=>{if(a.type===b.type)return a.path.localeCompare(b.path);return a.type==='tree'?-1:1});fileTree.innerHTML=s.map(i=>`<button class=\"file-item w-full flex items-center gap-2 p-2 text-left text-sm rounded-md hover:bg-gray-200\" data-path=\"${i.path}\" data-sha=\"${i.sha}\" data-type=\"${i.type}\"><i data-lucide=\"${i.type==='tree'?'folder':'file-text'}\" class=\"h-4 w-4 text-gray-500 shrink-0\"></i><span class=\"truncate\">${i.path.split('/').pop()}</span></button>`).join('')||'<div class=\"text-xs text-center text-gray-500 p-3\">Empty directory.</div>';window.lucide?.createIcons()}catch(e){alert(`Error fetching tree: ${e.message}`)}};\nconst addRepoRow=p=>{const r=document.createElement('div');r.className='repo-row relative flex w-full items-center';r.innerHTML=`<i data-lucide=\"github\" class=\"pointer-events-none absolute left-3 top-1/2 z-10 h-4 w-4 -translate-y-1/2 text-gray-400\"></i><input type=\"text\" value=\"${p}\" placeholder=\"org/repo@branch\" class=\"repo-path-input w-full rounded-lg border py-2 pl-9 pr-32 text-sm outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-500/50\"><div class=\"absolute right-1.5 flex items-center gap-1\"><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\">Browse</button><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\"><i data-lucide=\"x\" class=\"h-4 w-4\"></i></button></div>`;repoList.append(r);window.lucide?.createIcons()};\nconst startRepoBrowse=async b=>{const i=b.closest('.repo-row').querySelector('.repo-path-input'),v=i.value.trim();if(!v)return;const p=parseRepoPath(v);if(!p){alert(\"Invalid format: org/repo@branch\");return}b.disabled=true;try{currentRepoInfo={owner:p[0],repo:p[1],branch:p[2]||null};if(!currentRepoInfo.branch){const r=await ghApi('GET',`https://api.github.com/repos/${currentRepoInfo.owner}/${currentRepoInfo.repo}`);currentRepoInfo.branch=r.default_branch;i.value=`${currentRepoInfo.owner}/${currentRepoInfo.repo}@${currentRepoInfo.branch}`;saveRepos()}const branchInfo=await ghApi('GET',`https://api.github.com/repos/${currentRepoInfo.owner}/${currentRepoInfo.repo}/branches/${currentRepoInfo.branch}`);showView('browser');await fetchAndRenderTree(branchInfo.commit.commit.tree.sha)}catch(e){alert(`Error: ${e.message}`);showView('list')}finally{b.disabled=false}};\nconst deleteFileHandler=async()=>{const{owner,repo,branch}=currentRepoInfo,path=currentFilePath;if(!confirm(`Are you sure you want to delete \"${path}\"? This action cannot be undone.`))return;fileHistoryTitle.innerHTML=`<div class=\"flex items-center gap-2\"><svg class=\"animate-spin h-4 w-4\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\"><circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle><path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"></path></svg> <span>Deleting…</span></div>`;try{const d=await ghApi('GET',`https://api.github.com/repos/${owner}/${repo}/contents/${encodeURIComponent(path)}?ref=${encodeURIComponent(branch)}`);await ghApi('DELETE',`https://api.github.com/repos/${owner}/${repo}/contents/${encodeURIComponent(path)}`,{message:`Delete ${path.split('/').pop()}`,sha:d.sha,branch});alert(`Successfully deleted ${path}`);showView('list')}catch(e){alert(`Error deleting file: ${e.message}`);fileHistoryTitle.innerHTML=`History for <b>${path.split('/').pop()}</b>`}};\n\n// --- EDITOR SUNE --- (Integrated & Upgraded)\nwindow.SUNE_GH_EDITOR = (() => {\nconst getEl=id=>sune.querySelector(`#${id}`);\nconst [editorView,pathInput,commitBtn,fileEditorEl,searchBtn,copyBtn,cutBtn,pasteBtn,statusEl,langBadge,searchContainer,searchInput,searchCount,searchPrevBtn,searchNextBtn,searchCloseBtn,ghBackBtn]=['gh-editor-view','ghPathInput','ghCommitBtn','ghFileEditor','ghSearchBtn','ghCopyBtn','ghCutBtn','ghPasteBtn','ghStatus','ghLangBadge','ghSearchContainer','ghSearchInput','ghSearchCount','ghSearchPrevBtn','ghSearchNextBtn','ghSearchCloseBtn','gh-back-btn'].map(getEl);\nconst S={o:null,r:null,p:null,b:null,sha:null,jar:null,t:null,l:null,search:{q:'',m:[],c:-1,oh:null}};\nconst setStatus=(msg,isErr=false)=>{statusEl.textContent=msg||'';statusEl.className=`h-4 truncate ${isErr?'text-red-600':'text-gray-500'}`;if(!isErr&&msg)setTimeout(()=>statusEl.textContent===msg&&(statusEl.textContent=''),4e3);};\nconst fmtChars=c=>c<1e3?`${c} chars`:`${(c/1e3).toFixed(1).replace(/\\.0$/,'')}k chars`;\nconst 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')}window.lucide?.createIcons()};\nconst parsePath=i=>String(i).trim().match(/^([\\w.-]+)\\/([\\w.-]+)@([\\w.-]+)\\/(.+)$/);\nconst 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'});\nconst enc=s=>btoa(unescape(encodeURIComponent(s))),dec=b=>decodeURIComponent(escape(atob(b))),esc=s=>s.replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''})[c]);\nconst 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(txt);setStatus(`Pasted (${fmtChars(txt.length)}).`);}}catch{setStatus(`${op.charAt(0).toUpperCase()+op.slice(1)} failed.`,true);}};\nconst 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;};\nconst 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();};\nconst 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());};\nconst openSearch=()=>{searchContainer.classList.replace('hidden','flex');searchInput.focus();searchInput.select();if(searchInput.value)handleSearchInput();};\nconst 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();};\nconst ghApiEditor=async(m,o,r,ep,b=null)=>{const u=ep.startsWith('https://')?ep:`https://api.github.com/repos/${o}/${r}/${ep}`;const h={'Authorization':`Bearer ${S.t}`,'Accept':'application/vnd.github.v3+json'};const op={method:m,headers:h};if(b){op.body=JSON.stringify(b);h['Content-Type']='application/json'}const re=await fetch(u,op);if(!re.ok){const e=await re.json().catch(()=>({message:`API Err:${re.status}`}));throw new Error(e.message||`API Err:${re.status}`)}return re.status===204||re.headers.get('Content-Length')==='0'?null:re.json()};\nconst fetchHandler=async(pathValue,sha)=>{setStatus();S.t=window.USER?.PAT||'';if(!S.t)return setStatus('Error: GitHub token not found.',true);const m=parsePath(pathValue);if(!m)return setStatus('Error: Invalid path. Use: owner/repo@branch/path',true);[,S.o,S.r,S.b,S.p]=m;const{lang,label}=detectLang(S.p);S.l=lang;langBadge.textContent=label;const ref=sha||S.b;const endpoint=`contents/${encodeURIComponent(S.p).replace(/%2F/g,'/')}?ref=${encodeURIComponent(ref)}`;setStatus('Fetching…');S.jar.updateCode('Loading…');try{const d=await ghApiEditor('GET',S.o,S.r,endpoint);if(d.type!=='file')throw new Error('Path is not a file.');S.sha=d.sha;const content=dec(d.content||'');S.jar.updateCode(content);pathInput.value=pathValue;setStatus(`Loaded ${sha?`@${sha.substring(0,7)}`:'latest'} (${fmtChars(content.length)}).`)}catch(err){setStatus(`Error: ${err.message}`,true);S.sha=null;S.jar.updateCode(`// Error: ${err.message}`)}closeSearch()};\nconst 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 newPathVal=pathInput.value.trim();const m=parsePath(newPathVal);if(!m)return setStatus('Error: Invalid path format.',true);const[,newO,newR,newB,newP]=m;if(newO!==o||newR!==r||newB!==b)return setStatus('Error: Changing repo or branch not allowed.',true);const msg=prompt(`Commit message for:\\n${newP}`,`Update ${newP.split('/').pop()||newP}`);if(msg===null)return setStatus('Commit cancelled.');if(!msg.trim())return setStatus('Error: Commit message required.',true);const newContent=jar.toString();setLoading(commitBtn,true);setStatus('Committing…');try{if(newP===p){const data=await ghApiEditor('PUT',o,r,`contents/${encodeURIComponent(p).replace(/%2F/g,'/')}`,{message:msg.trim(),content:enc(newContent),sha,branch:b});S.sha=data.content.sha;setStatus('Committed successfully!')}else{setStatus('Renaming/Moving file…');const{object:{sha:parentSha}}=await ghApiEditor('GET',o,r,`git/refs/heads/${b}`);const{tree:{sha:baseTreeSha}}=await ghApiEditor('GET',o,r,`git/commits/${parentSha}`);const{sha:newBlobSha}=await ghApiEditor('POST',o,r,'git/blobs',{content:newContent,encoding:\"utf-8\"});const{sha:newTreeSha}=await ghApiEditor('POST',o,r,'git/trees',{base_tree:baseTreeSha,tree:[{path:p,mode:'100644',type:'blob',sha:null},{path:newP,mode:'100644',type:'blob',sha:newBlobSha}]});const{sha:newCommitSha}=await ghApiEditor('POST',o,r,'git/commits',{message:msg.trim(),tree:newTreeSha,parents:[parentSha]});await ghApiEditor('PATCH',o,r,`git/refs/heads/${b}`,{sha:newCommitSha});S.p=newP;S.sha=newBlobSha;setStatus('File moved and committed!')}}catch(err){setStatus(`Error: ${err.message}`,true)}finally{setLoading(commitBtn,false)}};\nconst init=async()=>{const{CodeJar:CJ}=await import('https://medv.io/codejar/codejar.js');const doHighlight=ed=>{const c=ed.textContent;if(S.search.q){const q=S.search.q.replace(/[-/\\\\^$*+?.()|[\\]{}]/g,\"\\\\$&\");ed.innerHTML=c.split(new RegExp(`(${q})`,'gi')).map((p,i)=>i%2?`<mark class=\"gh-search-match bg-yellow-200 rounded-sm\">${esc(p)}</mark>`:esc(p)).join('');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}};S.jar=CJ(fileEditorEl,doHighlight,{tab:' '});ghBackBtn.addEventListener('click',()=>backBtn.click());commitBtn.addEventListener('click',commitHandler);copyBtn.addEventListener('click',()=>clip('copy'));cutBtn.addEventListener('click',()=>clip('cut'));pasteBtn.addEventListener('click',()=>clip('paste'));searchBtn.addEventListener('click',openSearch);searchCloseBtn.addEventListener('click',closeSearch);searchNextBtn.addEventListener('click',()=>navigateSearch(1));searchPrevBtn.addEventListener('click',()=>navigateSearch(-1));searchInput.addEventListener('input',handleSearchInput);searchInput.addEventListener('keydown',e=>{if(e.key==='Enter'){e.preventDefault();navigateSearch(e.shiftKey?-1:1)}if(e.key==='Escape')closeSearch()});editorContainer.addEventListener('keydown',e=>{if(e.ctrlKey&&e.key==='f'){e.preventDefault();openSearch()}})};\nreturn{init,loadFile:fetchHandler};\n})();\n\n// --- BROWSER/EDITOR GLUE & INIT ---\nconst init = async () => {\nawait window.SUNE_GH_EDITOR.init();\nconst cachedRepos = JSON.parse(localStorage.getItem(CACHE_KEY_B) || '[]');\nif (cachedRepos.length) cachedRepos.forEach(p => addRepoRow(p)); else addRepoRow('');\nsune.addEventListener('click', e => {\nconst t = e.target;\nconst browseBtn = t.closest('.browse-repo-btn'); if(browseBtn) return startRepoBrowse(browseBtn);\nconst removeBtn = t.closest('.remove-repo-btn'); if(removeBtn) { removeBtn.closest('.repo-row').remove(); saveRepos(); if(repoList.childElementCount===0)addRepoRow(''); return; }\nif(t.closest('#addRepoBtn')) return addRepoRow('');\nif(t.closest('#backBtn')) { if(browserSubView==='editor')showBrowserSubView('history'); else if(browserSubView==='history')showBrowserSubView('tree'); else showView('list'); return; }\nconst fileItem = t.closest('.file-item'); if(fileItem) { const {path,type,sha}=fileItem.dataset; if(type==='tree')fetchAndRenderTree(sha,path.split('/').pop()); else if(type==='blob'){const fullPath=[...pathHistory.slice(1).map(p=>p.name),path].join('/');fetchHistory(fullPath,fileItem)} return; }\nconst breadcrumbItem = t.closest('.breadcrumb-item:not([disabled])'); if(breadcrumbItem) { const i = parseInt(breadcrumbItem.dataset.index,10); pathHistory = pathHistory.slice(0,i+1); fetchAndRenderTree(pathHistory[i].sha); return; }\nconst vContent = t.closest('.view-content-btn'); if(vContent) { const path = `${currentRepoInfo.owner}/${currentRepoInfo.repo}@${currentRepoInfo.branch}/${currentFilePath}`; window.SUNE_GH_EDITOR.loadFile(path, vContent.dataset.sha); showBrowserSubView('editor'); return; }\nif(t.closest('#commitLatestBtn')) { const path = `${currentRepoInfo.owner}/${currentRepoInfo.repo}@${currentRepoInfo.branch}/${currentFilePath}`; window.SUNE_GH_EDITOR.loadFile(path, null); showBrowserSubView('editor'); return; }\nif(t.closest('#deleteFileBtn')) return deleteFileHandler();\n});\nsune.addEventListener('input', e => { if(e.target.classList.contains('repo-path-input')) saveRepos(); });\nsune.addEventListener('keydown', e => { if(e.key==='Enter' && e.target.classList.contains('repo-path-input')) e.target.closest('.repo-row').querySelector('.browse-repo-btn')?.click(); });\nwindow.lucide?.createIcons();\n};\ninit();\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,"presence_penalty":"","max_tokens":""},"storage":{}}] |