File size: 6,568 Bytes
bc56771
 
 
 
8d8f38b
bc56771
 
 
 
 
65b49a4
 
 
 
 
 
 
 
 
 
 
 
bc56771
 
 
65b49a4
 
 
bc56771
65b49a4
 
8d8f38b
65b49a4
 
 
bc56771
 
 
 
 
 
 
65b49a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bc56771
 
65b49a4
bc56771
 
65b49a4
bc56771
 
65b49a4
 
 
 
 
 
 
 
 
 
 
 
 
bc56771
 
65b49a4
 
 
bc56771
65b49a4
 
 
 
bc56771
 
 
 
65b49a4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/>
<title>Code</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css">
</head>
<body class="bg-[#1e1e1e] text-[#ccc] h-screen flex flex-col overflow-hidden">

<div class="bg-[#181818] border-b border-[#2d2d2d] p-2 flex flex-wrap gap-2 items-center text-xs">
  <span class="font-bold text-white"><i class="bi bi-cpu"></i> fiel1986/Code</span>
  <select id="modelSelect" class="bg-[#252526] border border-[#3d3d3d] rounded px-2 py-1.5">
    <option value="groq-llama-3.3-70b">⚡ Groq Llama 3.3 70B</option>
    <option value="groq-llama3-8b">Groq Llama3 8B (rápido)</option>
    <option value="groq-mixtral">Groq Mixtral 8x7B</option>
    <option value="hf-qwen-1.5b">HF Qwen 1.5B Coder (gratis)</option>
  </select>
  <input id="groqKey" placeholder="gsk_... Groq API (tuya)" class="bg-[#252526] border border-[#3d3d3d] rounded px-2 py-1.5 w-44"/>
  <input id="hfKey" placeholder="hf_... HF Token (tuyo)" class="bg-[#252526] border border-[#3d3d3d] rounded px-2 py-1.5 w-44"/>
  <span class="text-[10px] text-zinc-500">🔒 Tus keys se guardan solo en tu navegador (localStorage), no en mi server</span>
  <button onclick="saveKeys()" class="ml-auto bg-blue-600 hover:bg-blue-700 text-white px-3 py-1.5 rounded font-bold">Guardar Keys</button>
</div>

<div class="flex flex-1 overflow-hidden">
  <div class="flex-1 flex flex-col border-r border-[#2d2d2d]">
    <div class="h-8 bg-[#252526] flex items-center px-3 text-xs"><span class="bg-[#1e1e1e] px-3 py-1 border-t-2 border-blue-500">main.py</span><span id="ghostStatus" class="ml-auto text-zinc-500">TAB = aceptar</span></div>
    <div id="monaco" class="flex-1"></div>
  </div>
  <div class="w-[380px] bg-[#252526] flex flex-col">
    <div class="p-3 font-bold text-xs uppercase tracking-widest border-b border-[#2d2d2d]">Chat</div>
    <div id="chat" class="flex-1 overflow-y-auto p-3 space-y-3 text-sm"><div class="bg-[#2d2d2d] p-3 rounded-lg">Pon tus keys arriba → Guarda → Selecciona modelo.<br></div></div>
    <div class="p-2 border-t border-[#2d2d2d] flex gap-2">
      <input id="chatInput" placeholder="Pregunta..." class="flex-1 bg-[#3c3c3c] rounded px-3 py-2 text-sm outline-none"/>
      <button onclick="askChat()" class="bg-blue-600 px-3 rounded"><i class="bi bi-send"></i></button>
    </div>
  </div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.44.0/min/vs/loader.min.js"></script>
<script>
let editor, ghost="";
const MODELS = {
  "groq-llama-3.3-70b": { api:"groq", model:"llama-3.3-70b-versatile" },
  "groq-llama3-8b": { api:"groq", model:"llama3-8b-8192" },
  "groq-mixtral": { api:"groq", model:"mixtral-8x7b-32768" },
  "hf-qwen-1.5b": { api:"hf", model:"Qwen/Qwen2.5-Coder-1.5B-Instruct" }
}
window.onload = ()=>{
  document.getElementById('groqKey').value = localStorage.getItem('groq_key')||"";
  document.getElementById('hfKey').value = localStorage.getItem('hf_key')||"";
}
function saveKeys(){
  localStorage.setItem('groq_key', document.getElementById('groqKey').value.trim());
  localStorage.setItem('hf_key', document.getElementById('hfKey').value.trim());
  alert("Keys guardadas en tu navegador!");
}

