Skip to content

Commit 6c382fd

Browse files
Initial commit
1 parent 021214e commit 6c382fd

1 file changed

Lines changed: 85 additions & 25 deletions

File tree

repo-intel.html

Lines changed: 85 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@
360360
align-items:center;justify-content:center;backdrop-filter:blur(4px);
361361
}
362362
#key-modal.show{display:flex}
363-
.km-box{background:var(--s1);border:1px solid var(--border);border-radius:var(--r);padding:20px;max-width:380px;width:90%}
363+
.km-box{background:var(--s1);border:1px solid var(--border);border-radius:var(--r);padding:24px;max-width:440px;width:90%}
364364
.km-input{width:100%;background:var(--s2);border:1px solid var(--border);border-radius:5px;padding:8px 12px;font-family:var(--mono);font-size:12px;color:var(--text);outline:none;margin-bottom:10px}
365365
.km-input:focus{border-color:var(--acc)}
366366
.km-btn{background:var(--acc);color:#fff;border:none;border-radius:5px;padding:8px 16px;cursor:pointer;font-family:var(--font);font-size:12px;font-weight:600;width:100%}
@@ -388,8 +388,8 @@
388388
<button id="analyze-btn" type="button" onclick="startAnalysis()">Analyze</button>
389389
</form>
390390
<div class="top-right">
391-
<div class="key-badge" onclick="showKeyModal()" title="Set Groq API key for AI summaries">
392-
<span id="key-indicator">AI Key</span>
391+
<div class="key-badge" onclick="showKeyModal()" title="Set GitHub token (fixes rate limits) + Groq AI key">
392+
<span id="key-indicator">Set Keys</span>
393393
</div>
394394
</div>
395395
</div>
@@ -439,6 +439,10 @@
439439
</div>
440440
<h1 class="landing-title">Repository Intelligence</h1>
441441
<p class="landing-sub">Paste any GitHub repository URL and instantly understand the entire codebase — dependency graph, architecture, health score, AI summaries and more.</p>
442+
<div id="landing-key-notice" style="display:none;align-items:center;gap:8px;background:#0f0f1a;border:1px solid #f59e0b30;border-radius:8px;padding:10px 14px;font-size:11.5px;color:#f59e0b;margin-bottom:12px;max-width:480px;text-align:left">
443+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" style="flex-shrink:0"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z" stroke="#f59e0b" stroke-width="1.5"/><line x1="12" y1="9" x2="12" y2="13" stroke="#f59e0b" stroke-width="1.5" stroke-linecap="round"/><circle cx="12" cy="17" r="1" fill="#f59e0b"/></svg>
444+
<span>No GitHub token set — analysis may hit rate limits (60 req/hr). <button onclick="showKeyModal()" style="background:transparent;border:none;color:#f59e0b;text-decoration:underline;cursor:pointer;font-size:11.5px;padding:0">Add a token ↗</button> for best results.</span>
445+
</div>
442446
<div class="landing-examples">
443447
<div class="ex-btn" onclick="loadExample('facebook/react')">facebook/react</div>
444448
<div class="ex-btn" onclick="loadExample('vercel/next.js')">vercel/next.js</div>
@@ -541,24 +545,27 @@ <h1 class="landing-title">Repository Intelligence</h1>
541545
<!-- KEY MODAL -->
542546
<div id="key-modal" onclick="if(event.target===this)hideKeyModal()">
543547
<div class="km-box">
544-
<div style="font-size:14px;font-weight:600;margin-bottom:4px">Groq API Key</div>
545-
<div style="font-size:11px;color:var(--t2);margin-bottom:12px;line-height:1.6">Required for AI summaries. Get a free key at <a href="https://console.groq.com" target="_blank">console.groq.com</a></div>
548+
<div style="font-size:14px;font-weight:600;margin-bottom:16px">API Keys</div>
549+
550+
<div style="font-size:11px;font-weight:600;color:var(--t2);margin-bottom:4px;text-transform:uppercase;letter-spacing:.5px">GitHub Token <span style="color:var(--ylw)">(recommended)</span></div>
551+
<div style="font-size:11px;color:var(--t3);margin-bottom:8px;line-height:1.6">Avoids rate limits (60 req/hr without token → 5000/hr with token). <a href="https://github.com/settings/tokens/new?scopes=public_repo&description=Devora+Repo+Intel" target="_blank" style="color:var(--acc2)">Generate a token ↗</a> — only needs <code style="font-size:10px;background:var(--s3);padding:1px 4px;border-radius:3px">public_repo</code> scope.</div>
552+
<input class="km-input" id="km-gh-input" type="password" placeholder="ghp_xxxx or github_pat_xxxx" style="margin-bottom:16px">
553+
554+
<div style="font-size:11px;font-weight:600;color:var(--t2);margin-bottom:4px;text-transform:uppercase;letter-spacing:.5px">Groq API Key <span style="color:var(--t3)">(optional)</span></div>
555+
<div style="font-size:11px;color:var(--t3);margin-bottom:8px;line-height:1.6">Powers AI summaries &amp; explanations. Get a free key at <a href="https://console.groq.com" target="_blank" style="color:var(--acc2)">console.groq.com ↗</a></div>
546556
<input class="km-input" id="km-key-input" type="password" placeholder="gsk_xxxx">
547-
<button class="km-btn" onclick="saveKey()">Save Key</button>
557+
558+
<button class="km-btn" onclick="saveKey()">Save Keys</button>
548559
</div>
549560
</div>
550561

551562
<script>
552563
// ── CONFIG ──────────────────────────────────────────────────────────────
553-
const GH_TOKEN = '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';
554-
const GROQ_KEYS = [
555-
'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',
556-
'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',
557-
'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',
558-
'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',
559-
];
564+
// NOTE: Never hardcode API tokens — they get auto-revoked on public repos.
565+
// Tokens are provided by the user and stored in localStorage only.
560566
const GROQ_MODELS = ['llama-3.3-70b-versatile','llama-3.1-8b-instant','gemma2-9b-it'];
561567
let _groqKey = localStorage.getItem('ri_groq') || '';
568+
let _ghToken = localStorage.getItem('ri_gh') || '';
562569
let _gki = 0;
563570

564571
// ── STATE ───────────────────────────────────────────────────────────────
@@ -580,22 +587,51 @@ <h1 class="landing-title">Repository Intelligence</h1>
580587
};
581588

