Files
store/commit-history.sune

1 line
16 KiB
JSON

[{"id":"28qvo5s","name":"GitHub File History","pinned":true,"avatar":"data:image/webp;base64,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","url":"gh://sune-org/store/commit-history.sune","updatedAt":1757172829568,"settings":{"model":"openai/gpt-5","temperature":"1","top_p":"0.96","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":"<div id=\"ghHistorySune\" class=\"relative p-4 bg-white/80 backdrop-blur-xl rounded-lg border border-gray-200 shadow-sm max-w-2xl mx-auto 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 mb-4\">\n <h2 class=\"font-bold text-lg text-gray-800\">GitHub File History</h2>\n <span class=\"text-xs text-gray-400\">v-0.3.2</span>\n </div>\n\n <!-- Input List -->\n <div id=\"inputList\" class=\"space-y-2 mb-3\">\n <!-- Input rows will be injected here by the script -->\n </div>\n\n <!-- Add Button -->\n <button id=\"addPathBtn\" 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 Path</span>\n </button>\n\n <!-- Divider -->\n <hr class=\"my-4 border-gray-200/80\">\n\n <!-- Status & Results Area -->\n <div id=\"resultsArea\" class=\"space-y-3\">\n <div id=\"statusArea\" class=\"text-sm text-center text-gray-600 p-4 border-2 border-dashed border-gray-200 rounded-lg hidden\"></div>\n <div id=\"commitList\" class=\"space-y-2 max-h-[50vh] overflow-y-auto -mr-2 pr-2\">\n <!-- Commit items will be injected here -->\n </div>\n </div>\n\n <!-- File Content Modal (Unchanged) -->\n <div id=\"fileContentModal\" class=\"fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4 hidden\">\n <div class=\"bg-white rounded-xl shadow-2xl w-full max-w-4xl flex flex-col max-h-[90vh]\">\n <div class=\"flex justify-between items-center p-3 border-b border-gray-200\">\n <p id=\"modalHeader\" class=\"text-sm font-medium text-gray-700 truncate\">File Content</p>\n <button id=\"closeModalBtn\" class=\"p-1 rounded-full hover:bg-gray-200 active:bg-gray-300 transition-colors\">\n <i data-lucide=\"x\" class=\"h-5 w-5 text-gray-600\"></i>\n </button>\n </div>\n <div class=\"flex-1 overflow-auto relative bg-gray-900\">\n <pre><code id=\"fileContentCode\" class=\"block p-4 text-xs font-mono leading-relaxed\"></code></pre>\n <button id=\"copyContentBtn\" class=\"absolute top-2 right-12 px-3 py-1 text-xs bg-gray-700/80 text-white rounded-md hover:bg-gray-600\">\n Copy\n </button>\n </div>\n </div>\n </div>\n</div>\n\n<script>\n(function() {\n const sune = document.getElementById('ghHistorySune');\n if (!sune || sune.dataset.initialized) return;\n sune.dataset.initialized = 'true';\n\n const SUNE_ID = window.SUNE?.id || 'gh-history-sune';\n const CACHE_KEY = `${SUNE_ID}_gh_paths`;\n\n // --- DOM Elements ---\n const inputList = sune.querySelector('#inputList'), addPathBtn = sune.querySelector('#addPathBtn');\n const statusArea = sune.querySelector('#statusArea'), commitList = sune.querySelector('#commitList');\n const modal = sune.querySelector('#fileContentModal'), modalHeader = sune.querySelector('#modalHeader');\n const modalCode = sune.querySelector('#fileContentCode'), copyContentBtn = sune.querySelector('#copyContentBtn');\n const closeModalBtn = sune.querySelector('#closeModalBtn');\n\n // --- Utility Functions ---\n const showStatus=(m,e=!1)=>(statusArea.innerHTML=m,statusArea.classList.toggle('text-red-600',e),statusArea.classList.remove('hidden'),commitList.innerHTML='');\n const hideStatus=()=>statusArea.classList.add('hidden');\n const setLoading=(b,l)=>{if(!b)return;b.disabled=l;b.innerHTML=l?`<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>`:`<i data-lucide=\"search\" class=\"h-4 w-4\"></i>`;if(!l)window.lucide?.createIcons()};\n const parsePath=p=>p?.match(/^([a-z\\d\\-_]+)\\/([a-z\\d\\-_.]+)(?:@([^\\/]+))?\\/(.+)$/i)?.slice(1);\n const b64_to_utf8=s=>{try{return decodeURIComponent(atob(s).split('').map(c=>'%'+('00'+c.charCodeAt(0).toString(16)).slice(-2)).join(''))}catch(e){return\"Err: Could not decode.\"}};\n const copyToClipboard=async(l,t)=>{const o=l.textContent;try{await navigator.clipboard.writeText(t);l.textContent='Copied!'}catch(r){l.textContent='Failed!'}finally{setTimeout(()=>{l.textContent=o},2e3)}};\n const highlightModalCode=()=>{if(window.hljs){delete modalCode.dataset.highlighted;window.hljs.highlightElement(modalCode)}};\n const savePaths=()=>{const p=[...sune.querySelectorAll('.gh-path-input')].map(i=>i.value.trim());localStorage.setItem(CACHE_KEY,JSON.stringify(p))};\n \n // --- Input Management ---\n const addInputRow=(p='')=>{const r=document.createElement('div');r.className='input-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/file/path\" class=\"gh-path-input w-full rounded-lg border border-gray-300 py-2 pl-9 pr-20 text-sm outline-none transition-colors focus:border-blue-500 focus:ring-2 focus:ring-blue-500\"><div class=\"absolute right-1.5 flex items-center gap-1\"><button class=\"fetch-history-btn flex items-center justify-center rounded-md p-1.5 text-gray-500 transition-all hover:bg-gray-200 hover:text-gray-900 disabled:cursor-not-allowed disabled:text-gray-400 disabled:hover:bg-transparent\"><i data-lucide=\"search\" class=\"h-4 w-4\"></i></button><button class=\"remove-path-btn flex items-center justify-center rounded-md p-1.5 text-red-500/80 transition-all hover:bg-red-500/10 hover:text-red-700\"><i data-lucide=\"x\" class=\"h-4 w-4\"></i></button></div>`;inputList.append(r);const i=r.querySelector('.gh-path-input'),f=r.querySelector('.fetch-history-btn');const t=()=>{f.disabled=!i.value.trim()};i.addEventListener('input',()=>{savePaths();t()});i.addEventListener('keydown',e=>{if(e.key==='Enter'&&!f.disabled){e.preventDefault();f.click()}});t();window.lucide?.createIcons()};\n\n // --- API Functions ---\n const ghFetch=async(u)=>{const h={'Accept':'application/vnd.github.v3+json'};if(window.USER?.PAT){h['Authorization']=`token ${window.USER.PAT}`};const r=await fetch(u,{headers:h});if(!r.ok){const e=await r.json().catch(()=>({message:`API Error: ${r.status}`}));throw new Error(e.message||`API Err: ${r.status}`)};return r.json()};\n const fetchHistory=async(btn)=>{const input=btn.closest('.input-row').querySelector('.gh-path-input'),val=input.value.trim();if(!val)return showStatus(\"Path cannot be empty.\",!0);const p=parsePath(val);if(!p)return showStatus(\"Invalid format: org/repo@branch/file/path\",!0);let parsed={owner:p[0],repo:p[1],branch:p[2]||null,path:p[3]};setLoading(btn,!0);hideStatus();commitList.innerHTML='';try{if(!parsed.branch){const repoInfo=await ghFetch(`https://api.github.com/repos/${parsed.owner}/${parsed.repo}`);parsed.branch=repoInfo.default_branch;input.value=`${parsed.owner}/${parsed.repo}@${parsed.branch}/${parsed.path}`;savePaths()}const url=`https://api.github.com/repos/${parsed.owner}/${parsed.repo}/commits?path=${encodeURIComponent(parsed.path)}&sha=${encodeURIComponent(parsed.branch)}`;const commits=await ghFetch(url);if(!commits?.length)showStatus(\"No commits found.\",!1);else renderCommits(commits,parsed)}catch(e){showStatus(`Error: ${e.message}`,!0)}finally{setLoading(btn,!1)}};\n const fetchFileContent=async(b)=>{const{owner,repo,path,sha}=b.dataset;if(!owner||!repo||!path||!sha)return;modalHeader.textContent=`Loading... (${sha.substring(0,7)})`;modalCode.textContent='Loading...';modalCode.className='block p-4 text-xs font-mono leading-relaxed hljs';modal.classList.remove('hidden');try{const d=await ghFetch(`https://api.github.com/repos/${owner}/${repo}/contents/${path}?ref=${sha}`);modalHeader.textContent=`${path} @ ${sha.substring(0,7)}`;modalCode.textContent=d.content?b64_to_utf8(d.content):\"Could not get content.\"}catch(e){modalHeader.textContent=`Error @ ${sha.substring(0,7)}`;modalCode.textContent=`Error: ${e.message}`}finally{highlightModalCode()}};\n const fetchDiffContent=async(b)=>{const{owner,repo,path,sha}=b.dataset;if(!owner||!repo||!path||!sha)return;modalHeader.textContent=`Loading diff... (${sha.substring(0,7)})`;modalCode.textContent='Loading...';modalCode.className='block p-4 text-xs font-mono leading-relaxed hljs language-diff';modal.classList.remove('hidden');try{const d=await ghFetch(`https://api.github.com/repos/${owner}/${repo}/commits/${sha}`);const f=d.files?.find(f=>f.filename===path);modalHeader.textContent=`Diff for ${path} @ ${sha.substring(0,7)}`;modalCode.textContent=f?.patch||\"No diff found.\"}catch(e){modalHeader.textContent=`Error diff @ ${sha.substring(0,7)}`;modalCode.textContent=`Error: ${e.message}`}finally{highlightModalCode()}};\n\n // --- Render Functions ---\n const renderCommits=(c,p)=>{commitList.innerHTML=c.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\" alt=\"${a.login||''}\">`:'<div class=\"h-5 w-5 rounded-full bg-gray-300\"></div>'}<span class=\"font-medium\">${m.author.name}</span><span class=\"hidden md:inline\">&bull;</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-diff-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-owner=\"${p.owner}\" data-repo=\"${p.repo}\" data-path=\"${p.path}\" data-sha=\"${i.sha}\">View Diff</button><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-owner=\"${p.owner}\" data-repo=\"${p.repo}\" data-path=\"${p.path}\" data-sha=\"${i.sha}\">View File</button></div></div></div>`}).join('')};\n\n // --- Initialization ---\n const init = () => {\n const cachedPaths = JSON.parse(localStorage.getItem(CACHE_KEY) || '[]');\n if (cachedPaths.length > 0) cachedPaths.forEach(p => addInputRow(p)); else addInputRow();\n addPathBtn.addEventListener('click',()=>addInputRow());\n inputList.addEventListener('click',e=>{const f=e.target.closest('.fetch-history-btn');if(f)return fetchHistory(f);const r=e.target.closest('.remove-path-btn');if(r){r.closest('.input-row').remove();savePaths();if(inputList.childElementCount===0)addInputRow()}});\n commitList.addEventListener('click',e=>{const v=e.target.closest('.view-content-btn');if(v)return fetchFileContent(v);const d=e.target.closest('.view-diff-btn');if(d)return fetchDiffContent(d)});\n closeModalBtn.addEventListener('click',()=>modal.classList.add('hidden'));\n modal.addEventListener('click',e=>e.target===modal&&modal.classList.add('hidden'));\n copyContentBtn.addEventListener('click',e=>copyToClipboard(e.target,modalCode.textContent));\n window.lucide?.createIcons();\n };\n if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); else init();\n})();\n</script>\n","extension_html":"\n<sune src='https://raw.githubusercontent.com/sune-org/store/refs/heads/main/sync.sune' private></sune"},"storage":{}}]