Files
.sune/inline-commit.sune

1 line
5.9 KiB
Plaintext

[{"id":"jbcwown","name":"Inline Commit","pinned":false,"avatar":"","url":"gh://multipleof4/.sune/inline-commit.sune","updatedAt":1757666694200,"settings":{"model":"google/gemini-2.5-pro","temperature":"","top_p":"","top_k":"","frequency_penalty":"","repetition_penalty":"","min_p":"","top_a":"","verbosity":"","reasoning_effort":"default","system_prompt":"","html":"<!--\nSune: GitHub Commit Helper\nVersion: 1.7\n-->\n<div id=\"sune_github_commit\" x-data=\"{v:'1.7'}\" class=\"hidden\"></div>\n<script>\n(()=>{\n const suneEl=document.getElementById('sune_github_commit');\n if(!suneEl){console.error(\"Sune container not found.\");return}\n const SUNE_NAME='[Sune: GitHub Commit Helper]', SUNE_V=suneEl.getAttribute('x-data').match(/v:'(.*?)'/)[1];\n console.log(`${SUNE_NAME} Initializing v${SUNE_V}`);\n\n const commitFile=async(btn,owner,repo,branch,path,content)=>{\n console.log(`${SUNE_NAME} commitFile called:`,{owner,repo,branch,path});\n const token=window.USER?.githubToken;\n if(!token){alert('GitHub token not set in Account Settings > API.');return}\n \n btn.disabled=true;\n btn.innerHTML='<span class=\"relative flex h-3 w-3\"><span class=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-sky-400 opacity-75\"></span><span class=\"relative inline-flex rounded-full h-3 w-3 bg-sky-500\"></span></span> Committing...';\n\n const URL=`https://api.github.com/repos/${owner}/${repo}/contents/${path}`, H={'Authorization':`Bearer ${token}`,'Accept':'application/vnd.github.v3+json'};\n try{\n let sha;\n const r=await fetch(`${URL}?ref=${branch}`,{headers:H});\n if(r.ok)sha=(await r.json()).sha;\n else if(r.status!==404)throw new Error(`GET failed: ${r.statusText}`);\n\n const msg=`feat: update ${path.split('/').pop()}`, b64c=btoa(unescape(encodeURIComponent(content)));\n const body={message:msg,content:b64c,sha,branch};\n const pr=await fetch(URL,{method:'PUT',headers:H,body:JSON.stringify(body)});\n \n if(!pr.ok)throw new Error(`PUT failed: ${(await pr.json()).message}`);\n \n btn.className=btn.className.replace(/bg-slate-100 text-slate-700 hover:bg-slate-200/,'bg-green-100 text-green-800');\n btn.innerHTML='<i data-lucide=\"check\" class=\"h-3.5 w-3.5\"></i> Success';\n }catch(err){\n console.error(`${SUNE_NAME} GitHub Commit Failed:`,err);\n btn.className=btn.className.replace(/bg-slate-100 text-slate-700 hover:bg-slate-200/,'bg-red-100 text-red-800');\n btn.innerHTML='<i data-lucide=\"x\" class=\"h-3.5 w-3.5\"></i> Failed';\n setTimeout(()=>{\n btn.disabled=false;\n btn.className='ml-2 inline-flex items-center gap-1.5 rounded-md bg-slate-100 px-2 py-1 text-xs font-medium text-slate-700 hover:bg-slate-200 transition-colors';\n btn.innerHTML='<i data-lucide=\"github\" class=\"h-3.5 w-3.5\"></i> Commit';\n window.lucide?.createIcons();\n },3e3);\n }finally{window.lucide?.createIcons()}\n };\n\n const processBubble=bubble=>{\n if(!bubble)return;\n bubble.querySelectorAll('p').forEach(p=>{\n if(p.querySelector('button.commit-btn'))return;\n const m=p.innerHTML.match(/<code>([a-zA-Z0-9.\\-_]+)\\/([a-zA-Z0-9.\\-_]+)@([a-zA-Z0-9.\\-_]+)\\/([^<]+)<\\/code>/);\n if(!m)return;\n \n let nextEl=p.nextElementSibling, preEl=null;\n while(nextEl){\n if(nextEl.tagName==='PRE'){preEl=nextEl;break}\n nextEl=nextEl.nextElementSibling;\n }\n if(!preEl)return;\n\n const[_,owner,repo,branch,path]=m, code=preEl.querySelector('code')?.innerText;\n if(!code)return;\n\n console.log(`${SUNE_NAME} Match found for ${path.trim()}`);\n const btn=document.createElement('button');\n btn.className='commit-btn ml-2 inline-flex items-center gap-1.5 rounded-md bg-slate-100 px-2 py-1 text-xs font-medium text-slate-700 hover:bg-slate-200 transition-colors';\n btn.innerHTML='<i data-lucide=\"github\" class=\"h-3.5 w-3.5\"></i> Commit';\n btn.onclick=()=>commitFile(btn,owner,repo,branch,path.trim(),code);\n p.append(btn);\n window.lucide?.createIcons();\n });\n };\n\n const scanExisting=()=>document.querySelectorAll('#messages .msg-bubble:not([class*=\"bg-gray-50\"])').forEach(processBubble);\n \n const observer=new MutationObserver(mutations=>{\n for(const m of mutations){\n for(const node of m.addedNodes){\n if(node.nodeType===1){\n const bubbles=node.matches?.('.msg-bubble')?[node]:node.querySelectorAll?.('.msg-bubble');\n bubbles?.forEach(b=>{if(!b.matches('[class*=\"bg-gray-50\"]'))processBubble(b)});\n }\n }\n }\n });\n \n const newResponseListener = e => {\n const id = e?.detail?.message?.id;\n if (!id) return;\n console.log(`${SUNE_NAME} Event 'sune:newSuneResponse' detected for message id: ${id}`);\n // Wait to ensure DOM is fully updated after streaming/rendering\n setTimeout(() => {\n console.log(`${SUNE_NAME} Processing bubble for id: ${id} after delay.`);\n processBubble(window.getBubbleById(id));\n }, 150);\n };\n\n const composerEl = window.el?.composer;\n const chatContainer = window.el?.messages;\n\n if(chatContainer && composerEl){\n observer.observe(chatContainer, {childList: true, subtree: true});\n composerEl.addEventListener('sune:newSuneResponse', newResponseListener);\n scanExisting();\n console.log(`${SUNE_NAME} Observer and event listener attached. Initial scan complete.`);\n }\n\n suneEl.addEventListener('sune:unmount',()=>{\n observer.disconnect();\n if (composerEl) composerEl.removeEventListener('sune:newSuneResponse', newResponseListener);\n console.log(`${SUNE_NAME} Unmounted. Observer and event listener disconnected.`);\n });\n})();\n</script>\n","extension_html":"<sune src='https://raw.githubusercontent.com/sune-org/store/refs/heads/main/sync.sune' private></sune>","hide_composer":false},"storage":{}}]