|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>RetroChat 3000 - AI Terminal</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<script> |
|
|
tailwind.config = { |
|
|
theme: { |
|
|
extend: { |
|
|
colors: { |
|
|
'primary': '#808080', |
|
|
'secondary': '#c0c0c0' |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
</script> |
|
|
<style> |
|
|
body { |
|
|
font-family: 'Courier New', monospace; |
|
|
background-color: #000080; |
|
|
color: #00ff00; |
|
|
} |
|
|
.retro-border { |
|
|
border: 3px double #c0c0c0; |
|
|
background-color: #000080; |
|
|
} |
|
|
.retro-button { |
|
|
background-color: #c0c0c0; |
|
|
border: 2px outset #c0c0c0; |
|
|
color: #000000; |
|
|
padding: 2px 6px; |
|
|
font-family: 'Courier New', monospace; |
|
|
cursor: pointer; |
|
|
} |
|
|
.retro-button:hover { |
|
|
background-color: #a0a0a0; |
|
|
} |
|
|
.retro-button:active { |
|
|
border: 2px inset #c0c0c0; |
|
|
} |
|
|
.retro-input { |
|
|
background-color: #ffffff; |
|
|
border: 2px inset #c0c0c0; |
|
|
color: #000000; |
|
|
font-family: 'Courier New', monospace; |
|
|
padding: 2px 4px; |
|
|
} |
|
|
.blink { |
|
|
animation: blink 1s infinite; |
|
|
} |
|
|
@keyframes blink { |
|
|
0%, 100% { opacity: 1; } |
|
|
50% { opacity: 0; } |
|
|
} |
|
|
.scan-line { |
|
|
position: fixed; |
|
|
top: 0; |
|
|
left: 0; |
|
|
width: 100%; |
|
|
height: 2px; |
|
|
background: rgba(0, 255, 0, 0.3); |
|
|
animation: scan 2s linear infinite; |
|
|
pointer-events: none; |
|
|
} |
|
|
@keyframes scan { |
|
|
0% { top: 0; } |
|
|
100% { top: 100%; } |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="min-h-screen p-4"> |
|
|
<div class="scan-line"></div> |
|
|
|
|
|
<div class="retro-border p-4 max-w-4xl mx-auto"> |
|
|
|
|
|
<div class="text-center mb-6"> |
|
|
<h1 class="text-3xl font-bold text-green-400 mb-2"> |
|
|
ββββββββββββββββββββββββββββββββββββ |
|
|
<br>β RETROCHAT 3000 - AI TERMINAL β |
|
|
<br>ββββββββββββββββββββββββββββββββββββ |
|
|
</h1> |
|
|
<div class="text-yellow-300 text-sm"> |
|
|
<span class="blink">β </span> SYSTEM READY - v1.0.0 |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="retro-border p-4 mb-6"> |
|
|
<h2 class="text-xl text-green-300 mb-3">β CONFIGURATION PANEL β</h2> |
|
|
|
|
|
<div class="space-y-3"> |
|
|
<div> |
|
|
<label class="block text-green-200 mb-1">API KEY:</label> |
|
|
<input type="password" id="apiKey" placeholder="Enter your API key..." |
|
|
class="retro-input w-full p-2"> |
|
|
</div> |
|
|
|
|
|
<div> |
|
|
<label class="block text-green-200 mb-1">CUSTOM API ENDPOINT:</label> |
|
|
<input type="text" id="apiEndpoint" placeholder="https://api.example.com/v1/chat/completions" |
|
|
class="retro-input w-full p-2"> |
|
|
</div> |
|
|
|
|
|
<div class="flex space-x-2"> |
|
|
<button onclick="saveSettings()" class="retro-button flex-1"> |
|
|
[SAVE CONFIG] |
|
|
</button> |
|
|
<button onclick="loadSettings()" class="retro-button flex-1"> |
|
|
[LOAD CONFIG] |
|
|
</button> |
|
|
<button onclick="clearSettings()" class="retro-button flex-1"> |
|
|
[CLEAR CONFIG] |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="retro-border p-4 mb-6"> |
|
|
<h2 class="text-xl text-green-300 mb-3">β CHAT INTERFACE β</h2> |
|
|
|
|
|
|
|
|
<div id="chatHistory" class="h-64 overflow-y-auto retro-border p-3 mb-3 bg-black text-green-400 font-mono text-sm"> |
|
|
<div>> SYSTEM: Welcome to RetroChat 3000. Configure your API settings and start chatting!</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="flex space-x-2"> |
|
|
<input type="text" id="userInput" placeholder="Type your message here..." |
|
|
class="retro-input flex-1 p-2" onkeypress="handleKeyPress(event)"> |
|
|
<button onclick="sendMessage()" class="retro-button px-4"> |
|
|
[SEND] |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="retro-border p-4"> |
|
|
<h2 class="text-xl text-green-300 mb-3">β CHAT MANAGEMENT β</h2> |
|
|
|
|
|
<div class="flex space-x-2"> |
|
|
<button onclick="saveChat()" class="retro-button flex-1"> |
|
|
[SAVE CHAT] |
|
|
</button> |
|
|
<button onclick="loadChat()" class="retro-button flex-1"> |
|
|
[LOAD CHAT] |
|
|
</button> |
|
|
<button onclick="clearChat()" class="retro-button flex-1"> |
|
|
[CLEAR CHAT] |
|
|
</button> |
|
|
<button onclick="exportChat()" class="retro-button flex-1"> |
|
|
[EXPORT CHAT] |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="mt-4 text-center text-green-200 text-sm"> |
|
|
<div>βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ</div> |
|
|
<div>β STATUS: <span id="status">IDLE</span> β CHAT SAVED: <span id="savedStatus">NO</span> β</div> |
|
|
<div>βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
const STORAGE_KEYS = { |
|
|
SETTINGS: 'retrochat_settings', |
|
|
CHAT_HISTORY: 'retrochat_history', |
|
|
SAVED_CHATS: 'retrochat_saved_chats' |
|
|
}; |
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
loadSettings(); |
|
|
loadChat(); |
|
|
updateStatus(); |
|
|
}); |
|
|
|
|
|
|
|
|
function saveSettings() { |
|
|
const settings = { |
|
|
apiKey: document.getElementById('apiKey').value, |
|
|
apiEndpoint: document.getElementById('apiEndpoint').value || 'https://api.openai.com/v1/chat/completions' |
|
|
}; |
|
|
|
|
|
localStorage.setItem(STORAGE_KEYS.SETTINGS, JSON.stringify(settings)); |
|
|
addToChat('> SYSTEM: Settings saved successfully.'); |
|
|
updateStatus(); |
|
|
} |
|
|
|
|
|
function loadSettings() { |
|
|
const saved = localStorage.getItem(STORAGE_KEYS.SETTINGS); |
|
|
if (saved) { |
|
|
const settings = JSON.parse(saved); |
|
|
document.getElementById('apiKey').value = settings.apiKey || ''; |
|
|
document.getElementById('apiEndpoint').value = settings.apiEndpoint || ''; |
|
|
addToChat('> SYSTEM: Settings loaded from storage.'); |
|
|
} |
|
|
updateStatus(); |
|
|
} |
|
|
|
|
|
function clearSettings() { |
|
|
localStorage.removeItem(STORAGE_KEYS.SETTINGS); |
|
|
document.getElementById('apiKey').value = ''; |
|
|
document.getElementById('apiEndpoint').value = ''; |
|
|
addToChat('> SYSTEM: Settings cleared.'); |
|
|
updateStatus(); |
|
|
} |
|
|
|
|
|
|
|
|
function saveChat() { |
|
|
const chatHistory = document.getElementById('chatHistory').innerHTML; |
|
|
const savedChats = JSON.parse(localStorage.getItem(STORAGE_KEYS.SAVED_CHATS) || {}); |
|
|
const timestamp = new Date().toISOString(); |
|
|
|
|
|
savedChats[timestamp] = { |
|
|
content: chatHistory, |
|
|
name: `Chat_${timestamp.split('T')[0]}` |
|
|
}; |
|
|
|
|
|
localStorage.setItem(STORAGE_KEYS.SAVED_CHATS, JSON.stringify(savedChats)); |
|
|
localStorage.setItem(STORAGE_KEYS.CHAT_HISTORY, chatHistory); |
|
|
|
|
|
document.getElementById('savedStatus').textContent = 'YES'; |
|
|
addToChat('> SYSTEM: Chat saved to browser storage.'); |
|
|
} |
|
|
|
|
|
function loadChat() { |
|
|
const saved = localStorage.getItem(STORAGE_KEYS.CHAT_HISTORY); |
|
|
if (saved) { |
|
|
document.getElementById('chatHistory').innerHTML = saved; |
|
|
document.getElementById('savedStatus').textContent = 'YES'; |
|
|
addToChat('> SYSTEM: Previous chat loaded.'); |
|
|
} |
|
|
} |
|
|
|
|
|
function clearChat() { |
|
|
document.getElementById('chatHistory').innerHTML = '<div>> SYSTEM: Chat cleared. Ready for new conversation.</div>'; |
|
|
localStorage.removeItem(STORAGE_KEYS.CHAT_HISTORY); |
|
|
document.getElementById('savedStatus').textContent = 'NO'; |
|
|
} |
|
|
|
|
|
function exportChat() { |
|
|
const chatContent = document.getElementById('chatHistory').innerText; |
|
|
const blob = new Blob([chatContent], { type: 'text/plain' }); |
|
|
const url = URL.createObjectURL(blob); |
|
|
const a = document.createElement('a'); |
|
|
a.href = url; |
|
|
a.download = `retrochat_${new Date().toISOString().split('T')[0]}.txt`; |
|
|
a.click(); |
|
|
URL.revokeObjectURL(url); |
|
|
addToChat('> SYSTEM: Chat exported as text file.'); |
|
|
} |
|
|
|
|
|
|
|
|
function addToChat(message) { |
|
|
const chatHistory = document.getElementById('chatHistory'); |
|
|
const newMessage = document.createElement('div'); |
|
|
newMessage.innerHTML = message; |
|
|
chatHistory.appendChild(newMessage); |
|
|
chatHistory.scrollTop = chatHistory.scrollHeight; |
|
|
} |
|
|
|
|
|
function handleKeyPress(event) { |
|
|
if (event.key === 'Enter') { |
|
|
sendMessage(); |
|
|
} |
|
|
} |
|
|
|
|
|
async function sendMessage() { |
|
|
const userInput = document.getElementById('userInput').value.trim(); |
|
|
if (!userInput) return; |
|
|
|
|
|
|
|
|
addToChat(`> USER: ${userInput}`); |
|
|
document.getElementById('userInput').value = ''; |
|
|
|
|
|
|
|
|
const settings = JSON.parse(localStorage.getItem(STORAGE_KEYS.SETTINGS) || {}; |
|
|
|
|
|
if (!settings.apiKey) { |
|
|
addToChat('> SYSTEM: ERROR - No API key configured. Please set your API key in the configuration panel.'); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
document.getElementById('status').textContent = 'PROCESSING...'; |
|
|
|
|
|
try { |
|
|
const response = await fetch(settings.apiEndpoint, { |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Content-Type': 'application/json', |
|
|
'Authorization': `Bearer ${settings.apiKey}` |
|
|
}, |
|
|
body: JSON.stringify({ |
|
|
model: "gpt-3.5-turbo", |
|
|
messages: [ |
|
|
{ |
|
|
role: "user", |
|
|
content: userInput |
|
|
} |
|
|
] |
|
|
}) |
|
|
}); |
|
|
|
|
|
if (!response.ok) { |
|
|
throw new Error(`API error: ${response.status}`); |
|
|
} |
|
|
|
|
|
const data = await response.json(); |
|
|
const aiResponse = data.choices[0].message.content; |
|
|
|
|
|
addToChat(`> AI: ${aiResponse}`); |
|
|
document.getElementById('status').textContent = 'IDLE'; |
|
|
|
|
|
} catch (error) { |
|
|
addToChat(`> SYSTEM: ERROR - ${error.message}`); |
|
|
document.getElementById('status').textContent = 'ERROR'; |
|
|
} |
|
|
} |
|
|
|
|
|
function updateStatus() { |
|
|
const hasSettings = localStorage.getItem(STORAGE_KEYS.SETTINGS); |
|
|
const hasChat = localStorage.getItem(STORAGE_KEYS.CHAT_HISTORY); |
|
|
|
|
|
document.getElementById('status').textContent = hasSettings ? 'CONFIGURED' : 'NEEDS_CONFIG'; |
|
|
document.getElementById('savedStatus').textContent = hasChat ? 'YES' : 'NO'; |
|
|
} |
|
|
|
|
|
|
|
|
setInterval(() => { |
|
|
const status = document.getElementById('status'); |
|
|
if (status.textContent === 'IDLE') { |
|
|
status.classList.toggle('text-green-400'); |
|
|
status.classList.toggle('text-green-600'); |
|
|
} |
|
|
}, 1000); |
|
|
</script> |
|
|
</body> |
|
|
</html> |
|
|
|