582589
// ── KEY MANAGEMENT ──────────────────────────────────────────────────────
583-
function showKeyModal(){document.getElementById('key-modal').classList.add('show');document.getElementById('km-key-input').value=_groqKey;}
590+
function _updateKeyBadge(){
591+
const hasGh=!!_ghToken, hasGroq=!!_groqKey;
592+
document.getElementById('key-indicator').textContent=
593+
hasGh&&hasGroq?'✓ GH + AI':hasGh?'✓ GH Token':hasGroq?'✓ AI Key':'⚡ Set Keys';
594+
}
595+
function showKeyModal(){
596+
document.getElementById('key-modal').classList.add('show');
597+
document.getElementById('km-key-input').value=_groqKey;
598+
document.getElementById('km-gh-input').value=_ghToken;
599+
}
584600
function hideKeyModal(){document.getElementById('key-modal').classList.remove('show');}
585-
function saveKey(){_groqKey=document.getElementById('km-key-input').value.trim();localStorage.setItem('ri_groq',_groqKey);document.getElementById('key-indicator').textContent=_groqKey?'✓ Key Set':'⚡ AI Key';hideKeyModal();}
586-
if(_groqKey) document.getElementById('key-indicator').textContent='✓ Key Set';
601+
function saveKey(){
602+
_groqKey=document.getElementById('km-key-input').value.trim();
603+
_ghToken=document.getElementById('km-gh-input').value.trim();
604+
localStorage.setItem('ri_groq',_groqKey);
605+
if(_ghToken) localStorage.setItem('ri_gh',_ghToken);
606+
else localStorage.removeItem('ri_gh');
607+
_updateKeyBadge();
608+
hideKeyModal();
609+
}
610+
_updateKeyBadge();
587611

588612
// ── GITHUB API ──────────────────────────────────────────────────────────
589613
async function ghFetch(path){
590-
const r=await fetch('https://api.github.com'+path,{headers:{'Authorization':'Bearer '+GH_TOKEN,'Accept':'application/vnd.github.v3+json'}});
614+
const headers={'Accept':'application/vnd.github.v3+json'};
615+
if(_ghToken) headers['Authorization']='Bearer '+_ghToken;
616+
const r=await fetch('https://api.github.com'+path,{headers});
617+
if(r.status===401){
618+
// Token is invalid — clear it so user knows to re-enter
619+
_ghToken=''; localStorage.removeItem('ri_gh'); _updateKeyBadge();
620+
throw new Error('GitHub token invalid or expired. Click ⚡ Set Keys to add a new one.');
621+
}
622+
if(r.status===403){
623+
const isRateLimit=(await r.clone().json().catch(()=>({}))).message?.includes('rate limit');
624+
if(isRateLimit&&!_ghToken) throw new Error('GitHub rate limit hit (60 req/hr for unauthenticated). Click ⚡ Set Keys to add a GitHub token for 5000 req/hr.');
625+
throw new Error('GitHub API 403: '+path);
626+
}
591627
if(!r.ok) throw new Error('GitHub API '+r.status+': '+path);
592628
return r.json();
593629
}
594630
async function ghSafe(path){try{return await ghFetch(path);}catch{return null;}}
595631

