Files
alicia-ai-terminology/pages/chat.html
ducoterra 9db28a5fdb
All checks were successful
Build and Push Container / build-and-push (push) Successful in 14s
add chat page
2026-05-05 07:07:29 -04:00

306 lines
9.3 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Chat - AI Cheat Sheet</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<nav>
<div class="nav-inner">
<a href="index.html" class="nav-brand">AI Cheat Sheet</a>
<div class="nav-links">
<a href="/pages/terminology.html">Terminology</a>
<a href="/pages/techniques.html">Techniques</a>
<a href="/pages/use-cases.html">Use Cases</a>
<a href="/pages/model-types.html">Model Types</a>
<a href="/pages/prompts.html">Prompt Guide</a>
<a href="/pages/math.html">Math & Concepts</a>
<a href="/pages/chat.html" class="active">Chat</a>
</div>
<button class="dark-toggle" id="darkToggle" aria-label="Toggle dark mode">🌙</button>
</div>
</nav>
<script>
(function(){
var btn = document.getElementById('darkToggle');
var saved = localStorage.getItem('theme');
if(saved === 'dark' || (!saved && window.matchMedia('(prefers-color-scheme: dark)').matches)){
document.documentElement.setAttribute('data-theme','dark');
btn.textContent = '☀️';
}
btn.addEventListener('click', function(){
var isDark = document.documentElement.getAttribute('data-theme') === 'dark';
if(isDark){
document.documentElement.removeAttribute('data-theme');
btn.textContent = '🌙';
localStorage.setItem('theme','light');
} else {
document.documentElement.setAttribute('data-theme','dark');
btn.textContent = '☀️';
localStorage.setItem('theme','dark');
}
});
})();
</script>
<div class="hero">
<h1>AI Chat</h1>
<p>Chat with an LLM powered by your chosen model.</p>
</div>
<div class="container">
<div class="chat-config">
<div class="config-row">
<label for="apiUrl">API Endpoint</label>
<input type="text" id="apiUrl" value="https://llama-instruct.reeselink.com/v1">
</div>
<div class="config-row">
<label for="apiToken">API Token</label>
<input type="password" id="apiToken" placeholder="Enter your API token">
</div>
<div class="config-row">
<label for="modelName">Model</label>
<input type="text" id="modelName" value="instruct">
</div>
<div class="config-row">
<button id="clearBtn" class="clear-btn">Clear Chat</button>
</div>
</div>
<div class="chat-messages" id="chatMessages">
<div class="message assistant-message">
<div class="message-bubble">
<p>Hello! Enter your API token and model name, then start typing your message below.</p>
</div>
</div>
</div>
<div class="chat-input-area">
<textarea id="userInput" placeholder="Type your message..." rows="1"></textarea>
<button id="sendBtn" class="send-btn">Send</button>
</div>
</div>
<footer>AI Cheat Sheet &mdash; A learning reference for artificial intelligence</footer>
<script>
(function(){
var chatMessages = document.getElementById('chatMessages');
var userInput = document.getElementById('userInput');
var sendBtn = document.getElementById('sendBtn');
var clearBtn = document.getElementById('clearBtn');
var apiUrlInput = document.getElementById('apiUrl');
var apiTokenInput = document.getElementById('apiToken');
var modelNameInput = document.getElementById('modelName');
// Restore saved settings
var savedToken = localStorage.getItem('apiToken');
if(savedToken) apiTokenInput.value = savedToken;
var savedModel = localStorage.getItem('modelName');
if(savedModel) modelNameInput.value = savedModel;
var savedUrl = localStorage.getItem('apiUrl');
if(savedUrl) apiUrlInput.value = savedUrl;
var conversationHistory = [];
var isStreaming = false;
function addMessage(role, content) {
var msgDiv = document.createElement('div');
msgDiv.className = 'message ' + (role === 'user' ? 'user-message' : 'assistant-message');
var avatar = document.createElement('div');
avatar.className = 'message-avatar';
avatar.textContent = role === 'user' ? 'You' : 'AI';
var bubble = document.createElement('div');
bubble.className = 'message-bubble';
var p = document.createElement('p');
p.className = 'message-content';
p.textContent = content;
bubble.appendChild(p);
msgDiv.appendChild(avatar);
msgDiv.appendChild(bubble);
chatMessages.appendChild(msgDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
return p;
}
function addStreamingMessage() {
var msgDiv = document.createElement('div');
msgDiv.className = 'message assistant-message';
msgDiv.id = 'streaming-msg';
var avatar = document.createElement('div');
avatar.className = 'message-avatar';
avatar.textContent = 'AI';
var bubble = document.createElement('div');
bubble.className = 'message-bubble';
var p = document.createElement('p');
p.className = 'message-content streaming';
p.textContent = '';
bubble.appendChild(p);
msgDiv.appendChild(avatar);
msgDiv.appendChild(bubble);
chatMessages.appendChild(msgDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
return p;
}
function setLoading(loading) {
isStreaming = loading;
sendBtn.disabled = loading;
sendBtn.textContent = loading ? '...' : 'Send';
userInput.disabled = loading;
if(!loading) {
userInput.focus();
}
}
function sendMessage() {
if(isStreaming) return;
var text = userInput.value.trim();
if(!text) return;
var model = modelNameInput.value.trim();
if(!model) {
alert('Please enter a model name.');
modelNameInput.focus();
return;
}
addMessage('user', text);
conversationHistory.push({ role: 'user', content: text });
userInput.value = '';
userInput.style.height = 'auto';
setLoading(true);
var apiEndpoint = apiUrlInput.value.trim().replace(/\/+$/, '');
var apiToken = apiTokenInput.value.trim();
var streamContent = '';
var contentEl = addStreamingMessage();
var headers = { 'Content-Type': 'application/json' };
if(apiToken) {
headers['Authorization'] = 'Bearer ' + apiToken;
}
fetch(apiEndpoint + '/chat/completions', {
method: 'POST',
headers: headers,
body: JSON.stringify({
messages: conversationHistory,
model: model,
stream: true
})
})
.then(function(response) {
if(!response.ok) {
throw new Error('API error: ' + response.status + ' ' + response.statusText);
}
var reader = response.body.getReader();
var decoder = new TextDecoder();
var buffer = '';
function read() {
return reader.read().then(function({ done, value }) {
if(done) {
setLoading(false);
contentEl.classList.remove('streaming');
return;
}
buffer += decoder.decode(value, { stream: true });
var lines = buffer.split('\n');
buffer = lines.pop();
for(var i = 0; i < lines.length; i++) {
var line = lines[i].trim();
if(line.startsWith('data: ')) {
var data = line.slice(6);
if(data === '[DONE]') {
setLoading(false);
contentEl.classList.remove('streaming');
return;
}
try {
var json = JSON.parse(data);
var delta = json.choices && json.choices[0] && json.choices[0].delta;
if(delta && delta.content) {
streamContent += delta.content;
contentEl.textContent = streamContent;
chatMessages.scrollTop = chatMessages.scrollHeight;
}
} catch(e) {
// skip malformed JSON
}
}
}
return read();
});
}
return read();
})
.catch(function(err) {
setLoading(false);
contentEl.classList.remove('streaming');
contentEl.textContent = 'Error: ' + err.message;
contentEl.style.color = 'var(--pink-700)';
// Remove failed message from history
conversationHistory.pop();
});
}
sendBtn.addEventListener('click', sendMessage);
userInput.addEventListener('keydown', function(e) {
if(e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
userInput.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = Math.min(this.scrollHeight, 200) + 'px';
});
clearBtn.addEventListener('click', function() {
conversationHistory = [];
chatMessages.innerHTML = '';
var welcomeMsg = document.createElement('div');
welcomeMsg.className = 'message assistant-message';
welcomeMsg.innerHTML = '<div class="message-avatar">AI</div><div class="message-bubble"><p>Hello! Chat cleared. How can I help you?</p></div>';
chatMessages.appendChild(welcomeMsg);
});
// Focus input on page load
userInput.focus();
// Save settings on change
apiTokenInput.addEventListener('change', function() {
localStorage.setItem('apiToken', this.value);
});
modelNameInput.addEventListener('change', function() {
localStorage.setItem('modelName', this.value);
});
apiUrlInput.addEventListener('change', function() {
localStorage.setItem('apiUrl', this.value);
});
})();
</script>
</body>
</html>