Files
store/commit-history.sune

28 lines
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@main/commit-history.sune",
"updatedAt": 1757095385587,
"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": 0,
"verbosity": "",
"reasoning_effort": "default",
"system_prompt": "",
"html": "<div id=\"ghHistorySune\" class=\"p-4 bg-white rounded-lg border border-gray-200 shadow-sm max-w-2xl mx-auto font-sans\">\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.2.0</span>\n </div>\n\n <!-- Input Form -->\n <div class=\"flex flex-col sm:flex-row gap-2 mb-4\">\n <div class=\"relative flex-1\">\n <i data-lucide=\"github\" class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400\"></i>\n <input type=\"text\" id=\"ghPathInput\" placeholder=\"org/repo@branch/file/path\" class=\"w-full pl-9 pr-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-colors\">\n </div>\n <button id=\"fetchHistoryBtn\" class=\"flex items-center justify-center px-4 py-2 bg-gray-800 text-white rounded-lg hover:bg-gray-900 active:scale-95 transition-all disabled:opacity-50 disabled:cursor-not-allowed\">\n <i data-lucide=\"search\" class=\"h-4 w-4\"></i>\n <span class=\"ml-2\">Fetch</span>\n </button>\n </div>\n\n <!-- Status & Results Area -->\n <div id=\"resultsArea\" class=\"space-y-3\">\n <div id=\"statusArea\" class=\"text-sm text-center text-gray-500 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 -->\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_path`;\n\n // --- DOM Elements ---\n const pathInput = sune.querySelector('#ghPathInput'), fetchBtn = sune.querySelector('#fetchHistoryBtn');\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 = (msg, isErr=false) => { (statusArea.innerHTML=msg, statusArea.classList.toggle('text-red-600',isErr), statusArea.classList.remove('hidden'), commitList.innerHTML=''); };\n const hideStatus = () => statusArea.classList.add('hidden');\n const setLoading = (isLd) => { (fetchBtn.disabled=isLd, fetchBtn.innerHTML = isLd ? `<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 class=\"ml-2\">Fetching...</span>` : `<i data-lucide=\"search\" class=\"h-4 w-4\"></i><span class=\"ml-2\">Fetch</span>`, 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 \"Error: Could not decode content.\" } };\n const copyToClipboard = async (el, txt) => { const orig = el.textContent; try { await navigator.clipboard.writeText(txt); el.textContent='Copied!'; } catch (err) { el.textContent='Failed!'; } finally { setTimeout(() => { el.textContent = orig; }, 2000); } };\n const highlightModalCode = () => { if(window.hljs) { delete modalCode.dataset.highlighted; window.hljs.highlightElement(modalCode); } };\n\n // --- API Functions ---\n const ghFetch = async (url) => {\n const h={'Accept':'application/vnd.github.v3+json'}; if(window.USER?.PAT){h['Authorization']=`token ${window.USER.PAT}`};\n const r=await fetch(url, {headers:h}); if(!r.ok){const e=await r.json().catch(()=>({message:`API Error: ${r.status}`}));throw new Error(e.message||`API Error: ${r.status}`)}; return r.json();\n };\n const fetchHistory = async () => {\n const val = pathInput.value.trim(); if(!val) return showStatus(\"Please enter a GitHub file path.\", true);\n const p = parsePath(val); if (!p) return showStatus(\"Invalid format. Use: org/repo@branch/file/path\", true);\n const parsed = {owner: p[0], repo: p[1], branch: p[2] || null, path: p[3]};\n localStorage.setItem(CACHE_KEY, val); setLoading(true); hideStatus(); commitList.innerHTML = '';\n try {\n let url = `https://api.github.com/repos/${parsed.owner}/${parsed.repo}/commits?path=${encodeURIComponent(parsed.path)}`;\n if (parsed.branch) url += `&sha=${encodeURIComponent(parsed.branch)}`;\n const commits = await ghFetch(url);\n if (!commits?.length) showStatus(\"No commits found. Check if the path and branch are correct.\", false); else renderCommits(commits, parsed);\n } catch (e) { showStatus(`Error: ${e.message}`, true); } finally { setLoading(false); }\n };\n const fetchFileContent = async (btn) => {\n const { owner, repo, path, sha } = btn.dataset; if (!owner || !repo || !path || !sha) return;\n modalHeader.textContent = `Loading... (${sha.substring(0, 7)})`; modalCode.textContent = 'Loading content...';\n modalCode.className = 'block p-4 text-xs font-mono leading-relaxed hljs'; modal.classList.remove('hidden');\n try {\n const data = await ghFetch(`https://api.github.com/repos/${owner}/${repo}/contents/${path}?ref=${sha}`);\n modalHeader.textContent = `${path} @ ${sha.substring(0, 7)}`;\n modalCode.textContent = data.content ? b64_to_utf8(data.content) : \"Could not retrieve file content.\";\n } catch (e) { modalHeader.textContent = `Error @ ${sha.substring(0, 7)}`; modalCode.textContent = `Error: ${e.message}`; } \n finally { highlightModalCode(); }\n };\n const fetchDiffContent = async (btn) => {\n const { owner, repo, path, sha } = btn.dataset; if (!owner || !repo || !path || !sha) return;\n modalHeader.textContent = `Loading diff... (${sha.substring(0, 7)})`; modalCode.textContent = 'Loading diff content...';\n modalCode.className = 'block p-4 text-xs font-mono leading-relaxed hljs language-diff'; modal.classList.remove('hidden');\n try {\n const data = await ghFetch(`https://api.github.com/repos/${owner}/${repo}/commits/${sha}`);\n const file = data.files?.find(f => f.filename === path);\n modalHeader.textContent = `Diff for ${path} @ ${sha.substring(0, 7)}`;\n modalCode.textContent = file?.patch || \"No diff (patch) found for this file in this commit.\";\n } catch (e) { modalHeader.textContent = `Error loading diff @ ${sha.substring(0, 7)}`; modalCode.textContent = `Error: ${e.message}`; } \n finally { highlightModalCode(); }\n };\n\n // --- Render Functions ---\n const renderCommits = (commits, pInfo) => {\n commitList.innerHTML = commits.map(item => {\n const c = item.commit, author = item.author || c.author;\n const date = new Date(c.author.date).toLocaleString([], { dateStyle: 'medium', timeStyle: 'short' });\n return `\n <div class=\"p-3 border border-gray-200 rounded-lg bg-gray-50/50 hover:bg-gray-100 hover:border-gray-300 transition-all\">\n <p class=\"font-medium text-sm text-gray-800 truncate\">${c.message.split('\\n')[0]}</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 ${author?.avatar_url ? `<img src=\"${author.avatar_url}\" class=\"h-5 w-5 rounded-full\" alt=\"${author.login||''}\">` : '<div class=\"h-5 w-5 rounded-full bg-gray-300\"></div>'}\n <span class=\"font-medium\">${c.author.name}</span>\n <span class=\"hidden md:inline\">&bull;</span>\n <span class=\"flex-shrink-0\">${date}</span>\n </div>\n <div class=\"flex items-center gap-2 self-end sm:self-center flex-shrink-0\">\n <button class=\"view-diff-btn px-3 py-1 text-xs bg-white border border-gray-300 rounded-md shadow-sm hover:bg-gray-200 active:scale-95 transition-all\" data-owner=\"${pInfo.owner}\" data-repo=\"${pInfo.repo}\" data-path=\"${pInfo.path}\" data-sha=\"${item.sha}\">View Diff</button>\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-200 active:scale-95 transition-all\" data-owner=\"${pInfo.owner}\" data-repo=\"${pInfo.repo}\" data-path=\"${pInfo.path}\" data-sha=\"${item.sha}\">View File</button>\n </div>\n </div>\n </div>`;\n }).join('');\n };\n\n // --- Initialization ---\n const init = () => {\n pathInput.value = localStorage.getItem(CACHE_KEY) || '';\n fetchBtn.addEventListener('click', fetchHistory);\n pathInput.addEventListener('keydown', e => e.key==='Enter' && (e.preventDefault(), fetchHistory()));\n commitList.addEventListener('click', e => {\n const vBtn = e.target.closest('.view-content-btn'); if (vBtn) return fetchFileContent(vBtn);\n const dBtn = e.target.closest('.view-diff-btn'); if (dBtn) return fetchDiffContent(dBtn);\n });\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": {}
}
]