Jarvis_protocol / index.html
protocol-jarvis's picture
Update index.html
f785ea6 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CodeForge · AI Code Generator</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700;800&family=Space+Grotesk:wght@400;600&display=swap');
```
:root {
--accent: #ff8c00;
--accent-dim: rgba(255,140,0,0.15);
--accent-glow: rgba(255,140,0,0.5);
--bg: #09090e;
--surface: rgba(255,255,255,0.03);
--border: rgba(255,140,0,0.12);
--text: #d8d8d8;
--muted: rgba(255,255,255,0.25);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: var(--bg);
font-family: 'JetBrains Mono', monospace;
color: var(--text);
min-height: 100vh;
overflow-x: hidden;
}
/* Grid + orbs */
.grid-bg {
position: fixed;
inset: 0;
background-image:
linear-gradient(rgba(255,140,0,0.025) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,140,0,0.025) 1px, transparent 1px);
background-size: 48px 48px;
pointer-events: none;
z-index: 0;
}
.orb {
position: fixed;
border-radius: 50%;
pointer-events: none;
z-index: 0;
background: radial-gradient(circle, rgba(255,140,0,0.07) 0%, transparent 70%);
}
.orb1 { top: 10%; left: 5%; width: 500px; height: 500px; }
.orb2 { bottom: 10%; right: 5%; width: 400px; height: 400px; }
/* Layout */
.shell {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
height: 100dvh;
max-width: 920px;
margin: 0 auto;
padding: 0 16px;
}
/* ── Header ── */
.header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 0 12px;
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
.header-left { display: flex; align-items: center; gap: 12px; }
.logo-icon {
font-size: 26px;
color: var(--accent);
animation: logoPulse 3s ease-in-out infinite;
line-height: 1;
}
@keyframes logoPulse {
0%,100% { filter: drop-shadow(0 0 6px var(--accent-glow)); }
50% { filter: drop-shadow(0 0 18px var(--accent-glow)); }
}
.header h1 {
font-size: 17px;
font-weight: 800;
color: var(--accent);
letter-spacing: 6px;
}
.header-right {
display: flex;
align-items: center;
gap: 8px;
}
.stat-pill {
background: var(--accent-dim);
border: 1px solid var(--border);
border-radius: 20px;
color: rgba(255,140,0,0.7);
font-size: 10px;
letter-spacing: 1px;
padding: 4px 10px;
}
/* ── Buttons ── */
button {
font-family: 'JetBrains Mono', monospace;
cursor: pointer;
border: none;
border-radius: 3px;
transition: all 0.18s;
}
.btn-primary {
background: var(--accent);
color: #0a0a0f;
font-weight: 800;
font-size: 11px;
letter-spacing: 2px;
padding: 10px 22px;
}
.btn-primary:hover:not(:disabled) {
background: #ffaa33;
box-shadow: 0 0 18px var(--accent-glow);
transform: translateY(-1px);
}
.btn-primary:disabled { opacity: 0.4; cursor: not-allowed; }
.btn-ghost {
background: var(--surface);
border: 1px solid rgba(255,255,255,0.07);
color: var(--muted);
font-size: 10px;
letter-spacing: 1.5px;
padding: 8px 14px;
}
.btn-ghost:hover { background: rgba(255,255,255,0.07); color: rgba(255,255,255,0.6); }
.btn-copy {
background: rgba(255,140,0,0.08);
border: 1px solid rgba(255,140,0,0.2);
color: rgba(255,140,0,0.7);
font-size: 9px;
letter-spacing: 1px;
padding: 4px 10px;
}
.btn-copy:hover { background: rgba(255,140,0,0.18); color: var(--accent); }
.btn-copy.copied {
background: rgba(80,220,100,0.1);
border-color: rgba(80,220,100,0.3);
color: rgba(80,220,100,0.9);
}
/* ── Language bar ── */
.lang-bar {
flex-shrink: 0;
padding: 10px 0;
border-bottom: 1px solid rgba(255,140,0,0.07);
overflow-x: auto;
-ms-overflow-style: none;
scrollbar-width: none;
}
.lang-bar::-webkit-scrollbar { display: none; }
.lang-inner {
display: flex;
gap: 6px;
white-space: nowrap;
}
.lang-btn {
background: var(--surface);
border: 1px solid rgba(255,255,255,0.06);
color: rgba(255,255,255,0.35);
font-size: 10px;
font-family: 'JetBrains Mono', monospace;
letter-spacing: 0.5px;
padding: 5px 13px;
border-radius: 2px;
transition: all 0.15s;
}
.lang-btn:hover { color: rgba(255,255,255,0.6); background: rgba(255,255,255,0.06); }
.lang-btn.active {
background: var(--accent-dim);
border-color: rgba(255,140,0,0.35);
color: var(--accent);
font-weight: 700;
}
/* ── Examples ── */
.examples {
flex-shrink: 0;
padding: 10px 0;
overflow-x: auto;
-ms-overflow-style: none;
scrollbar-width: none;
}
.examples::-webkit-scrollbar { display: none; }
.examples-inner {
display: flex;
gap: 7px;
white-space: nowrap;
}
.ex-btn {
background: rgba(255,140,0,0.04);
border: 1px solid rgba(255,140,0,0.13);
color: rgba(255,140,0,0.55);
font-size: 10px;
font-family: 'JetBrains Mono', monospace;
padding: 6px 13px;
border-radius: 2px;
transition: all 0.15s;
}
.ex-btn:hover { background: rgba(255,140,0,0.1); color: var(--accent); }
/* ── Chat ── */
.chat-area {
flex: 1;
overflow-y: auto;
padding: 16px 4px;
scroll-behavior: smooth;
}
.chat-area::-webkit-scrollbar { width: 4px; }
.chat-area::-webkit-scrollbar-thumb { background: rgba(255,140,0,0.25); border-radius: 4px; }
.chat-area::-webkit-scrollbar-track { background: transparent; }
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
gap: 12px;
color: rgba(255,255,255,0.1);
font-size: 12px;
letter-spacing: 2px;
text-align: center;
}
.empty-state .big { font-size: 40px; opacity: 0.15; }
.message {
display: flex;
gap: 10px;
margin-bottom: 16px;
animation: msgIn 0.28s ease forwards;
}
@keyframes msgIn {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
.message.user { flex-direction: row-reverse; }
.avatar {
flex-shrink: 0;
width: 30px;
height: 30px;
border-radius: 3px;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
font-weight: 700;
}
.message.user .avatar {
background: rgba(255,140,0,0.12);
border: 1px solid rgba(255,140,0,0.25);
color: var(--accent);
}
.message.assistant .avatar {
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.08);
color: rgba(255,255,255,0.4);
}
.bubble {
max-width: 82%;
padding: 12px 14px;
border-radius: 4px;
font-size: 13px;
line-height: 1.65;
word-break: break-word;
}
.message.user .bubble {
background: rgba(255,140,0,0.08);
border: 1px solid rgba(255,140,0,0.18);
color: #ffb347;
border-radius: 6px 2px 6px 6px;
}
.message.assistant .bubble {
background: rgba(255,255,255,0.025);
border: 1px solid rgba(255,255,255,0.06);
color: #ccc;
border-radius: 2px 6px 6px 6px;
}
/* Inline markdown */
.bubble strong { color: #ffcc80; font-weight: 700; }
.bubble em { color: #aad4f5; font-style: italic; }
.bubble code {
background: rgba(0,0,0,0.5);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 2px;
padding: 1px 5px;
font-size: 12px;
color: #ffb347;
font-family: 'JetBrains Mono', monospace;
}
/* Code blocks */
.code-block {
margin: 12px 0;
border-radius: 4px;
overflow: hidden;
border: 1px solid rgba(255,140,0,0.14);
}
.code-header {
display: flex;
justify-content: space-between;
align-items: center;
background: rgba(0,0,0,0.5);
padding: 7px 12px;
border-bottom: 1px solid rgba(255,255,255,0.05);
}
.code-lang {
font-size: 10px;
font-weight: 700;
color: var(--accent);
letter-spacing: 2px;
text-transform: uppercase;
}
.code-block pre {
background: rgba(0,0,0,0.55);
padding: 14px 16px;
overflow-x: auto;
margin: 0;
}
.code-block pre code {
background: none;
border: none;
padding: 0;
color: #d4d4d4;
font-size: 12.5px;
line-height: 1.65;
}
/* Typing indicator */
.typing-wrap {
display: flex;
align-items: center;
gap: 5px;
padding: 4px 0;
}
.dot {
width: 6px; height: 6px;
background: var(--accent);
border-radius: 50%;
animation: dotPop 1.1s ease-in-out infinite;
}
.dot:nth-child(2) { animation-delay: 0.18s; }
.dot:nth-child(3) { animation-delay: 0.36s; }
@keyframes dotPop {
0%,100% { opacity: 0.2; transform: scale(0.75); }
50% { opacity: 1; transform: scale(1.25); }
}
.typing-label {
font-size: 10px;
color: rgba(255,140,0,0.45);
letter-spacing: 1px;
margin-left: 4px;
}
/* ── Input area ── */
.input-area {
flex-shrink: 0;
padding: 12px 0 16px;
border-top: 1px solid rgba(255,140,0,0.09);
}
textarea {
width: 100%;
padding: 12px 14px;
background: rgba(255,255,255,0.035);
border: 1px solid rgba(255,140,0,0.15);
border-radius: 4px;
color: #e0e0e0;
font-family: 'JetBrains Mono', monospace;
font-size: 13px;
line-height: 1.6;
resize: none;
outline: none;
transition: border-color 0.2s, box-shadow 0.2s;
min-height: 60px;
max-height: 160px;
overflow-y: auto;
}
textarea:focus {
border-color: rgba(255,140,0,0.4);
box-shadow: 0 0 0 3px rgba(255,140,0,0.04);
}
.btn-row {
display: flex;
gap: 8px;
margin-top: 9px;
align-items: center;
}
.btn-row .btn-primary { flex: 1; }
.hint {
font-size: 9px;
color: rgba(255,255,255,0.13);
letter-spacing: 1px;
text-align: right;
margin-top: 5px;
}
/* ── Password Gate ── */
.gate {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100dvh;
gap: 0;
position: relative;
z-index: 1;
}
.gate-card {
background: rgba(255,140,0,0.03);
border: 1px solid var(--border);
border-radius: 6px;
padding: 40px;
max-width: 380px;
width: 90%;
text-align: center;
backdrop-filter: blur(12px);
}
.gate-icon {
font-size: 52px;
color: var(--accent);
animation: logoPulse 3s ease-in-out infinite;
margin-bottom: 12px;
display: block;
}
.gate-title {
font-size: 26px;
font-weight: 800;
color: var(--accent);
letter-spacing: 8px;
margin-bottom: 4px;
}
.gate-sub {
font-size: 10px;
color: rgba(255,140,0,0.35);
letter-spacing: 4px;
margin-bottom: 28px;
}
.gate-label {
font-size: 10px;
color: rgba(255,140,0,0.4);
letter-spacing: 3px;
margin-bottom: 10px;
}
.gate-input {
width: 100%;
padding: 13px 16px;
background: rgba(0,0,0,0.55);
border: 1px solid rgba(255,140,0,0.2);
border-radius: 3px;
color: var(--accent);
font-family: 'JetBrains Mono', monospace;
font-size: 14px;
letter-spacing: 4px;
text-align: center;
outline: none;
transition: all 0.2s;
margin-bottom: 18px;
}
.gate-input:focus {
border-color: rgba(255,140,0,0.55);
box-shadow: 0 0 14px rgba(255,140,0,0.1);
}
.gate-error {
color: #ff6b6b;
font-size: 11px;
letter-spacing: 1px;
margin-top: 10px;
min-height: 16px;
}
/* Scrollbar */
::-webkit-scrollbar { width: 4px; height: 4px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(255,140,0,0.2); border-radius: 4px; }
@media (max-width: 600px) {
.header h1 { font-size: 13px; letter-spacing: 4px; }
.bubble { max-width: 92%; font-size: 12px; }
.btn-primary { font-size: 10px; padding: 9px 14px; }
}
</style>
```
</head>
<body>
<div class="grid-bg"></div>
<div class="orb orb1"></div>
<div class="orb orb2"></div>
<div id="root"></div>
<script>
// ─────────────────────────────────────────────
// CONFIG
// ─────────────────────────────────────────────
const PASSWORD = "CodeForge#2026";
const MODEL = "claude-sonnet-4-20250514";
const API_URL = "https://api.anthropic.com/v1/messages";
const SYSTEM_PROMPT = `You are CodeForge, an elite AI code generator. When a user asks for code:
1. Briefly explain what you're building (1-2 sentences, plain text).
2. Output the complete, working code inside a fenced code block with the correct language tag.
3. After the block, add one short tip or note if relevant.
Keep explanations concise. Prioritise clean, well-commented, production-ready code.
If the user is following up or asking for modifications, continue naturally.`;
const LANGUAGES = [
{ id:"auto", label:"✦ Auto" },
{ id:"javascript", label:"JS" },
{ id:"typescript", label:"TS" },
{ id:"python", label:"Python" },
{ id:"html", label:"HTML/CSS" },
{ id:"react", label:"⚛ React" },
{ id:"sql", label:"SQL" },
{ id:"bash", label:"Bash" },
{ id:"rust", label:"🦀 Rust" },
{ id:"go", label:"Go" },
{ id:"java", label:"Java" },
{ id:"cpp", label:"C++" },
];
const EXAMPLES = [
"Build a REST API with JWT auth",
"Responsive navbar with hamburger",
"Binary search algorithm",
"SQL user analytics query",
"Python web scraper",
"React counter with history",
"Debounce utility function",
"CSS glassmorphism card",
];
// ─────────────────────────────────────────────
// STATE
// ─────────────────────────────────────────────
const state = {
unlocked: false,
messages: [], // {role, content}
lang: "auto",
count: 0,
loading: false,
};
// ─────────────────────────────────────────────
// API CALL
// ─────────────────────────────────────────────
async function callClaude(userPrompt) {
const langNote = state.lang !== "auto"
? ` [Preferred language: ${state.lang}]`
: "";
// Build history + current message
const history = state.messages.map(m => ({ role: m.role, content: m.content }));
history.push({ role: "user", content: userPrompt + langNote });
const res = await fetch(API_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: MODEL,
max_tokens: 2048,
system: SYSTEM_PROMPT,
messages: history,
}),
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(err?.error?.message || `HTTP ${res.status}`);
}
const data = await res.json();
return data.content?.find(b => b.type === "text")?.text ?? "No response.";
}
// ─────────────────────────────────────────────
// MARKDOWN → HTML (minimal, safe)
// ─────────────────────────────────────────────
function escHtml(s) {
return s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;");
}
function renderMarkdown(text) {
// Extract fenced code blocks first (protect from inline processing)
const codeBlocks = [];
const placeholder = "\x00CB\x00";
let result = text.replace(/```(\w*)\n?([\s\S]*?)```/g, (_, lang, code) => {
const id = codeBlocks.length;
const langLabel = lang || "code";
const safeCode = escHtml(code.trim());
codeBlocks.push(
`<div class="code-block">` +
`<div class="code-header"><span class="code-lang">${langLabel}</span>` +
`<button class="btn-copy" onclick="copyCode(this)">⎘ COPY</button></div>` +
`<pre><code>${safeCode}</code></pre></div>`
```
);
return placeholder + id + placeholder;
});
// Inline code (before escaping remaining text)
result = result.replace(/`([^`]+)`/g, (_, c) => `<code>${escHtml(c)}</code>`);
// Bold & italic on remaining text segments
// Process text nodes between placeholders
const parts = result.split(new RegExp(placeholder.replace(/\x00/g,"\\x00") + "(\\d+)" + placeholder.replace(/\x00/g,"\\x00")));
let final = "";
for (let i = 0; i < parts.length; i++) {
if (i % 2 === 0) {
// Text segment — apply inline formatting
let seg = escHtml(parts[i]);
seg = seg.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
seg = seg.replace(/\*(.+?)\*/g, "<em>$1</em>");
seg = seg.replace(/\n/g, "<br>");
final += seg;
} else {
// Code block index
final += codeBlocks[parseInt(parts[i])];
}
}
return final;
```
}
// ─────────────────────────────────────────────
// COPY CODE
// ─────────────────────────────────────────────
window.copyCode = async (btn) => {
const pre = btn.closest(”.code-block”).querySelector(“pre code”);
try {
await navigator.clipboard.writeText(pre.innerText);
btn.textContent = “✓ COPIED”;
btn.classList.add(“copied”);
setTimeout(() => { btn.textContent = “⎘ COPY”; btn.classList.remove(“copied”); }, 1800);
} catch {
btn.textContent = “ERR”;
}
};
// ─────────────────────────────────────────────
// RENDER
// ─────────────────────────────────────────────
const root = document.getElementById(“root”);
function renderGate() {
root.innerHTML = ` <div class="gate"> <div class="gate-card"> <span class="gate-icon">⟨/⟩</span> <div class="gate-title">CODEFORGE</div> <div class="gate-sub">AI · CODE · GENERATOR</div> <div class="gate-label">🔐 ENTER ACCESS CODE</div> <input class="gate-input" id="pwInput" type="password" placeholder="••••••••••••••" autofocus onkeydown="if(event.key==='Enter') tryUnlock()"> <button class="btn-primary" style="width:100%" onclick="tryUnlock()">UNLOCK ACCESS →</button> <div class="gate-error" id="gateErr"></div> </div> </div>`;
document.getElementById(“pwInput”).focus();
}
function renderApp() {
// Build messages
let msgHtml = “”;
for (const m of state.messages) {
const isUser = m.role === “user”;
msgHtml += ` <div class="message ${isUser ? "user" : "assistant"}"> <div class="avatar">${isUser ? "U" : "⟨/⟩"}</div> <div class="bubble">${isUser ? escHtml(m.content).replace(/\n/g,"<br>") : renderMarkdown(m.content)}</div> </div>`;
}
```
if (state.loading) {
msgHtml += `
<div class="message assistant">
<div class="avatar">⟨/⟩</div>
<div class="bubble">
<div class="typing-wrap">
<div class="dot"></div><div class="dot"></div><div class="dot"></div>
<span class="typing-label">generating...</span>
</div>
</div>
</div>`;
}
const langBtns = LANGUAGES.map(l =>
`<button class="lang-btn ${state.lang === l.id ? "active" : ""}" onclick="setLang('${l.id}')">${l.label}</button>`
).join("");
const exBtns = EXAMPLES.map(e =>
`<button class="ex-btn" onclick="useExample(this)">▸ ${escHtml(e)}</button>`
).join("");
root.innerHTML = `
<div class="shell">
<div class="header">
<div class="header-left">
<span class="logo-icon">⟨/⟩</span>
<h1>CODEFORGE</h1>
</div>
<div class="header-right">
<span class="stat-pill">⚡ ${state.count} prompts</span>
<button class="btn-ghost" onclick="lockApp()">🔒 Lock</button>
<button class="btn-ghost" onclick="clearChat()">🗑 Clear</button>
</div>
</div>
<div class="lang-bar">
<div class="lang-inner">${langBtns}</div>
</div>
<div class="examples">
<div class="examples-inner">${exBtns}</div>
</div>
<div class="chat-area" id="chatArea">
${msgHtml || `<div class="empty-state"><div class="big">⟨/⟩</div><div>Describe the code you need to get started</div></div>`}
</div>
<div class="input-area">
<textarea id="userInput" rows="2"
placeholder="Describe what you want to build…"
onkeydown="handleKey(event)"></textarea>
<div class="btn-row">
<button class="btn-primary" id="sendBtn" onclick="sendMessage()" ${state.loading ? "disabled" : ""}>▶ GENERATE</button>
</div>
<div class="hint">↵ SEND · ⇧↵ NEW LINE</div>
</div>
</div>`;
// Scroll chat to bottom
const chat = document.getElementById("chatArea");
if (chat) chat.scrollTop = chat.scrollHeight;
// Restore focus
const inp = document.getElementById("userInput");
if (inp) inp.focus();
```
}
function render() {
state.unlocked ? renderApp() : renderGate();
}
// ─────────────────────────────────────────────
// EVENT HANDLERS (on window so inline onclick works)
// ─────────────────────────────────────────────
window.tryUnlock = () => {
const v = document.getElementById(“pwInput”).value;
if (v === PASSWORD) {
state.unlocked = true;
render();
} else {
const err = document.getElementById(“gateErr”);
err.textContent = “❌ Invalid access code. Try again.”;
const inp = document.getElementById(“pwInput”);
inp.value = “”;
inp.focus();
// Shake animation
inp.style.animation = “none”;
inp.style.borderColor = “rgba(255,80,80,0.6)”;
setTimeout(() => { inp.style.borderColor = “”; }, 800);
}
};
window.setLang = (id) => {
state.lang = id;
// Update buttons without full re-render
document.querySelectorAll(”.lang-btn”).forEach(b => {
const label = LANGUAGES.find(l => l.id === id)?.label;
b.classList.toggle(“active”, b.textContent.trim() === label ||
(id === “auto” && b.textContent.trim() === “✦ Auto”));
});
// Re-render just lang bar cleanly
document.querySelectorAll(”.lang-btn”).forEach(b => b.classList.remove(“active”));
document.querySelectorAll(”.lang-btn”).forEach(b => {
const match = LANGUAGES.find(l => l.id === id);
if (b.textContent.trim() === match?.label) b.classList.add(“active”);
});
};
window.useExample = (btn) => {
const text = btn.textContent.replace(/^▸\s*/, “”);
const inp = document.getElementById(“userInput”);
if (inp) { inp.value = text; inp.focus(); }
};
window.handleKey = (e) => {
if (e.key === “Enter” && !e.shiftKey) { e.preventDefault(); sendMessage(); }
};
window.sendMessage = async () => {
const inp = document.getElementById(“userInput”);
const text = (inp?.value || “”).trim();
if (!text || state.loading) return;
inp.value = “”;
```
state.messages.push({ role: "user", content: text });
state.count++;
state.loading = true;
render();
try {
const reply = await callClaude(text);
state.messages.push({ role: "assistant", content: reply });
} catch (err) {
state.messages.push({
role: "assistant",
content: `⚠️ **Error**: ${err.message}\n\nPlease try again.`
});
} finally {
state.loading = false;
render();
}
```
};
window.clearChat = () => {
if (state.messages.length === 0) return;
if (confirm(“Clear all messages?”)) {
state.messages = [];
state.count = 0;
render();
}
};
window.lockApp = () => {
if (confirm(“Lock CodeForge? You’ll need the password again.”)) {
state.unlocked = false;
state.messages = [];
state.count = 0;
render();
}
};
// ─────────────────────────────────────────────
// BOOT
// ─────────────────────────────────────────────
render();
</script>
</body>
</html>