require.config({paths:{'vs':'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.44.0/min/vs'}});
require(['vs/editor/editor.main'],()=>{
  editor=monaco.editor.create(document.getElementById('monaco'),{value:"# Escribe codigo\ndef fibonacci(n):\n ", language:'python', theme:'vs-dark', fontSize:14, minimap:{enabled:false}, automaticLayout:true});
  const prov={provideInlineCompletions:async(m,p)=>{if(!ghost) return {items:[]}; return {items:[{insertText:ghost, range:new monaco.Range(p.lineNumber,p.column,p.lineNumber,p.column)}]}},freeInlineCompletions:()=>{}};
  ['python','javascript','cpp'].forEach(l=>monaco.languages.registerInlineCompletionsProvider(l,prov));
  let t; editor.onDidChangeModelContent(()=>{clearTimeout(t); t=setTimeout(runCopilot,700);});
});

async function callAI(prompt){
  const modelKey = document.getElementById('modelSelect').value;
  const cfg = MODELS[modelKey];
  if(cfg.api==="groq"){
    const key = localStorage.getItem('groq_key') || document.getElementById('groqKey').value;
    if(!key) throw "Pon tu gsk_... Groq key arriba";
    const r = await fetch("https://api.groq.com/openai/v1/chat/completions",{method:"POST", headers:{"Content-Type":"application/json", Authorization:`Bearer ${key}`}, body:JSON.stringify({model:cfg.model, messages:[{role:"user", content:prompt}], max_tokens:300, temperature:0.2})});
    const d = await r.json(); return d.choices[0].message.content;
  } else {
    const key = localStorage.getItem('hf_key') || document.getElementById('hfKey').value;
    const r = await fetch(`https://router.huggingface.co/hf-inference/models/${cfg.model}`,{method:"POST", headers:{"Content-Type":"application/json",...(key?{Authorization:`Bearer ${key}`}:{})}, body:JSON.stringify({inputs:prompt, parameters:{max_new_tokens:120, return_full_text:false}})});
    const d = await r.json(); return d[0]?.generated_text || "";
  }
}

async function runCopilot(){
  try{ const code=editor.getValue(); if(code.length<10) return; document.getElementById('ghostStatus').textContent="Pensando..."; let out=await callAI(code.slice(-600)); ghost=out.slice(0,150); if(ghost){ editor.trigger('k','editor.action.inlineSuggest.trigger',{}); document.getElementById('ghostStatus').textContent="TAB para aceptar"; }}catch(e){ document.getElementById('ghostStatus').textContent=e; }
}
async function askChat(){
  const input=document.getElementById('chatInput'); const q=input.value; if(!q) return; const chat=document.getElementById('chat'); const sel=editor.getModel().getValueInRange(editor.getSelection())||editor.getValue().slice(-600);
  chat.innerHTML+=`<div class="bg-blue-600 text-white p-3 rounded-lg ml-6">${q}</div>`; input.value="";
  const load=document.createElement('div'); load.className="bg-[#2d2d2d] p-3 rounded-lg"; load.textContent="Escribiendo..."; chat.appendChild(load); chat.scrollTop=chat.scrollHeight;
  try{ let ans=await callAI(`Codigo:\n${sel}\n\nPregunta:${q}`); load.innerHTML=`<pre class="whitespace-pre-wrap">${ans.replace(/</g,'&lt;')}</pre>`; }catch(e){ load.textContent="Error: "+e; }
}
document.getElementById('chatInput').addEventListener('keydown', e=>{ if(e.key==='Enter') askChat(); });
</script>
</body>
</html>