mirror of
https://github.com/sune-org/store.git
synced 2026-01-13 16:17:58 +00:00
1 line
16 KiB
JSON
1 line
16 KiB
JSON
[{"id":"zp2je1g","name":"Github Fetch","pinned":false,"avatar":"data:image/webp;base64,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","url":"gh://sune-org/store/github-utilities/fetch.sune","updatedAt":1757656189362,"settings":{"model":"","temperature":"","top_p":"","top_k":"","frequency_penalty":"","repetition_penalty":"","min_p":"","top_a":"","verbosity":"","reasoning_effort":"default","system_prompt":"","html":"<!-- Sune: GitHub Blob Fetch -->\n<!-- Version: 1.6.0 -->\n<div id=\"ghFetchSune\" class=\"p-1.5 bg-slate-100 rounded-lg shadow-sm font-sans\" x-data>\n <div class=\"flex items-center justify-between\">\n <div class=\"flex items-center gap-1.5\">\n <i data-lucide=\"github\" class=\"h-4 w-4 text-slate-600\"></i>\n <span class=\"text-xs font-medium text-slate-800\">GitHub Fetch</span>\n <span class=\"text-[10px] text-slate-400\">v1.6.0</span>\n </div>\n <div class=\"flex items-center gap-1\">\n <button id=\"ghFetchAllBtn\" type=\"button\" class=\"rounded-md px-2 py-1 text-xs font-semibold bg-slate-800 text-white hover:bg-slate-700 active:scale-95 transition-all\">\n Fetch All\n </button>\n <button type=\"button\" id=\"ghAddRowBtn\" class=\"h-7 w-7 rounded-md bg-slate-200/80 text-slate-600 hover:bg-white active:shadow-inner active:scale-95 flex items-center justify-center transition-all\" title=\"Add URL\">\n <i data-lucide=\"plus\" class=\"h-4 w-4\"></i>\n </button>\n <button type=\"button\" id=\"ghHelpBtn\" class=\"h-7 w-7 rounded-md bg-slate-200/80 text-slate-600 hover:bg-white active:shadow-inner active:scale-95 flex items-center justify-center transition-all\" title=\"Help\">\n <i data-lucide=\"help-circle\" class=\"h-4 w-4\"></i>\n </button>\n </div>\n </div>\n\n <div id=\"ghRows\" class=\"mt-2 space-y-1.5\"></div>\n <div id=\"ghStatus\" class=\"mt-1.5 px-1 text-[11px] text-slate-600 min-h-[1em]\"></div>\n</div>\n\n<script>\n(() => {\n const root=document.getElementById('ghFetchSune');\n if(!root)return;\n\n const $=(s,c=root)=>c.querySelector(s);\n const els={\n rows:$('#ghRows'),addBtn:$('#ghAddRowBtn'),\n fetchAllBtn:$('#ghFetchAllBtn'),helpBtn:$('#ghHelpBtn'),\n status:$('#ghStatus'),\n };\n\n const icons=()=>{try{window.lucide&&window.lucide.createIcons({attrs:{'aria-hidden':'true'}})}catch(e){console.error(\"Lucide err:\",e)}};\n const getUrlsKey=()=>{const id=window.SUNE?.id||'gh_fetch_default';return`gh_fetch_sune_${id}_urls_v3`;};\n const loadUrls=()=>{try{const d=localStorage.getItem(getUrlsKey());if(d){const a=JSON.parse(d);return Array.isArray(a)?a.map(x=>String(x||'')):['']}return['']}catch{return['']}};\n const saveUrls=urls=>{try{localStorage.setItem(getUrlsKey(),JSON.stringify(urls.map(u=>(u||'').trim())))}catch{}};\n \n const setStatus=(el,msg,kind)=>{const colors={error:'text-red-600',ok:'text-emerald-600',info:el.id==='ghStatus'?'text-slate-600':'text-slate-500'};el.textContent=msg||'';el.className=`${el.dataset.baseClass} ${colors[kind]||colors.info}`};\n const setGlobalStatus=(msg,kind='info')=>setStatus(els.status,msg,kind);\n const setRowStatus=(idx,msg,kind='info')=>{const n=$(`[data-role=\"row-status\"][data-index=\"${idx}\"]`);if(n)setStatus(n,msg,kind)};\n\n const spinnerSvg=c=>`<svg class=\"animate-spin ${c}\" viewBox=\"0 0 24 24\"><circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\" fill=\"none\"></circle><path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8v4A4 4 0 004 12z\"></path></svg>`;\n const repoCache={};\n \n const ghFetch=async(url,opts={})=>{const token=window.globalStore?.ghToken;const headers=opts.headers||{};if(token)headers['Authorization']=`Bearer ${token}`;const res=await fetch(url,{...opts,headers});if(!res.ok)throw new Error(`Request failed: HTTP ${res.status}`);return res};\n\n async function getRepoInfo(owner,repo){const k=`${owner}/${repo}`;if(repoCache[k])return repoCache[k];const res=await ghFetch(`https://api.github.com/repos/${owner}/${repo}`);const data=await res.json();if(!data.default_branch)throw new Error('No default branch.');repoCache[k]=data;return data}\n \n async function parseInput(u){try{let lines=null;const hashMatch=u.match(/#L(\\d+)(?:-L(\\d+))?$/i);if(hashMatch)lines={start:Math.max(1,+hashMatch[1]),end:Math.max(+hashMatch[1],+hashMatch[2]||+hashMatch[1])};const urlStr=u.split('#')[0];try{const url=new URL(urlStr);let owner,repo,ref,path,raw,blob;if(url.hostname==='github.com'){const p=url.pathname.split('/').filter(Boolean);if(p.length>=4&&(p[2]==='blob'||p[2]==='raw')){[owner,repo,,ref]=p;path=p.slice(4).join('/');return{type:'file',payload:{raw:`https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${path}`,blob:`https://github.com/${owner}/${repo}/blob/${ref}/${path}${hashMatch?hashMatch[0]:''}`,path,lines}}}}else if(url.hostname==='raw.githubusercontent.com'){const p=url.pathname.split('/').filter(Boolean);if(p.length>=4){[owner,repo,ref]=p;path=p.slice(3).join('/');return{type:'file',payload:{raw:url.href,blob:`https://github.com/${owner}/${repo}/blob/${ref}/${path}${hashMatch?hashMatch[0]:''}`,path,lines}}}}}catch(e){}const pathParts=urlStr.split('/').filter(Boolean);if(pathParts.length>=2){const[owner,repo]=pathParts;if(pathParts.length>2){const path=pathParts.slice(2).join('/');const{default_branch:ref}=await getRepoInfo(owner,repo);return{type:'file',payload:{raw:`https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${path}`,blob:`https://github.com/${owner}/${repo}/blob/${ref}/${path}${hashMatch?hashMatch[0]:''}`,path,lines}}}else if(pathParts.length===2){const{default_branch}=await getRepoInfo(owner,repo);return{type:'repo',payload:{owner,repo,default_branch,blob:`https://github.com/${owner}/${repo}`}}}}}catch(err){return null}return null}\n\n const isBinary=p=>/\\.(png|jpe?g|gif|bmp|webp|ico|tif?f|svg|zip|rar|7z|tar|gz|bz2|xz|pdf|docx?|xlsx?|pptx?|mp[34]|wav|ogg|webm|mov|avi|mkv|woff2?|ttf|otf|eot|exe|dll|so|dylib|bin|o|a|class|jar|pyc|iso|img|dmg)$/i.test(p);\n const guessLang=p=>(p.split('.').pop()||'').toLowerCase();\n \n async function fetchFileContent({raw,blob,path,lines}){\n if(isBinary(path)) return {md:`From: ${blob}\\n\\n*Binary file (\\`${path}\\`) - content not displayed.*`};\n const res=await ghFetch(raw,{cache:'no-store'});\n let content=await res.text();\n if(lines){const textLines=content.replace(/\\r\\n/g,'\\n').split('\\n');content=textLines.slice(lines.start-1,lines.end).join('\\n')}\n const lang=guessLang(path);\n return{md:`From: ${blob}\\n\\n\\`\\`\\`${lang}\\n${content}\\n\\`\\`\\``}\n }\n\n async function fetchRepoContent({owner,repo,default_branch,blob},idx){\n setRowStatus(idx,'Fetching tree...','info');\n const branchRes=await ghFetch(`https://api.github.com/repos/${owner}/${repo}/branches/${default_branch}`);\n const{commit:{sha}}=await branchRes.json();\n const treeRes=await ghFetch(`https://api.github.com/repos/${owner}/${repo}/git/trees/${sha}?recursive=1`);\n const{tree,truncated}=await treeRes.json();\n if(truncated)throw new Error('Repo too large to fetch completely.');\n const files=tree.filter(i=>i.type==='blob');\n if(!files.length)throw new Error('No files found in repo.');\n setRowStatus(idx,`Fetching ${files.length} file(s)...`,'info');\n const contents=await Promise.all(files.map(async file=>{\n if(isBinary(file.path))return{path:file.path,content:null,binary:true};\n try{const res=await ghFetch(`https://raw.githubusercontent.com/${owner}/${repo}/${sha}/${file.path}`);return{path:file.path,content:await res.text()}}\n catch{return{path:file.path,content:'Error: Could not fetch file.'}}}));\n const mdParts=[`From Repository: ${blob}`];\n for(const{path,content,binary}of contents){\n mdParts.push(`\\n---\\n\\n**File:** \\`${path}\\``);\n if(binary)mdParts.push(`\\n\\n*Binary file - content not displayed.*`);\n else mdParts.push(`\\n\\n\\`\\`\\`${guessLang(path)}\\n${content}\\n\\`\\`\\``)}\n return{md:mdParts.join('')}\n }\n \n let urls=loadUrls();\n\n const rowTpl=(idx,v='')=>`<div><div class=\"flex items-stretch gap-1\"><input data-role=\"url\" data-index=\"${idx}\" type=\"text\" placeholder=\"user/repo or user/repo/file or full URL...\" class=\"flex-1 min-w-0 rounded-md border-0 bg-white shadow-inner px-2.5 py-1.5 text-xs text-slate-800 placeholder:text-slate-400 focus:ring-2 focus:ring-slate-400 transition\" aria-label=\"GitHub Path or URL\" value=\"${v.replace(/\"/g,'"')}\"><button data-role=\"copy\" data-index=\"${idx}\" type=\"button\" class=\"shrink-0 rounded-md w-8 bg-slate-200/80 text-slate-600 hover:bg-white active:shadow-inner active:scale-95 flex items-center justify-center transition-all\" title=\"Copy URL\"><i data-lucide=\"copy\" class=\"h-4 w-4\"></i></button><button data-role=\"fetch-one\" data-index=\"${idx}\" type=\"button\" class=\"shrink-0 rounded-md px-2.5 text-xs font-semibold bg-slate-800 text-white hover:bg-slate-700 active:scale-95 transition-all\">Fetch</button><button data-role=\"remove\" data-index=\"${idx}\" type=\"button\" class=\"shrink-0 rounded-md w-8 text-slate-500 bg-slate-200/80 hover:bg-red-100 hover:text-red-600 active:scale-95 flex items-center justify-center transition-all\" title=\"Remove\"><i data-lucide=\"trash-2\" class=\"h-4 w-4\"></i></button></div><div data-role=\"row-status\" data-index=\"${idx}\" data-base-class=\"mt-1 px-1 text-[11px] min-h-[1em]\" class=\"mt-1 px-1 text-[11px] text-slate-500 min-h-[1em]\"></div></div>`;\n\n const renderRows=()=>{if(!urls.length)urls=[''];els.rows.innerHTML=urls.map((u,i)=>rowTpl(i,u)).join('');icons()};\n \n async function postToChat(md){if(window.USER?.log)await window.USER.log(md);else throw new Error('Chat injection failed.')}\n\n async function fetchRow(index){const input=$(`input[data-role=\"url\"][data-index=\"${index}\"]`);const fetchBtn=$(`button[data-role=\"fetch-one\"][data-index=\"${index}\"]`);if(!input||!fetchBtn)return!1;setRowStatus(index,'');const url=(input.value||'').trim();if(!url){setRowStatus(index,'Please enter a path or URL.','error');return!1}const prevHTML=fetchBtn.innerHTML;fetchBtn.innerHTML=spinnerSvg('h-4 w-4');fetchBtn.disabled=input.disabled=true;try{setRowStatus(index,'Parsing...','info');const parsed=await parseInput(url);if(!parsed)throw new Error('Invalid URL or repo not found.');let result;if(parsed.type==='file'){result=await fetchFileContent(parsed.payload)}else{result=await fetchRepoContent(parsed.payload,index)}await postToChat(result.md);setRowStatus(index,'Added to chat.','ok');return!0}catch(err){setRowStatus(index,err.message||String(err),'error');return!1}finally{fetchBtn.innerHTML=prevHTML;fetchBtn.disabled=input.disabled=false}}\n\n els.rows.addEventListener('click',async e=>{const b=e.target.closest('button[data-role]');if(!b)return;const r=b.dataset.role,i=+b.dataset.index;if(r==='copy'){const n=$(`input[data-index=\"${i}\"]`);if(n&&n.value){try{await navigator.clipboard.writeText(n.value);setRowStatus(i,'Copied!','ok')}catch{setRowStatus(i,'Copy failed.','error')}}else{setRowStatus(i,'Nothing to copy.','info')}}else if(r==='fetch-one')await fetchRow(i);else if(r==='remove'){urls.length>1?urls.splice(i,1):(urls[0]='');saveUrls(urls);renderRows()}});\n els.rows.addEventListener('input',e=>{const i=e.target.closest('input[data-role=\"url\"]');if(i){urls[+i.dataset.index]=i.value;saveUrls(urls)}});\n els.rows.addEventListener('keydown',e=>{if(e.key==='Enter'&&e.target.matches('input[data-role=\"url\"]')){e.preventDefault();fetchRow(+e.target.dataset.index)}});\n \n els.addBtn.addEventListener('click',()=>{urls.push('');saveUrls(urls);renderRows();$(`input[data-index=\"${urls.length-1}\"]`)?.focus()});\n\n els.fetchAllBtn.addEventListener('click',async()=>{const list=urls.map((u,i)=>({url:u.trim(),i})).filter(item=>item.url);if(!list.length)return setGlobalStatus('No URLs to fetch. Add one with +.','error');const prevHTML=els.fetchAllBtn.innerHTML;els.fetchAllBtn.innerHTML=spinnerSvg('h-4 w-4');els.fetchAllBtn.disabled=true;setGlobalStatus(`Fetching ${list.length} item(s)...`);let s=0,f=0;for(const item of list){const ok=await fetchRow(item.i);ok?s++:f++}els.fetchAllBtn.innerHTML=prevHTML;els.fetchAllBtn.disabled=false;if(f===0)setGlobalStatus(`Fetched ${s} item(s) successfully.`,'ok');else setGlobalStatus(`Completed: ${s} succeeded, ${f} failed.`, 'error')});\n \n els.helpBtn.addEventListener('click',()=>alert(['GitHub Fetch Sune - Help','','• Add URL fields with the + button.','• You can fetch single files or entire repositories.','','FORMATS:',' - Repo: user/repo',' - File: user/repo/path/to/file.js',' - Full URL: .../blob/main/file.js','','• For files, you can add #L10-L20 to specify lines.','• Fetching a whole repo may take time and is only possible for smaller repositories.','','Pro Tip: Set a GitHub Token in Account Settings to fetch from private repos and avoid API rate limits.'].join('\\n')));\n\n els.status.dataset.baseClass = 'mt-1.5 px-1 text-[11px] min-h-[1em]';\n renderRows();\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,"presence_penalty":0,"max_tokens":0},"storage":{}}] |