596632
// ── GROQ AI ─────────────────────────────────────────────────────────────
597633
async function callGroq(prompt,maxTok=600){
598-
const key=_groqKey||null;
634+
if(!_groqKey) return null; // No key — AI features disabled, not an error
599635
const tryOne=async(k,m)=>{
600636
try{
601637
const r=await fetch('https://api.groq.com/openai/v1/chat/completions',{
@@ -609,10 +645,7 @@ <h1 class="landing-title">Repository Intelligence</h1>
609645
return d.choices?.[0]?.message?.content?.trim()||null;
610646
}catch{return null;}
611647
};
612-
if(key){for(const m of GROQ_MODELS){const t=await tryOne(key,m);if(t)return t;}}
613-
for(let i=_gki;i<GROQ_KEYS.length;i++){
614-
for(const m of GROQ_MODELS){const t=await tryOne(GROQ_KEYS[i],m);if(t){_gki=i;return t;}}
615-
}
648+
for(const m of GROQ_MODELS){const t=await tryOne(_groqKey,m);if(t)return t;}
616649
return null;
617650
}
618651

@@ -630,23 +663,38 @@ <h1 class="landing-title">Repository Intelligence</h1>
630663
async function startAnalysis(){
631664
const input=document.getElementById('url-input').value;
632665
const repo=parseRepoUrl(input);
633-
if(!repo){alert('Please enter a valid GitHub repo URL or owner/name');return;}
666+
if(!repo){showInlineError('Please enter a valid GitHub repo URL (e.g. github.com/owner/repo)');return;}
634667
S.repo=repo;
635668
document.getElementById('analyze-btn').disabled=true;
636669
document.getElementById('analyze-btn').textContent='Analyzing…';
670+
clearInlineError();
637671

638672
showLoading();
639673
try{
640674
await runAnalysis();
641675
}catch(e){
642676
console.error(e);
643-
alert('Error analyzing repository: '+e.message);
677+
const isAuthErr=e.message.includes('token')||e.message.includes('401')||e.message.includes('rate limit')||e.message.includes('403');
678+
showInlineError(e.message, isAuthErr);
644679
showLanding();
645680
}
646681
document.getElementById('analyze-btn').disabled=false;
647682
document.getElementById('analyze-btn').textContent='Analyze';
648683
}
649684

685+
function showInlineError(msg, showKeyBtn=false){
686+
let el=document.getElementById('inline-error');
687+
if(!el){
688+
el=document.createElement('div');
689+
el.id='inline-error';
690+
el.style.cssText='position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:999;background:#1a0f0f;border:1px solid #f8717140;border-radius:8px;padding:10px 16px;font-size:12px;color:#f87171;display:flex;align-items:center;gap:10px;max-width:520px;box-shadow:0 4px 24px #00000060;';
691+
document.body.appendChild(el);
692+
}
693+
el.innerHTML=`<svg width="14" height="14" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="#f87171" stroke-width="1.5"/><line x1="12" y1="8" x2="12" y2="12" stroke="#f87171" stroke-width="2" stroke-linecap="round"/><circle cx="12" cy="16" r="1" fill="#f87171"/></svg><span style="flex:1">${msg}</span>${showKeyBtn?`<button onclick="showKeyModal();clearInlineError()" style="background:#f8717118;border:1px solid #f8717140;color:#f87171;border-radius:5px;padding:4px 10px;font-size:11px;cursor:pointer;white-space:nowrap">⚡ Set Keys</button>`:''}<button onclick="clearInlineError()" style="background:transparent;border:none;color:#f87171;cursor:pointer;font-size:16px;padding:0 0 0 4px">×</button>`;
694+
el.style.display='flex';
695+
}
696+
function clearInlineError(){const el=document.getElementById('inline-error');if(el)el.style.display='none';}
697+
650698
function step(id,state){
651699
const el=document.getElementById(id);
652700
if(!el)return;
@@ -1699,7 +1747,19 @@ <h2 style="font-size:16px;font-weight:700;margin-bottom:6px;letter-spacing:-.3px
16991747

17001748
// Init
17011749
document.getElementById('url-input').addEventListener('keydown',e=>{if(e.key==='Enter')startAnalysis();});
1750+
1751+
// Show no-token warning on landing if user hasn't set a GH token
1752+
function showLandingKeyNotice(){
1753+
const notice=document.getElementById('landing-key-notice');
1754+
if(notice) notice.style.display=_ghToken?'none':'flex';
1755+
}
1756+
1757+
// Patch showKeyModal/saveKey to refresh the notice
1758+
const _origSave=saveKey;
1759+
saveKey=function(){_origSave();showLandingKeyNotice();};
1760+
17021761
showLanding();
1762+
showLandingKeyNotice();
17031763
</script>
17041764
</body>
1705-
</html>
1765+
</html>

0 commit comments

Comments
 (0)