Files
store/github-utilities/git-reset.sune
2025-09-08 12:07:11 -07:00

28 lines
16 KiB
JSON

[
{
"id": "pyaok99",
"name": "GitHub Reset Hard",
"pinned": false,
"avatar": "",
"url": "gh://sune-org/store@main/git-reset.sune",
"updatedAt": 1756931146474,
"settings": {
"model": "openai/gpt-5",
"temperature": 1,
"top_p": 0.97,
"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": "<!-- Sune: GitHub Time Machine -->\n<div id=\"gitReverterSune\" class=\"w-full max-w-3xl mx-auto p-4 md:p-6 bg-white rounded-2xl border border-gray-200 shadow-sm font-sans text-gray-800\">\n\n <!-- Styles for this Sune -->\n <style>\n /* Custom scrollbar for commit list */\n #gr_commitsList::-webkit-scrollbar { width: 6px; }\n #gr_commitsList::-webkit-scrollbar-track { background: #f1f5f9; }\n #gr_commitsList::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }\n #gr_commitsList::-webkit-scrollbar-thumb:hover { background: #94a3b8; }\n \n /* Animation for the modal */\n @keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes scaleIn {\n from { opacity: 0; transform: scale(0.95) translateY(10px); }\n to { opacity: 1; transform: scale(1) translateY(0); }\n }\n #gr_confirmResetModal .modal-bg { animation: fadeIn 0.2s ease-out forwards; }\n #gr_confirmResetModal .modal-content { animation: scaleIn 0.2s ease-out forwards; }\n </style>\n\n <!-- Sune Header -->\n <div class=\"flex items-center justify-between pb-4 border-b border-gray-200\">\n <h1 class=\"text-xl font-bold flex items-center gap-2\">\n <i data-lucide=\"history\" class=\"w-6 h-6 text-gray-600\"></i>\n GitHub Time Machine\n </h1>\n </div>\n\n <!-- Input Form -->\n <div class=\"mt-4\">\n <label for=\"gr_repoPathInput\" class=\"text-sm font-medium text-gray-700\">Repository Path</label>\n <div class=\"mt-1 flex flex-col sm:flex-row items-stretch gap-2\">\n <input type=\"text\" id=\"gr_repoPathInput\" placeholder=\"owner/repo@branch\" class=\"flex-grow w-full px-3 py-2 bg-gray-50 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-black focus:border-black transition-all\">\n <button id=\"gr_loadCommitsBtn\" class=\"w-full sm:w-auto px-5 py-2 bg-black text-white font-semibold rounded-lg hover:bg-gray-800 active:scale-95 transition-all flex items-center justify-center gap-2 disabled:bg-gray-400 disabled:cursor-not-allowed\">\n <i data-lucide=\"arrow-down-circle\" class=\"w-4 h-4\"></i>\n <span>Load Commits</span>\n </button>\n </div>\n </div>\n\n <!-- Error and Loading Display -->\n <div id=\"gr_statusDisplay\" class=\"mt-4\"></div>\n\n <!-- Commits List -->\n <div id=\"gr_commitsContainer\" class=\"mt-4 hidden\">\n <h2 class=\"text-md font-semibold text-gray-600 mb-2\">Commit History</h2>\n <div id=\"gr_commitsList\" class=\"max-h-[50vh] overflow-y-auto border border-gray-200 rounded-lg bg-gray-50/50 divide-y divide-gray-200\">\n <!-- Commit items will be injected here -->\n </div>\n </div>\n\n <!-- Confirmation Modal -->\n <div id=\"gr_confirmResetModal\" class=\"hidden fixed inset-0 z-[100] p-4 flex items-center justify-center\">\n <div class=\"modal-bg absolute inset-0 bg-black/50 backdrop-blur-sm\"></div>\n <div class=\"modal-content relative w-full max-w-md bg-white rounded-2xl shadow-2xl p-6 border border-gray-200\">\n <div class=\"text-center\">\n <div class=\"mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100\">\n <i data-lucide=\"alert-triangle\" class=\"h-6 w-6 text-red-600\"></i>\n </div>\n <h3 class=\"mt-4 text-lg font-bold text-gray-900\">Force Push Required</h3>\n <div class=\"mt-2 text-sm text-gray-600 space-y-2\">\n <p>This is a highly destructive action. You are about to <strong class=\"font-bold text-red-700\">permanently reset</strong> the branch:</p>\n <p id=\"gr_confirmBranchInfo\" class=\"font-mono bg-gray-100 p-2 rounded-md text-xs\"></p>\n <p>This will rewrite the public history of the branch. Please type the repository name <strong id=\"gr_confirmRepoName\" class=\"font-bold\"></strong> to confirm.</p>\n </div>\n </div>\n <div class=\"mt-4\">\n <input type=\"text\" id=\"gr_confirmRepoInput\" autocomplete=\"off\" class=\"w-full text-center px-3 py-2 bg-white border-2 border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-500 focus:border-red-500 transition-all\">\n </div>\n <div class=\"mt-5 grid grid-cols-1 sm:grid-cols-2 gap-3\">\n <button id=\"gr_cancelResetBtn\" type=\"button\" class=\"w-full inline-flex justify-center rounded-md border border-gray-300 px-4 py-2 bg-white text-base font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500\">\n Cancel\n </button>\n <button id=\"gr_confirmResetBtn\" type=\"button\" class=\"w-full inline-flex justify-center rounded-md border border-transparent px-4 py-2 bg-red-600 text-base font-medium text-white shadow-sm hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 disabled:bg-red-300 disabled:cursor-not-allowed\">\n Reset and Force Push\n </button>\n </div>\n </div>\n </div>\n\n\n <!-- Sune Script -->\n <script>\n (() => {\n const SUNE_ID = 'sune_git_reverter';\n const GITHUB_API_BASE = 'https://api.github.com';\n\n // --- Web Worker Code ---\n const workerCode = `\n self.onmessage = async (e) => {\n const { action, payload } = e.data;\n const { path, token, sha } = payload;\n const [owner, repoWithBranch] = (path || '').split('/');\n const [repo, branch] = (repoWithBranch || '').split('@');\n\n if (!token) {\n self.postMessage({ type: 'error', message: 'GitHub token not found. Please set it in Account Settings.' });\n return;\n }\n\n if (!owner || !repo || !branch) {\n self.postMessage({ type: 'error', message: 'Invalid path format. Use: owner/repo@branch' });\n return;\n }\n\n const headers = {\n 'Authorization': \\`Bearer \\${token}\\`,\n 'Accept': 'application/vnd.github.v3+json',\n 'X-GitHub-Api-Version': '2022-11-28'\n };\n\n try {\n if (action === 'getCommits') {\n const url = \\`https://api.github.com/repos/\\${owner}/\\${repo}/commits?sha=\\${branch}\\`;\n const response = await fetch(url, { headers });\n const data = await response.json();\n\n if (!response.ok) {\n throw new Error(data.message || \\`HTTP error! status: \\${response.status}\\`);\n }\n self.postMessage({ type: 'commitsResult', data });\n\n } else if (action === 'resetBranch') {\n if (!sha) {\n throw new Error('Commit SHA is missing for reset operation.');\n }\n const url = \\`https://api.github.com/repos/\\${owner}/\\${repo}/git/refs/heads/\\${branch}\\`;\n const body = JSON.stringify({ sha, force: true });\n \n const response = await fetch(url, {\n method: 'PATCH',\n headers: { ...headers, 'Content-Type': 'application/json' },\n body: body\n });\n \n const data = await response.json();\n if (!response.ok) {\n throw new Error(data.message || \\`Failed to reset branch. Status: \\${response.status}\\`);\n }\n self.postMessage({ type: 'resetSuccess', message: 'Branch reset and force pushed successfully.' });\n }\n } catch (error) {\n self.postMessage({ type: 'error', message: error.message });\n }\n };\n `;\n\n // --- Main Sune Logic ---\n const suneRoot = document.getElementById('gitReverterSune');\n const repoPathInput = suneRoot.querySelector('#gr_repoPathInput');\n const loadCommitsBtn = suneRoot.querySelector('#gr_loadCommitsBtn');\n const statusDisplay = suneRoot.querySelector('#gr_statusDisplay');\n const commitsContainer = suneRoot.querySelector('#gr_commitsContainer');\n const commitsList = suneRoot.querySelector('#gr_commitsList');\n \n const confirmModal = suneRoot.querySelector('#gr_confirmResetModal');\n const confirmBranchInfo = suneRoot.querySelector('#gr_confirmBranchInfo');\n const confirmRepoName = suneRoot.querySelector('#gr_confirmRepoName');\n const confirmRepoInput = suneRoot.querySelector('#gr_confirmRepoInput');\n const cancelResetBtn = suneRoot.querySelector('#gr_cancelResetBtn');\n const confirmResetBtn = suneRoot.querySelector('#gr_confirmResetBtn');\n \n let worker;\n let state = {\n isLoading: false,\n commits: [],\n resetTarget: null // { sha, owner, repo, branch }\n };\n\n const getGhToken = () => localStorage.getItem('gh_token') || '';\n\n const renderStatus = (type, message) => {\n const colors = {\n error: 'bg-red-100 border-red-300 text-red-800',\n success: 'bg-green-100 border-green-300 text-green-800',\n loading: 'bg-blue-100 border-blue-300 text-blue-800'\n };\n const icons = {\n error: 'alert-circle',\n success: 'check-circle-2',\n loading: 'loader-2'\n };\n const iconClass = type === 'loading' ? 'animate-spin' : '';\n\n statusDisplay.innerHTML = `\n <div class=\"flex items-center gap-3 p-3 text-sm font-medium rounded-lg border ${colors[type]}\">\n <i data-lucide=\"${icons[type]}\" class=\"w-5 h-5 flex-shrink-0 ${iconClass}\"></i>\n <span>${message}</span>\n </div>\n `;\n window.lucide && window.lucide.createIcons();\n };\n \n const renderCommits = (commitData) => {\n if (!commitData || commitData.length === 0) {\n commitsList.innerHTML = `<div class=\"p-4 text-center text-gray-500\">No commits found for this branch.</div>`;\n return;\n }\n state.commits = commitData;\n commitsList.innerHTML = commitData.map((c, index) => {\n const msg = c.commit.message.split('\\\\n')[0];\n const author = c.commit.author;\n const shortSha = c.sha.substring(0, 7);\n const date = new Date(author.date).toLocaleString(undefined, {\n year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'\n });\n\n return `\n <div class=\"p-3 hover:bg-white/70 transition-colors duration-150\">\n <div class=\"flex items-center justify-between\">\n <div class=\"flex items-center gap-3 min-w-0\">\n ${c.author ? `<img src=\"${c.author.avatar_url}\" class=\"w-8 h-8 rounded-full bg-gray-200\" alt=\"${author.name}\">` : ''}\n <div class=\"min-w-0\">\n <p class=\"font-semibold text-gray-900 truncate\" title=\"${msg}\">${msg}</p>\n <p class=\"text-xs text-gray-500\">\n <strong>${author.name}</strong> committed on ${date}\n </p>\n </div>\n </div>\n <button data-sha=\"${c.sha}\" title=\"Reset branch to this commit\" class=\"reset-btn ml-2 flex-shrink-0 px-3 py-1.5 text-xs font-semibold text-red-700 bg-red-100 rounded-md hover:bg-red-200 active:scale-95 transition-all\">\n Reset\n </button>\n </div>\n <div class=\"mt-2 text-xs text-gray-600 font-mono bg-gray-200/50 inline-block px-2 py-0.5 rounded\">\n ${shortSha}\n </div>\n </div>\n `;\n }).join('');\n };\n\n const handleLoadCommits = () => {\n if (state.isLoading) return;\n const path = repoPathInput.value.trim();\n if (!path) {\n renderStatus('error', 'Please enter a repository path.');\n return;\n }\n\n const token = getGhToken();\n if (!token) {\n renderStatus('error', 'GitHub token not found. Please set it in Account Settings.');\n return;\n }\n\n state.isLoading = true;\n loadCommitsBtn.disabled = true;\n commitsContainer.classList.add('hidden');\n renderStatus('loading', `Fetching commits for ${path}...`);\n \n localStorage.setItem(`${SUNE_ID}_last_path`, path);\n worker.postMessage({ action: 'getCommits', payload: { path, token } });\n };\n \n const openResetModal = (sha) => {\n const [owner, repoWithBranch] = (repoPathInput.value.trim() || '').split('/');\n const [repo, branch] = (repoWithBranch || '').split('@');\n\n if (!owner || !repo || !branch) return;\n \n state.resetTarget = { sha, owner, repo, branch };\n \n confirmBranchInfo.textContent = `${owner}/${repo}@${branch}`;\n confirmRepoName.textContent = repo;\n confirmRepoInput.value = '';\n confirmResetBtn.disabled = true;\n \n confirmModal.classList.remove('hidden');\n document.body.style.overflow = 'hidden';\n confirmRepoInput.focus();\n };\n \n const closeResetModal = () => {\n confirmModal.classList.add('hidden');\n document.body.style.overflow = '';\n state.resetTarget = null;\n };\n\n const handleConfirmReset = () => {\n if (!state.resetTarget || state.isLoading) return;\n \n state.isLoading = true;\n confirmResetBtn.disabled = true;\n confirmResetBtn.innerHTML = '<i data-lucide=\"loader-2\" class=\"w-5 h-5 animate-spin mx-auto\"></i>';\n window.lucide && window.lucide.createIcons();\n\n const token = getGhToken();\n const { sha, owner, repo, branch } = state.resetTarget;\n const path = `${owner}/${repo}@${branch}`;\n\n worker.postMessage({ action: 'resetBranch', payload: { path, token, sha } });\n };\n \n function init() {\n // Setup Worker\n const workerBlob = new Blob([workerCode], { type: 'application/javascript' });\n worker = new Worker(URL.createObjectURL(workerBlob));\n \n worker.onmessage = (e) => {\n const { type, data, message } = e.data;\n state.isLoading = false;\n loadCommitsBtn.disabled = false;\n \n if (type === 'commitsResult') {\n statusDisplay.innerHTML = '';\n commitsContainer.classList.remove('hidden');\n renderCommits(data);\n } else if (type === 'resetSuccess') {\n closeResetModal();\n renderStatus('success', message);\n // Reload commits to show updated history\n setTimeout(handleLoadCommits, 1000);\n } else if (type === 'error') {\n renderStatus('error', message);\n commitsContainer.classList.add('hidden');\n }\n };\n\n // Setup Event Listeners\n loadCommitsBtn.addEventListener('click', handleLoadCommits);\n repoPathInput.addEventListener('keydown', e => e.key === 'Enter' && handleLoadCommits());\n\n commitsList.addEventListener('click', e => {\n const btn = e.target.closest('.reset-btn');\n if (btn && btn.dataset.sha) {\n openResetModal(btn.dataset.sha);\n }\n });\n \n cancelResetBtn.addEventListener('click', closeResetModal);\n confirmResetBtn.addEventListener('click', handleConfirmReset);\n confirmModal.querySelector('.modal-bg').addEventListener('click', closeResetModal);\n \n confirmRepoInput.addEventListener('input', () => {\n const matches = confirmRepoInput.value === state.resetTarget?.repo;\n confirmResetBtn.disabled = !matches;\n });\n\n // Load initial state\n repoPathInput.value = localStorage.getItem(`${SUNE_ID}_last_path`) || '';\n window.lucide && window.lucide.createIcons();\n }\n\n // Run sune\n init();\n\n })();\n </script>\n</div>\n",
"extension_html": "<sune src='https://raw.githubusercontent.com/sune-org/store/refs/heads/main/sync.sune' private />",
"script": ""
}
}
]